diff -u --recursive --new-file v1.3.97/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.97/linux/Documentation/Configure.help	Mon Apr 29 18:05:15 1996
+++ linux/Documentation/Configure.help	Fri May  3 11:11:16 1996
@@ -31,6 +31,24 @@
 # in your own kernel configuration tools. The texts are copyrighted
 # (c) 1995,1996 by Axel Boldt and governed by the GNU Public License.
 
+Prompt for development and/or incomplete code/drivers
+CONFIG_EXPERIMENTAL
+  Some of the various things that Linux supports (such as network 
+  drivers, filesystems, network protocols, etc.) can be in a state 
+  of development where the functionality, stability, or the level of 
+  testing is not yet high enough for general use. This is usually
+  known as the "alpha-test" phase amongst developers. If a feature is
+  currently in alpha-test, then the developers usually discourage 
+  widespread use of this feature by the general public to avoid 
+  "Why doesn't this work?" type mail messages. However, active testing
+  and and detailed bug reports from people familiar with the kernel's
+  internals are usually welcomed by the developers. Unless you intend
+  to help test and develop a feature or driver that falls into this 
+  category, you should probably say N here, which will cause this 
+  configure script to present you with less choices. If you say Y here,
+  you will be offered the choice of using features or drivers that are
+  currently considered to be in the alpha-test phase.
+
 Kernel math emulation
 CONFIG_MATH_EMULATION
   Linux can emulate a math coprocessor (used for floating point
@@ -990,10 +1008,12 @@
   say Y. You will need to use the netatalk package so that your Linux
   box can act as a print and file server for macs as well as access
   appletalk printers. Check out
-  http://www.cs.dartmouth.edu/~flowerpt/projects/linux-netatalk/ on
-  the WWW for details (to browse the WWW, you need to have access to a
+  http://artoo.hitchcock.org/~flowerpt/projects/linux-netatalk/ on the
+  WWW for details (to browse the WWW, you need to have access to a
   machine on the Internet that has one of the programs lynx, netscape
-  or Mosaic). This driver is also available as a module ( = code which
+  or Mosaic). The NET-2-HOWTO, available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO contains valuable information
+  as well. This driver is also available as a module ( = code which
   can be inserted in and removed from the running kernel whenever you
   want). If you want to compile it as a module, say M here and read
   Documentation/modules.txt. I hear that the GNU boycott of Apple is
@@ -1052,7 +1072,7 @@
   useful if some other computer on your local network has a direct
   amateur radio connection.
 
-Bridging (test)
+Bridging (EXPERIMENTAL)
 CONFIG_BRIDGE
   If you enable this, your Linux box will be able to act as an
   ethernet bridge, which means that the different ethernet segments it
@@ -1065,9 +1085,7 @@
   the Multiple-Ethernet-mini-HOWTO, available via ftp (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. The
   Bridging code is still in test. If unsure, say N.
-###
-### How to use?
-###
+  The bridge configuration tools are available via ftp from shadow.cabi.net.
 
 Kernel/User network link driver(ALPHA)
 CONFIG_NETLINK
@@ -1561,6 +1579,12 @@
   RELCOM line fill and keepalive monitoring. Ideal on poor quality
   analogue lines.
 
+Radio network interfaces
+CONFIG_NET_RADIO
+  Radio based interfaces for Linux. Both amateur radio (AX.25) and other
+  systems. In addition shadow.cabi.net carries user mode drivers for the
+  Scarab devices. These need no kernel support.
+
 PPP (point-to-point) support
 CONFIG_PPP
   PPP (Point to Point Protocol) is a newer and better SLIP. It serves
@@ -1641,9 +1665,11 @@
   time (you can find the wiring of these cables in
   drivers/net/README?.plip). The cables can be up to 15m long. This
   works also if one of the machines runs DOS and has some PLIP
-  software installed, e.g. NCSA telnet.  If you want to use this, say
-  Y and read the PLIP mini-HOWTO, available via ftp (user: anonymous)
-  in sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini as well as the
+  software installed, e.g. the Crynwr PLIP packet driver
+  (http://sunsite.cnam.fr/packages/Telnet/PC/msdos/misc/pktdrvr.txt)
+  and NCSA's telnet.  If you want to use this, say Y and read the PLIP
+  mini-HOWTO, available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini as well as the
   NET-2-HOWTO in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the
   PLIP protocol was changed and this PLIP driver won't work together
   with the PLIP support in Linux versions 1.0.x.  This option enlarges
@@ -1737,20 +1763,6 @@
   This is support for the intel ethernet cards on some Sun workstations
   (all those with a network interface 'ie0' under SunOS).
 
-Do you want to be offered ALPHA test drivers
-CONFIG_NET_ALPHA
-  ALPHA means that they might be unstable and buggy; it has nothing to
-  do with the computer architecture of the same name. If you don't
-  have a network card in your computer, say N; otherwise say Y,
-  because in most circumstances buggy support for your hardware is
-  still better than none at all (in particular, it enables you to test
-  and improve the drivers). Note that the answer to this question
-  doesn't directly affect the kernel: saying N will just cause this
-  configure script to present you with less choices. If you plan to
-  use more than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available via ftp (user anonymous)
-  from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
-
 Western Digital/SMC cards
 CONFIG_NET_VENDOR_SMC
   If you have a network (ethernet) card belonging to this class, say Y
@@ -2447,8 +2459,8 @@
   addresses and drive types; this can help to find facts in cases you
   are not sure, but can consume some time during the boot process if
   none of the supported drives gets found.
-  Once your drive got found, you should enter the reported parameters into
-  linux/include/linux/sbpcd.h and set "DISTRIBUTION 0" there.
+  Once your drive got found, you should enter the reported parameters 
+  into linux/include/linux/sbpcd.h and set "DISTRIBUTION 0" there.
   This driver can support up to four CDROM interface cards, and each
   card can support up to four CDROM drives; if you say Y here, you
   will be asked how many controllers you have. If compiled as a
@@ -2462,12 +2474,13 @@
   the parameters for the second, third and fourth interface card into
   linux/include/linux/sbpcd.h before compiling the new kernel.
 
-Aztech/Orchid/Okano/Wearnes/TXC (non IDE) CDROM support
+Aztech/Orchid/Okano/Wearnes/TXC/CyDROM  CDROM support
 CONFIG_AZTCD
-  This is your driver if you have an Aztech CDA268-01A, Orchid CD-3110,
-  Okano or Wearnes CDD110 or a Conrad TXC CDROM drive.
-  This driver - just like all these CDROM drivers - is NOT for CDROM
-  drives with IDE/ATAPI interface, such as Aztech CDA269-031SE.
+  This is your driver if you have an Aztech CDA268-01A, Orchid
+  CD-3110, Okano or Wearnes CDD110, Conrad TXC, or CyCDROM CR520 or
+  CR540 CDROM drive.  This driver - just like all these CDROM drivers
+  - is NOT for CDROM drives with IDE/ATAPI interface, such as Aztech
+  CDA269-031SE.
 
 Sony CDU535 CDROM support
 CONFIG_CDU535
@@ -2510,7 +2523,6 @@
   at boot time, please say Y. Boot time command line options (or
   'append=' options in /etc/lilo.conf) are:
                 isp16=<port>,<irq>,<dma>,<drive_type>
-
   Here 'port','irq' and 'dma' are the base i/o address, irq number and
   dma line assumed to be used by the attached cdrom
   drive. 'drive_type' is the type of cdrom drive or its emulation
@@ -2816,7 +2828,7 @@
   FreeBSD uses its own partition scheme on your PC. It requires only
   one entry in the primary partition table of your disk and manages it
   similarly to DOS extended partitions, putting in its first sector a
-  new partition table in disklabel format. Enabling this option allow
+  new partition table in disklabel format. Enabling this option allows
   you to read these disklabels and further mount FreeBSD partitions on
   your Linux box if you also have configured BSD ufs filesystem
   support. If you don't know what all this is about, say N.
@@ -2824,7 +2836,7 @@
 SMD disklabel (Sun partition tables) support
 CONFIG_SMD_DISKLABEL
   Like most systems, SunOS uses its own partition table format,
-  incompatible with each other. Enabling this option allow you to read
+  incompatible with all others. Enabling this option allows you to read
   these partition tables and further mount SunOS disks on your Linux
   box if you also have configured BSD ufs filesystem support. This is
   mainly used to carry data from a Sparc under SunOS to your Linux box
@@ -2868,9 +2880,9 @@
 CONFIG_AFFS_FS
   The Fast File System (FFS) is the common filesystem used on harddisks
   by Amiga (tm) Systems since AmigaOS Version 1.3 (34.20). It's also
-  possible to mount Diskfiles used by the Un*X Amiga Emulator by Bernd
+  possible to mount diskfiles used by the Un*X Amiga Emulator by Bernd
   Schmidt (http://www-users.informatik.rwth-aachen.de/~crux/uae.html)
-  If you want to do this, you will also need the loopback device
+  If you want to do the latter, you will also need the loop device
   support. Because it's in an early development state, the AFFS is 
   read only. Say Y if you want to be able to read files from an Amiga
   FFS partition of your harddrive. Amiga floppies however cannot be
@@ -3147,7 +3159,13 @@
   anything to do with your VESA-compliant power-saving monitor).
   Further, this option doesn't work for all laptops -- it might not turn
   off your backlight at all, or it might print a lot of errors to the
-  console.
+  console, especially if you are using gpm.
+
+Power off on shutdown 
+CONFIG_APM_POWER_OFF
+  This option will power off the computer after the Linux kernel is halted
+  (e.g., with the halt(8) command).  As with the other APM options, this
+  option may not work reliably with some APM BIOS implementations.
 
 Watchdog Timer Support 
 CONFIG_WATCHDOG
@@ -3391,4 +3409,7 @@
 # LocalWords:  DIGI Xe Xeve digiboard UMISC touchscreens mtu ethernets HBAs MEX
 # LocalWords:  Shifflett netcom js jshiffle WIC DECchip ELCP EtherPower dst RTC
 # LocalWords:  rtc SMP lp Digi Intl RightSwitch DGRS dgrs AFFS Amiga UFS SDL AP
-# LocalWords:  Solaris RISCom riscom syncPPP PCBIT pcbit sparc anu au
+# LocalWords:  Solaris RISCom riscom syncPPP PCBIT pcbit sparc anu au artoo ufs
+# LocalWords:  hitchcock Crynwr cnam pktdrvr NCSA's CyDROM CyCDROM FreeBSD NeXT
+# LocalWords:  NeXTstep disklabel disklabels SMD FFS tm AmigaOS diskfiles Un
+# LocalWords:  Bernd informatik rwth aachen uae affs
diff -u --recursive --new-file v1.3.97/linux/Documentation/locks.txt linux/Documentation/locks.txt
--- v1.3.97/linux/Documentation/locks.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/locks.txt	Tue Apr 30 13:07:51 1996
@@ -0,0 +1,56 @@
+		      File Locking Release Notes
+
+		Andy Walker <andy@lysaker.kvaerner.no>
+
+			   15 April 1996
+
+
+What's New?
+-----------
+
+Flock Emulation Warnings
+------------------------
+Many people will have noticed the ugly messages that the file locking
+code started generating with the release of kernel version 1.3.95. The
+messages look something like this:
+
+    fcntl_setlk() called by process XX with broken flock() emulation
+
+This is a warning for people using older C libraries that those libraries
+are still calling the pre 1.3.x flock() emulation routines, instead of
+the real flock() system call. The old routines are quite badly broken,
+especially with respect to parent-child lock sharing, and can give bad
+results if, for example, sendmail attempts to use them.
+
+Fixed versions of the C libraries have been on public release for many
+months. The latest versions are 5.2.18 or 5.3.12 for ELF, and I believe
+somebody made a 4.7.6 release for people using a.out systems.
+
+In 1.3.96 Linus decided to be lenient on the stragglers and changed the
+warning message so that the kernel will only complain five times and
+then shut up. That should make life more bearable even for people who,
+for some reason, don't want to upgrade.
+
+Sendmail Problems
+-----------------
+Because sendmail was unable to use the old flock() emulation, many sendmail
+installations use fcntl() instead of flock(). This is true of Slackware 3.0
+for example. This gave rise to some other subtle problems if sendmail was
+configured to rebuild the alias file. Sendmail tried to lock the aliases.dir
+file with fcntl() at the same time as the GDBM routines tried to lock this
+file with flock(). With pre 1.3.96 kernels this could result in deadlocks that,
+over time, or under a very heavy mail load, would eventually cause the kernel
+to lock solid with deadlocked processes.
+
+I have chosen the rather cruel solution of returning an error when such a
+deadlock would occur. I can't see any other way to handle this situation
+gracefully. The other options are to maintain two entirely separate lists
+for flock() and fcntl() locks, thus defeating any protection between the
+two, or to free locks placed by one method when the same process later
+tries to lock the same file by the other method. Neither option seems
+satisfactory.
+
+Some programs may break (again, groan). In particular the aforementioned
+sendmail may have problems running in 'newaliases' mode. It will no longer
+deadlock though. Recompile sendmail to use flock() and your troubles will
+be over.
diff -u --recursive --new-file v1.3.97/linux/Documentation/networking/ncsa-telnet linux/Documentation/networking/ncsa-telnet
--- v1.3.97/linux/Documentation/networking/ncsa-telnet	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/networking/ncsa-telnet	Tue Apr 30 12:42:36 1996
@@ -0,0 +1,16 @@
+NCSA telnet doesn't work with path MTU discovery enabled. This is due to a
+bug in NCSA that also stops it working with other modern networking code
+such as Solaris.
+
+The following information is courtesy of 
+Marek <marekm@i17linuxb.ists.pwr.wroc.pl>
+
+There is a fixed version somewhere on ftp.upe.ac.za (sorry, I don't
+remember the exact pathname, and this site is very slow from here).
+It may or may not be faster for you to get it from
+ftp://ftp.ists.pwr.wroc.pl/pub/msdos/telnet/ncsa_upe/tel23074.zip
+(source is in v230704s.zip).  I have tested it with 1.3.79 (with
+path mtu discovery enabled - ncsa 2.3.08 didn't work) and it seems
+to work.  I don't know if anyone is working on this code - this
+version is over a year old.  Too bad - it's faster and often more
+stable than these windoze telnets, and runs on almost anything...
diff -u --recursive --new-file v1.3.97/linux/Documentation/oops-tracing.txt linux/Documentation/oops-tracing.txt
--- v1.3.97/linux/Documentation/oops-tracing.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/oops-tracing.txt	Tue Apr 30 12:42:36 1996
@@ -0,0 +1,72 @@
+From: Linus Torvalds <torvalds@cs.helsinki.fi>
+
+How to track down an Oops.. [originally a mail to linux-kernel]
+
+The main trick is having 5 years of experience with those pesky oops 
+messages ;-)
+
+Actually, there are things you can do that make this easier. I have two 
+separate approached:
+
+	gdb /usr/src/linux/vmlinux
+	gdb> disassemble <offending_function>
+
+That's the easy way to find the problem, at least if the bug-report is 
+well made (like this one was - run through ksymoops to get the 
+information of which function and the offset in the function that it 
+happened in).
+
+Oh, it helps if the report happens on a kernel that is compiled with the 
+same compiler and similar setups.
+
+The other thing to do is disassemble the "Code:" part of the bugreprot: 
+ksymoops will do this too with the correct tools (and new version of 
+ksymoops), but if you don't have the tools you can just do a silly 
+program:
+
+	char str[] = "\xXX\xXX\xXX...";
+	main(){}
+
+and compile it with gcc -g and then do "disassemble str" (where the "XX" 
+stuff are the values reported by the Oops - you can just cut-and-paste 
+and do a replace of spaces to "\x" - that's what I do, as I'm too lazy 
+to write a prigram to automate this all).
+
+Finally, if you want to see where the code comes from, you can do
+
+	cd /usr/src/linux
+	make fs/buffer.s 	# or whatever file the bug happened in
+
+and then you get a better idea of what happens than with the gdb 
+disassembly.
+
+Now, the trick is just then to combine all the data you have: the C 
+sources (and general knowledge of what it _should_ do, the assembly 
+listing and the code disassembly (and additionally the register dump you 
+also get from the "oops" message - that can be useful to see _what_ the 
+corrupted pointers were, and when you have the assembler listing you can 
+also match the other registers to whatever C expressions they were used 
+for).
+
+Essentially, you just look at what doesn't match (in this case it was the 
+"Code" disassembly that didn't match with what the compiler generated). 
+Then you need to find out _why_ they don't match. Often it's simple - you 
+see that the code uses a NULL pointer and then you look at the code and 
+wonder how the NULL pointer got there, and if it's a valid thing to do 
+you just check against it..
+
+Now, if somebody gets the idea that this is time-consuming and requires 
+some small amount of concentration, you're right. Which is why I will 
+mostly just ignore any panic reports that don't have the symbol table 
+info etc looked up: it simply gets too hard to look it up (I have some 
+programs to search for specific patterns in the kernel code segment, and 
+sometimes I have been able to look up those kinds of panics too, but 
+that really requires pretty good knowledge of the kernel just to be able 
+to pick out the right sequences etc..)
+
+_Sometimes_ it happens that I just see the disassembled code sequence 
+from the panic, and I know immediately where it's coming from. That's when 
+I get worried that I've been doing this for too long ;-)
+
+		Linus
+
diff -u --recursive --new-file v1.3.97/linux/Documentation/ramdisk.txt linux/Documentation/ramdisk.txt
--- v1.3.97/linux/Documentation/ramdisk.txt	Sat Apr 27 15:19:44 1996
+++ linux/Documentation/ramdisk.txt	Sat May  4 19:39:23 1996
@@ -36,7 +36,7 @@
 
 The old "ramdisk=<ram_size>" has been changed to "ramdisk_size=<ram_size>"
 to make it clearer.  The original "ramdisk=<ram_size>" has been kept around
-for compatiblity reasons, but it will probably be removed in 2.1.x.
+for compatibility reasons, but it will probably be removed in 2.1.x.
 
 The new ramdisk also has the ability to load compressed ramdisk images,
 allowing one to squeeze more programs onto an average installation or 
diff -u --recursive --new-file v1.3.97/linux/Documentation/rtc.txt linux/Documentation/rtc.txt
--- v1.3.97/linux/Documentation/rtc.txt	Mon Apr 15 12:20:16 1996
+++ linux/Documentation/rtc.txt	Tue Apr 30 12:48:50 1996
@@ -45,7 +45,15 @@
 only allowed by root. This is perhaps a bit conservative, but we don't want
 an evil user generating lots of IRQs on a slow 386sx-16, where it might have
 a negative impact on performance.  Note that the interrupt handler is only
-four lines of code to minimize any possibility of this effect.
+a few lines of code to minimize any possibility of this effect.
+
+Also, if the kernel time is synchronized with an external source, the 
+kernel will write the time back to the CMOS clock every 11 minutes. In 
+the process of doing this, the kernel briefly turns off RTC periodic 
+interrupts, so be aware of this if you are doing serious work. If you
+don't synchronize the kernel time with an external source (via ntp or
+whatever) then the kernel will keep its hands off the RTC, allowing you
+exclusive access to the device for your applications.
 
 The alarm and/or interrupt frequency are programmed into the RTC via
 various ioctl(2) calls as listed in ./include/linux/mc146818rtc.h
diff -u --recursive --new-file v1.3.97/linux/MAINTAINERS linux/MAINTAINERS
--- v1.3.97/linux/MAINTAINERS	Mon Apr 29 18:05:15 1996
+++ linux/MAINTAINERS	Wed May  1 11:45:38 1996
@@ -65,8 +65,8 @@
 	Orphan:		No current maintainer [but maybe you could take the 
 			role as you write your new code].
 	Obsolete:	Old code. Something tagged obsolete generally means
-			its been replaced by a better system and you should
-			be using that.
+			it has been replaced by a better system and you
+			should be using that.
 
 3C501 NETWORK DRIVER
 P:	Alan Cox
diff -u --recursive --new-file v1.3.97/linux/Makefile linux/Makefile
--- v1.3.97/linux/Makefile	Mon Apr 29 18:05:15 1996
+++ linux/Makefile	Mon Apr 29 18:10:40 1996
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 97
+SUBLEVEL = 98
 
 ARCH = i386
 
diff -u --recursive --new-file v1.3.97/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v1.3.97/linux/arch/alpha/config.in	Sun Apr 21 12:39:00 1996
+++ linux/arch/alpha/config.in	Tue Apr 30 12:55:53 1996
@@ -10,6 +10,11 @@
 unset CONFIG_ALPHA_NEED_ROUNDING_EMULATION
 
 mainmenu_option next_comment
+comment 'Code maturity level options'
+bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
+endmenu
+
+mainmenu_option next_comment
 comment 'Loadable module support'
 bool 'Enable loadable module support' CONFIG_MODULES
 if [ "$CONFIG_MODULES" = "y" ]; then
diff -u --recursive --new-file v1.3.97/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
--- v1.3.97/linux/arch/alpha/defconfig	Wed Apr 24 17:00:33 1996
+++ linux/arch/alpha/defconfig	Fri May  3 15:51:32 1996
@@ -3,6 +3,11 @@
 #
 
 #
+# Code maturity level options
+#
+# CONFIG_EXPERIMENTAL is not set
+
+#
 # Loadable module support
 #
 CONFIG_MODULES=y
@@ -75,7 +80,6 @@
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
 # CONFIG_AX25 is not set
-# CONFIG_BRIDGE is not set
 # CONFIG_NETLINK is not set
 
 #
@@ -133,25 +137,21 @@
 #
 CONFIG_NETDEVICES=y
 CONFIG_DUMMY=m
-# CONFIG_SLIP is not set
-# CONFIG_PPP is not set
-# CONFIG_STRIP is not set
-# CONFIG_WIC is not set
-# CONFIG_SCC is not set
-# CONFIG_PLIP is not set
 # CONFIG_EQUALIZER is not set
-# CONFIG_DLCI is not set
-# CONFIG_NET_ALPHA is not set
-# CONFIG_NET_VENDOR_SMC is not set
-# CONFIG_LANCE is not set
+# CONFIG_PLIP is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+CONFIG_NET_ETHERNET=y
 # CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_NET_ISA is not set
 CONFIG_NET_EISA=y
 # CONFIG_APRICOT is not set
 CONFIG_DE4X5=y
 # CONFIG_DEC_ELCP is not set
 # CONFIG_DGRS is not set
-# CONFIG_ZNET is not set
 # CONFIG_NET_POCKET is not set
 # CONFIG_TR is not set
 # CONFIG_ARCNET is not set
diff -u --recursive --new-file v1.3.97/linux/arch/alpha/kernel/ptrace.c linux/arch/alpha/kernel/ptrace.c
--- v1.3.97/linux/arch/alpha/kernel/ptrace.c	Wed Apr  3 16:06:55 1996
+++ linux/arch/alpha/kernel/ptrace.c	Fri May  3 15:55:57 1996
@@ -510,8 +510,10 @@
 			return -EPERM;
 		if ((!child->dumpable ||
 		     (current->uid != child->euid) ||
+		     (current->uid != child->suid) ||
 		     (current->uid != child->uid) ||
 		     (current->gid != child->egid) ||
+		     (current->gid != child->sgid) ||
 		     (current->gid != child->gid)) && !suser())
 			return -EPERM;
 		/* the same process cannot be attached many times */
diff -u --recursive --new-file v1.3.97/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.3.97/linux/arch/i386/config.in	Mon Feb 26 11:58:03 1996
+++ linux/arch/i386/config.in	Tue Apr 30 12:55:42 1996
@@ -5,6 +5,11 @@
 mainmenu_name "Linux Kernel Configuration"
 
 mainmenu_option next_comment
+comment 'Code maturity level options'
+bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
+endmenu
+
+mainmenu_option next_comment
 comment 'Loadable module support'
 bool 'Enable loadable module support' CONFIG_MODULES
 if [ "$CONFIG_MODULES" = "y" ]; then
diff -u --recursive --new-file v1.3.97/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v1.3.97/linux/arch/i386/defconfig	Wed Apr 24 17:00:34 1996
+++ linux/arch/i386/defconfig	Tue Apr 30 13:50:47 1996
@@ -3,6 +3,11 @@
 #
 
 #
+# Code maturity level options
+#
+# CONFIG_EXPERIMENTAL is not set
+
+#
 # Loadable module support
 #
 CONFIG_MODULES=y
@@ -74,7 +79,6 @@
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
 # CONFIG_AX25 is not set
-# CONFIG_BRIDGE is not set
 # CONFIG_NETLINK is not set
 
 #
@@ -87,22 +91,19 @@
 #
 CONFIG_NETDEVICES=y
 CONFIG_DUMMY=m
-# CONFIG_SLIP is not set
-# CONFIG_PPP is not set
-# CONFIG_STRIP is not set
-# CONFIG_WIC is not set
-# CONFIG_SCC is not set
-# CONFIG_PLIP is not set
 # CONFIG_EQUALIZER is not set
-# CONFIG_DLCI is not set
-# CONFIG_NET_ALPHA is not set
-# CONFIG_NET_VENDOR_SMC is not set
-# CONFIG_LANCE is not set
+# CONFIG_PLIP is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_RADIO is not set
+CONFIG_NET_ETHERNET=y
 CONFIG_NET_VENDOR_3COM=y
 # CONFIG_EL1 is not set
 # CONFIG_EL2 is not set
 CONFIG_EL3=y
 # CONFIG_VORTEX is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_NET_ISA is not set
 # CONFIG_NET_EISA is not set
 # CONFIG_NET_POCKET is not set
diff -u --recursive --new-file v1.3.97/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v1.3.97/linux/arch/i386/kernel/ptrace.c	Wed Apr  3 16:06:55 1996
+++ linux/arch/i386/kernel/ptrace.c	Thu May  2 16:11:56 1996
@@ -320,8 +320,10 @@
 			return -EPERM;
 		if ((!child->dumpable ||
 		    (current->uid != child->euid) ||
+		    (current->uid != child->suid) ||
 		    (current->uid != child->uid) ||
 	 	    (current->gid != child->egid) ||
+	 	    (current->gid != child->sgid) ||
 	 	    (current->gid != child->gid)) && !suser())
 			return -EPERM;
 		/* the same process cannot be attached many times */
diff -u --recursive --new-file v1.3.97/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- v1.3.97/linux/arch/i386/kernel/time.c	Sun Mar 17 08:54:33 1996
+++ linux/arch/i386/kernel/time.c	Fri May  3 12:00:45 1996
@@ -29,6 +29,7 @@
 
 extern int setup_x86_irq(int, struct irqaction *);
 
+#ifndef	CONFIG_APM	/* cycle counter may be unreliable */
 /* Cycle counter value at the previous timer interrupt.. */
 static unsigned long long last_timer_cc = 0;
 static unsigned long long init_timer_cc = 0;
@@ -82,6 +83,7 @@
 		quotient = 1000000/HZ-1;
 	return quotient;
 }
+#endif
 
 /* This function must be called with interrupts disabled 
  * It was inspired by Steve McCanne's microtime-i386 for BSD.  -- jrs
@@ -176,8 +178,8 @@
 
 	xtime = *tv;
 	time_state = TIME_BAD;
-	time_maxerror = 0x70000000;
-	time_esterror = 0x70000000;
+	time_maxerror = MAXPHASE;
+	time_esterror = MAXPHASE;
 	sti();
 }
 
@@ -271,6 +273,7 @@
 	    
 }
 
+#ifndef	CONFIG_APM	/* cycle counter may be unreliable */
 /*
  * This is the same as the above, except we _also_ save the current
  * cycle counter value at the time of the timer interrupt, so that
@@ -284,6 +287,7 @@
 		 "=d" (((unsigned long *) &last_timer_cc)[1]));
 	timer_interrupt(irq, NULL, regs);
 }
+#endif
 
 /* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
  * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
diff -u --recursive --new-file v1.3.97/linux/arch/sparc/kernel/ptrace.c linux/arch/sparc/kernel/ptrace.c
--- v1.3.97/linux/arch/sparc/kernel/ptrace.c	Sat Apr 27 15:19:48 1996
+++ linux/arch/sparc/kernel/ptrace.c	Sat May  4 19:39:23 1996
@@ -340,7 +340,7 @@
 		break;
 
 	case 948:
-		/* Isn't binary compatability _fun_??? */
+		/* Isn't binary compatibility _fun_??? */
 		if(cregs->psr & PSR_C)
 			regs->u_regs[UREG_I0] = cregs->u_regs[UREG_I0] << 24;
 		else
diff -u --recursive --new-file v1.3.97/linux/drivers/block/README.fd linux/drivers/block/README.fd
--- v1.3.97/linux/drivers/block/README.fd	Mon Apr 29 18:05:15 1996
+++ linux/drivers/block/README.fd	Sat May  4 19:39:22 1996
@@ -99,7 +99,7 @@
 floppy=nofifo
 	Disables the FIFO entirely. This is needed if you get "Bus
 	master arbitration error" messages from your ethernet card (or
-	from other devices) while accessing the foppy.
+	from other devices) while accessing the floppy.
 
 floppy=fifo
 	Enables the FIFO (default)
@@ -119,9 +119,9 @@
 	higher value, until you only get an occasional Over/Underrun.
 	It is a good idea to compile the floppy driver as a module
 	when doing this tuning. Indeed, it allows to try different
-	fifo values whithout rebooting the machine for each test. Note
+	fifo values without rebooting the machine for each test. Note
 	that you need to do 'floppycontrol --messages' every time you
-	re-inseert the module.
+	re-insert the module.
 	Usually, tuning the fifo threshold should not be needed, as
 	the default (0xa) is reasonable.
 
diff -u --recursive --new-file v1.3.97/linux/drivers/block/README.ide linux/drivers/block/README.ide
--- v1.3.97/linux/drivers/block/README.ide	Fri Apr 12 15:51:49 1996
+++ linux/drivers/block/README.ide	Fri May  3 11:07:24 1996
@@ -210,6 +210,7 @@
  "idex=" is recognized for all "x" from "0" to "3", such as "ide1".
 
  "hdx=noprobe"		: drive may be present, but do not probe for it
+ "hdx=none"		: drive is NOT present, ignore cmos and do not probe
  "hdx=nowerr"		: ignore the WRERR_STAT bit on this drive
  "hdx=cdrom"		: drive is present, and is a cdrom drive
  "hdx=cyl,head,sect"	: disk drive is present, with specified geometry
diff -u --recursive --new-file v1.3.97/linux/drivers/block/cmd640.c linux/drivers/block/cmd640.c
--- v1.3.97/linux/drivers/block/cmd640.c	Fri Apr 12 15:51:49 1996
+++ linux/drivers/block/cmd640.c	Fri May  3 11:07:23 1996
@@ -110,7 +110,7 @@
 
 /* Interface to access cmd640x registers */
 static void (*put_cmd640_reg)(int reg_no, int val);
-static byte (*get_cmd640_reg)(int reg_no);
+       byte (*get_cmd640_reg)(int reg_no);
 
 enum { none, vlb, pci1, pci2 };
 static int	bus_type = none;
diff -u --recursive --new-file v1.3.97/linux/drivers/block/dtc2278.c linux/drivers/block/dtc2278.c
--- v1.3.97/linux/drivers/block/dtc2278.c	Sat Mar 16 13:52:13 1996
+++ linux/drivers/block/dtc2278.c	Fri May  3 11:07:23 1996
@@ -42,6 +42,7 @@
  * DTC2278S has only a single IDE interface.
  * DTC2278D has two IDE interfaces and is otherwise identical to the S version.
  * DTC2278E has onboard BIOS, while the others do not.
+ * DTC2278EB: "works like a charm" -- Kent Bradford <kent@theory.caltech.edu>
  *
  * There may be a fourth controller type. The S and D versions use the
  * Winbond chip, and I think the E version does also.
diff -u --recursive --new-file v1.3.97/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v1.3.97/linux/drivers/block/floppy.c	Mon Apr 29 18:05:16 1996
+++ linux/drivers/block/floppy.c	Tue Apr 30 11:41:25 1996
@@ -1734,9 +1734,6 @@
 #ifdef DEBUGT
 	debugt("reset interrupt:");
 #endif
-#ifdef __sparc__
-	fdc_specify();  /* P3: It gives us "sector not found" without this. */
-#endif
 	result();		/* get the status ready for set_fdc */
 	if (FDCS->reset) {
 		printk("reset set in interrupt, calling %p\n", cont->error);
@@ -4004,6 +4001,10 @@
 		CLEARSTRUCT(FDCS);
 		FDCS->dtr = -1;
 		FDCS->dor = 0x4;
+#ifdef __sparc__
+		/*sparcs don't have a DOR reset which we can fall back on to*/
+		FDCS->version = FDC_82072A;
+#endif
 	}
 
 	fdc_state[0].address = FDC1;
@@ -4036,6 +4037,7 @@
 		FDCS->rawcmd = 2;
 		if (user_reset_fdc(-1,FD_RESET_ALWAYS,0)){
 			FDCS->address = -1;
+			FDCS->version = FDC_NONE;
 			continue;
 		}
 		/* Try to determine the floppy controller type */
@@ -4266,7 +4268,7 @@
 {
 	int dummy;
 	floppy_grab_irq_and_dma();
-	lock_fdc(0,0);
+	lock_fdc(MAXTIMEOUT,0);
 	dummy=fd_eject(0);
 	process_fd_request();
 	floppy_release_irq_and_dma();
diff -u --recursive --new-file v1.3.97/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v1.3.97/linux/drivers/block/ide-cd.c	Fri Apr 12 15:51:49 1996
+++ linux/drivers/block/ide-cd.c	Wed May  1 13:29:14 1996
@@ -92,6 +92,8 @@
  *                       Reformat to match kernel tabbing style.
  *                       Add CDROM_GET_UPC ioctl.
  * 3.10  Apr 10, 1996 -- Fix compilation error with STANDARD_ATAPI.
+ * 3.11  Apr 29, 1996 -- Patch from Heiko Eissfeldt <heiko@colossus.escape.de>
+ *                       to remove redundant verify_area calls.
  *
  * 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.
@@ -2079,9 +2081,6 @@
 		struct cdrom_tocentry tocentry;
 		struct atapi_toc_entry *toce;
 
-		stat = verify_area (VERIFY_READ, (void *) arg,
-				    sizeof (tocentry));
-		if (stat) return stat;
 		stat = verify_area (VERIFY_WRITE, (void *) arg,
 				    sizeof (tocentry));
 		if (stat) return stat;
@@ -2117,9 +2116,6 @@
 		stat = verify_area (VERIFY_WRITE, (void *) arg,
 				    sizeof (subchnl));
 		if (stat) return stat;
-		stat = verify_area (VERIFY_READ, (void *) arg,
-				    sizeof (subchnl));
-		if (stat) return stat;
 
 		memcpy_fromfs (&subchnl, (void *) arg, sizeof (subchnl));
 
@@ -2228,9 +2224,6 @@
 		struct atapi_toc *toc;
 		int stat;
 
-		stat = verify_area (VERIFY_READ,  (void *)arg,
-				    sizeof (ms_info));
-		if (stat) return stat;
 		stat = verify_area (VERIFY_WRITE, (void *)arg,
 				    sizeof (ms_info));
 		if (stat) return stat;
@@ -2327,8 +2320,6 @@
 			format = 3;
 		}
 
-		stat = verify_area (VERIFY_READ, (char *)arg, sizeof (msf));
-		if (stat) return stat;
 		stat = verify_area (VERIFY_WRITE, (char *)arg, blocksize);
 		if (stat) return stat;
 
diff -u --recursive --new-file v1.3.97/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v1.3.97/linux/drivers/block/ide-tape.c	Wed Apr 24 17:00:36 1996
+++ linux/drivers/block/ide-tape.c	Fri May  3 11:07:24 1996
@@ -1,7 +1,7 @@
 /*
  * linux/drivers/block/ide-tape.c	Version 1.5 - ALPHA	Apr  12, 1996
  *
- * Copyright (C) 1995, 1996 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
+ * Copyright (C) 1995, 1996 Gadi Oxman <gadio@netvision.net.il>
  *
  * This driver was constructed as a student project in the software laboratory
  * of the faculty of electrical engineering in the Technion - Israel's
@@ -1525,6 +1525,7 @@
 	}		
 #endif /* CONFIG_BLK_DEV_TRITON */
 
+	OUT_BYTE (drive->ctl,IDETAPE_CONTROL_REG);
 	OUT_BYTE (dma_ok ? 1:0,IDETAPE_FEATURES_REG);			/* Use PIO/DMA */
 	OUT_BYTE (bcount.b.high,IDETAPE_BCOUNTH_REG);
 	OUT_BYTE (bcount.b.low,IDETAPE_BCOUNTL_REG);
diff -u --recursive --new-file v1.3.97/linux/drivers/block/ide-tape.h linux/drivers/block/ide-tape.h
--- v1.3.97/linux/drivers/block/ide-tape.h	Fri Apr 12 15:51:50 1996
+++ linux/drivers/block/ide-tape.h	Fri May  3 11:07:24 1996
@@ -1,7 +1,7 @@
 /*
- * linux/drivers/block/ide-tape.h	Version 1.3 - ALPHA	Feb   9, 1996
+ * linux/drivers/block/ide-tape.h	Version 1.5 - ALPHA	Apr  12, 1996
  *
- * Copyright (C) 1995, 1996 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
+ * Copyright (C) 1995, 1996 Gadi Oxman <gadio@netvision.net.il>
  */
 
 /*
diff -u --recursive --new-file v1.3.97/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v1.3.97/linux/drivers/block/ide.c	Wed Apr 24 17:00:36 1996
+++ linux/drivers/block/ide.c	Fri May  3 15:56:48 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 5.37  Apr 6, 1996
+ *  linux/drivers/block/ide.c	Version 5.39  May 3, 1996
  *
  *  Copyright (C) 1994-1996  Linus Torvalds & authors (see below)
  */
@@ -227,6 +227,10 @@
  * Version 5.37		don't use DMA when "noautotune" is specified
  * Version 5.37a (go)	fix shared irq probing (was broken in kernel 1.3.72)
  *			call unplug_device() from ide_do_drive_cmd()
+ * Version 5.38		add "hdx=none" option, courtesy of Joel Maslak
+ *			mask drive irq after use, if sharing with another hwif
+ *			add code to help debug weird cmd640 problems
+ * Version 5.39		fix horrible error in earlier irq sharing "fix"
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -749,6 +753,7 @@
 	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 */
+	udelay(5);			/* more than enough time */
 	hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
 	ide_set_handler (drive, &reset_pollfunc, HZ/20);
 #endif	/* OK_TO_RESET_CONTROLLER */
@@ -1476,6 +1481,8 @@
 		ide_hwif_t *hwif = hwgroup->hwif;
 		struct request *rq;
 		if ((rq = hwgroup->rq) == NULL) {
+			if (hwif->sharing_irq && hwgroup->drive) /* set nIEN */
+				OUT_BYTE(hwgroup->drive->ctl|2,hwif->ctl_port);
 			/*
 			 * hwgroup->next_hwif is different from hwgroup->hwif
 			 * only when a request is inserted using "ide_next".
@@ -2405,8 +2412,17 @@
 		irqs = probe_irq_off(irqs);	/* get irq number */
 		if (irqs > 0)
 			HWIF(drive)->irq = irqs;
-		else				/* Mmmm.. multiple IRQs */
+		else {				/* Mmmm.. multiple IRQs */
 			printk("%s: IRQ probe failed (%d)\n", drive->name, irqs);
+#ifdef CONFIG_BLK_DEV_CMD640
+			if (HWIF(drive)->chipset == ide_cmd640) {
+				extern byte (*get_cmd640_reg)(int);
+				printk("%s: Hmmm.. probably a driver problem.\n", drive->name);
+				printk("%s: cmd640 reg 09h == 0x%02x\n", drive->name, get_cmd640_reg(9));
+				printk("%s: cmd640 reg 51h == 0x%02x\n", drive->name, get_cmd640_reg(0x51));
+			}
+#endif /* CONFIG_BLK_DEV_CMD640 */
+		}
 	}
 	return rc;
 }
@@ -2542,7 +2558,7 @@
 	/* Extract drive geometry from CMOS+BIOS if not already setup */
 	for (unit = 0; unit < MAX_DRIVES; ++unit) {
 		ide_drive_t *drive = &hwif->drives[unit];
-		if ((cmos_disks & (0xf0 >> (unit*4))) && !drive->present) {
+		if ((cmos_disks & (0xf0 >> (unit*4))) && !drive->present && !drive->nobios) {
 			drive->cyl   = drive->bios_cyl  = *(unsigned short *)BIOS;
 			drive->head  = drive->bios_head = *(BIOS+2);
 			drive->sect  = drive->bios_sect = *(BIOS+14);
@@ -2683,6 +2699,7 @@
  * "idex=" is recognized for all "x" from "0" to "3", such as "ide1".
  *
  * "hdx=noprobe"	: drive may be present, but do not probe for it
+ * "hdx=none"		: drive is NOT present, ignore cmos and do not probe
  * "hdx=nowerr"		: ignore the WRERR_STAT bit on this drive
  * "hdx=cdrom"		: drive is present, and is a cdrom drive
  * "hdx=cyl,head,sect"	: disk drive is present, with specified geometry
@@ -2737,33 +2754,35 @@
 	 * Look for drive options:  "hdx="
 	 */
 	if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
-		const char *hd_words[] = {"noprobe", "nowerr", "cdrom", "serialize",
-						"autotune", "noautotune", NULL};
+		const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom",
+				"serialize", "autotune", "noautotune", NULL};
 		unit = s[2] - 'a';
 		hw   = unit / MAX_DRIVES;
 		unit = unit % MAX_DRIVES;
 		hwif = &ide_hwifs[hw];
 		drive = &hwif->drives[unit];
 		switch (match_parm(&s[3], hd_words, vals, 3)) {
-			case -1: /* "noprobe" */
+			case -1: /* "none" */
+				drive->nobios = 1;  /* drop into "noprobe" */
+			case -2: /* "noprobe" */
 				drive->noprobe = 1;
 				goto done;
-			case -2: /* "nowerr" */
+			case -3: /* "nowerr" */
 				drive->bad_wstat = BAD_R_STAT;
 				hwif->noprobe = 0;
 				goto done;
-			case -3: /* "cdrom" */
+			case -4: /* "cdrom" */
 				drive->present = 1;
 				drive->media = ide_cdrom;
 				hwif->noprobe = 0;
 				goto done;
-			case -4: /* "serialize" */
+			case -5: /* "serialize" */
 				printk(" -- USE \"ide%d=serialize\" INSTEAD", hw);
 				goto do_serialize;
-			case -5: /* "autotune" */
+			case -6: /* "autotune" */
 				drive->autotune = 1;
 				goto done;
-			case -6: /* "noautotune" */
+			case -7: /* "noautotune" */
 				drive->autotune = 2;
 				goto done;
 			case 3: /* cyl,head,sect */
@@ -2856,7 +2875,7 @@
 			}
 #endif /* CONFIG_BLK_DEV_HT6560B */
 #if CONFIG_BLK_DEV_QD6580
-			case -5: /* "qd6580" (no secondary i/f) */
+			case -5: /* "qd6580" (has secondary i/f) */
 			{
 				extern void init_qd6580 (void);
 				init_qd6580();
@@ -3011,12 +3030,13 @@
 	 */
 	for (index = 0; index < MAX_HWIFS; index++) {
 		if (index != hwif->index) {
-			ide_hwif_t *g = &ide_hwifs[index];
-			if (g->irq == hwif->irq || g->irq == mate_irq) {
-				if (hwgroup && !g->hwgroup)
-					g->hwgroup = hwgroup;
+			ide_hwif_t *h = &ide_hwifs[index];
+			if (h->irq == hwif->irq || h->irq == mate_irq) {
+				hwif->sharing_irq = h->sharing_irq = 1;
+				if (hwgroup && !h->hwgroup)
+					h->hwgroup = hwgroup;
 				else if (!hwgroup)
-					hwgroup = g->hwgroup;
+					hwgroup = h->hwgroup;
 			}
 		}
 	}
diff -u --recursive --new-file v1.3.97/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v1.3.97/linux/drivers/block/ide.h	Fri Apr 12 15:51:50 1996
+++ linux/drivers/block/ide.h	Fri May  3 16:19:49 1996
@@ -313,6 +313,7 @@
 	unsigned using_dma	: 1;	/* disk is using dma for read/write */
 	unsigned forced_geom	: 1;	/* 1 if hdx=c,h,s was given at boot */
 	unsigned unmask		: 1;	/* flag: okay to unmask other irqs */
+	unsigned nobios		: 1;	/* flag: do not probe bios for drive */
 	unsigned autotune	: 2;	/* 1=autotune, 2=noautotune, 0=default */
 #if FAKE_FDISK_FOR_EZDRIVE
 	unsigned remap_0_to_1	: 1;	/* flag: partitioned with ezdrive */
@@ -419,6 +420,7 @@
 	unsigned	serialized : 1;	/* serialized operation with mate hwif */
 	unsigned	no_unmask  : 1;	/* disallow setting unmask bits */
 	unsigned	got_irq    : 1;	/* 1 = already alloc'd our irq */
+	unsigned	sharing_irq: 1;	/* 1 = sharing irq with another hwif */
 #ifdef CONFIG_BLK_DEV_PROMISE
 	unsigned	is_promise2: 1;	/* 2nd i/f on promise DC4030 */
 #endif /* CONFIG_BLK_DEV_PROMISE */
diff -u --recursive --new-file v1.3.97/linux/drivers/block/qd6580.c linux/drivers/block/qd6580.c
--- v1.3.97/linux/drivers/block/qd6580.c	Mon Feb 12 07:04:03 1996
+++ linux/drivers/block/qd6580.c	Fri May  3 11:07:24 1996
@@ -38,6 +38,9 @@
  * and can work out the answers!
  *
  * I/O ports are 0xb0 0xb2 and 0xb3
+ *
+ * More research on qd6580 being done by willmore@cig.mot.com (David)
+ *	-- this is apparently a *dual* IDE interface
  */
 
 static void tune_qd6580 (ide_drive_t *drive, byte pio)
@@ -61,5 +64,6 @@
 void init_qd6580 (void)
 {
 	ide_hwifs[0].chipset = ide_qd6580;
+	ide_hwifs[1].chipset = ide_qd6580;
 	ide_hwifs[0].tuneproc = &tune_qd6580;
 }
diff -u --recursive --new-file v1.3.97/linux/drivers/cdrom/aztcd.c linux/drivers/cdrom/aztcd.c
--- v1.3.97/linux/drivers/cdrom/aztcd.c	Mon Apr 29 18:05:17 1996
+++ linux/drivers/cdrom/aztcd.c	Sat May  4 19:39:23 1996
@@ -1,5 +1,5 @@
-#define AZT_VERSION "2.30"
-/*      $Id: aztcd.c,v 2.30 1996/04/26 05:32:15 root Exp root $
+#define AZT_VERSION "2.40"
+/*      $Id: aztcd.c,v 2.40 1996/05/01 11:09:35 root Exp root $
 	linux/drivers/block/aztcd.c - Aztech CD268 CDROM driver
 
 	Copyright (C) 1994,95,96 Werner Zimmermann(zimmerma@rz.fht-esslingen.de)
@@ -148,7 +148,9 @@
         V2.30   Implemented support for CyCDROM CR520, CR940, Code for CR520 
         	delivered by H.Berger with preworks by E.Moenkeberg.
                 Werner Zimmermann, April 29, 96
-
+        V2.40   Reorganized the placement of functions in the source code file
+                to reflect the layered approach; did not actually change code
+                Werner Zimmermann, Mai 1, 96
 */
 #include <linux/module.h>
 #include <linux/errno.h>
@@ -171,6 +173,10 @@
 #include <linux/blk.h>
 #include <linux/aztcd.h>
 
+/*###########################################################################
+  Defines
+  ###########################################################################
+*/
 #define SET_TIMER(func, jifs)   delay_timer.expires = jiffies + (jifs); \
                                 delay_timer.function = (void *) (func); \
                                 add_timer(&delay_timer); 
@@ -189,7 +195,6 @@
 	                  outb_p(0x10,azt_port+6); 
 #define SWITCH_IDE_MASTER outb_p(0xa0,azt_port+6);
 
-static int aztPresent = 0;
 
 #if 0
 #define AZT_TEST
@@ -209,17 +214,14 @@
 #define AFL_STATUSorDATA (AFL_STATUS | AFL_DATA)
 #define AZT_BUF_SIZ 16
 
-static volatile int azt_transfer_is_active=0;
+#define READ_TIMEOUT 3000
 
-static char azt_buf[CD_FRAMESIZE_RAW*AZT_BUF_SIZ];/*buffer for block size conversion*/
-#if AZT_PRIVATE_IOCTLS
-static char buf[CD_FRAMESIZE_RAW];              /*separate buffer for the ioctls*/
-#endif
+#define azt_port aztcd  /*needed for the modutils*/
 
-static volatile int azt_buf_bn[AZT_BUF_SIZ], azt_next_bn;
-static volatile int azt_buf_in, azt_buf_out = -1;
-static volatile int azt_error=0;
-static int azt_open_count=0;
+/*##########################################################################
+  Type Definitions
+  ##########################################################################
+*/
 enum azt_state_e 
 { AZT_S_IDLE,    /* 0 */
   AZT_S_START,   /* 1 */
@@ -229,24 +231,39 @@
   AZT_S_STOP,    /* 5 */
   AZT_S_STOPPING /* 6 */
 };
-static volatile enum azt_state_e azt_state = AZT_S_IDLE;
-#ifdef AZT_TEST3
-static volatile enum azt_state_e azt_state_old = AZT_S_STOP;  
-static volatile int azt_st_old = 0;
-#endif
 enum azt_read_modes 
 { AZT_MODE_0,     /*read mode for audio disks, not supported by Aztech firmware*/
   AZT_MODE_1,     /*read mode for normal CD-ROMs*/
   AZT_MODE_2      /*read mode for XA CD-ROMs*/
 };
+
+/*##########################################################################
+  Global Variables
+  ##########################################################################
+*/
+static int aztPresent = 0;
+
+static volatile int azt_transfer_is_active=0;
+
+static char azt_buf[CD_FRAMESIZE_RAW*AZT_BUF_SIZ];/*buffer for block size conversion*/
+#if AZT_PRIVATE_IOCTLS
+static char buf[CD_FRAMESIZE_RAW];              /*separate buffer for the ioctls*/
+#endif
+
+static volatile int azt_buf_bn[AZT_BUF_SIZ], azt_next_bn;
+static volatile int azt_buf_in, azt_buf_out = -1;
+static volatile int azt_error=0;
+static int azt_open_count=0;
+static volatile enum azt_state_e azt_state = AZT_S_IDLE;
+#ifdef AZT_TEST3
+static volatile enum azt_state_e azt_state_old = AZT_S_STOP;  
+static volatile int azt_st_old = 0;
+#endif
 static volatile enum azt_read_modes azt_read_mode = AZT_MODE_1;
 
 static int azt_mode = -1;
 static volatile int azt_read_count = 1;
 
-#define READ_TIMEOUT 3000
-
-#define azt_port aztcd  /*needed for the modutils*/
 static int azt_port = AZT_BASE_ADDR;
 
 static char  azt_cont = 0;
@@ -265,35 +282,85 @@
 static char aztDiskChanged = 1;
 static char aztTocUpToDate = 0;
 
-static void azt_transfer(void);
-static void azt_poll(void);
-static void azt_invalidate_buffers(void);
-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);
+static unsigned char aztIndatum;
+static unsigned long aztTimeOutCount;
+static int aztCmd = 0;
+
+/*###########################################################################
+   Function Prototypes
+  ###########################################################################
+*/
+/* CDROM Drive Low Level I/O Functions */
+void        op_ok(void);
+void        pa_ok(void);
+void        sten_low(void);
+void        dten_low(void);
+void        statusAzt(void);
+static void aztStatTimer(void);
+
+/* CDROM Drive Command Functions */
 static int  aztSendCmd(int cmd);
 static int  sendAztCmd(int cmd, struct azt_Play_msf *params);
+static int  aztSeek(struct azt_Play_msf *params);
+static int  aztSetDiskType(int type);
+static int  aztStatus(void);
+static int  getAztStatus(void);
+static int  aztPlay(struct azt_Play_msf *arg);
+static void aztCloseDoor(void);
+static void aztLockDoor(void);
+static void aztUnlockDoor(void);
+static int  aztGetValue(unsigned char *result);
 static int  aztGetQChannelInfo(struct azt_Toc *qp);
 static int  aztUpdateToc(void);
 static int  aztGetDiskInfo(void);
 #if AZT_MULTISESSION 
-  static int  aztGetMultiDiskInfo(void);
+ static int aztGetMultiDiskInfo(void);
 #endif
 static int  aztGetToc(int multi);
-static int  aztGetValue(unsigned char *result);
-static void aztStatTimer(void);
-static void aztCloseDoor(void);
-static void aztLockDoor(void);
-static void aztUnlockDoor(void);
 
-static unsigned char aztIndatum;
-static unsigned long aztTimeOutCount;
-static int aztCmd = 0;
+/* Kernel Interface Functions */
+void        aztcd_setup(char *str, int *ints);
+static int  check_aztcd_media_change(kdev_t full_dev);
+static int  aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
+static void azt_transfer(void);
+static void do_aztcd_request(void);
+static void azt_invalidate_buffers(void);
+int         aztcd_open(struct inode *ip, struct file *fp);
+static void aztcd_release(struct inode * inode, struct file * file);
+int         aztcd_init(void);
+#ifdef MODULE
+ int        init_module(void);
+ void       cleanup_module(void);
+#endif MODULE
+static struct file_operations azt_fops = {
+	NULL,                   /* lseek - default */
+	block_read,             /* read - general block-dev read */
+	block_write,            /* write - general block-dev write */
+	NULL,                   /* readdir - bad */
+	NULL,                   /* select */
+	aztcd_ioctl,            /* ioctl */
+	NULL,                   /* mmap */
+	aztcd_open,             /* open */
+	aztcd_release,          /* release */
+	NULL,                   /* fsync */
+	NULL,                   /* fasync*/
+	check_aztcd_media_change, /*media change*/
+	NULL                    /* revalidate*/
+};
+
+/* Aztcd State Machine: Controls Drive Operating State */
+static void azt_poll(void);
 
+/* Miscellaneous support functions */
+static void azt_hsg2msf(long hsg, struct msf *msf);
+static long azt_msf2hsg(struct msf *mp);
+static void azt_bin2bcd(unsigned char *p);
+static int  azt_bcd2bin(unsigned char bcd);
+
+/*##########################################################################
+  CDROM Drive Low Level I/O Functions
+  ##########################################################################
+*/
 /* Macros for the drive hardware interface handshake, these macros use
    busy waiting */
 /* Wait for OP_OK = drive answers with AFL_OP_OK after receiving a command*/
@@ -375,43 +442,10 @@
   SET_TIMER(aztStatTimer, HZ/100);
 }
 
-void aztcd_setup(char *str, int *ints)
-{  if (ints[0] > 0)
-      azt_port = ints[1];
-   if (ints[0] > 1)
-      azt_cont = ints[2];
-}
-
-/*
- * Subroutines to automatically close the door (tray) and 
- * lock it closed when the cd is mounted.  Leave the tray
- * locking as an option
- */
-static void aztCloseDoor(void)
-{
-  aztSendCmd(ACMD_CLOSE);
-  STEN_LOW;
-  return;
-}
-
-static void aztLockDoor(void)
-{
-#if AZT_ALLOW_TRAY_LOCK
-  aztSendCmd(ACMD_LOCK);
-  STEN_LOW;
-#endif
-  return;
-}
-
-static void aztUnlockDoor(void)
-{
-#if AZT_ALLOW_TRAY_LOCK
-  aztSendCmd(ACMD_UNLOCK);
-  STEN_LOW;
-#endif
-  return;
-}
-
+/*##########################################################################
+  CDROM Drive Command Functions
+  ##########################################################################
+*/
 /* 
  * Send a single command, return -1 on error, else 0
 */
@@ -557,14 +591,6 @@
 }
 
 
-/* 
- * Checking if the media has been changed not yet implemented
-*/
-static int check_aztcd_media_change(kdev_t full_dev)
-{ return 0;
-}
-
-
 /* used in azt_poll to poll the status, expects another program to issue a 
  * ACMD_GET_STATUS directly before 
  */
@@ -621,838 +647,847 @@
 	return 0;
 }
 
+/*
+ * Subroutines to automatically close the door (tray) and 
+ * lock it closed when the cd is mounted.  Leave the tray
+ * locking as an option
+ */
+static void aztCloseDoor(void)
+{
+  aztSendCmd(ACMD_CLOSE);
+  STEN_LOW;
+  return;
+}
 
-
-static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
-{       int i, st;
-	struct azt_Toc qInfo;
-	struct cdrom_ti ti;
-	struct cdrom_tochdr tocHdr;
-	struct cdrom_msf msf;
-	struct cdrom_tocentry entry;
-	struct azt_Toc *tocPtr;            
-	struct cdrom_subchnl subchnl;
-        struct cdrom_volctrl volctrl;
-
-#ifdef AZT_DEBUG
-	printk("aztcd: starting aztcd_ioctl - Command:%x   Time: %li\n",cmd, jiffies);
-        printk("aztcd Status %x\n", getAztStatus());
+static void aztLockDoor(void)
+{
+#if AZT_ALLOW_TRAY_LOCK
+  aztSendCmd(ACMD_LOCK);
+  STEN_LOW;
 #endif
-	if (!ip) RETURNM("aztcd_ioctl 1",-EINVAL);
-	if (getAztStatus()<0) RETURNM("aztcd_ioctl 2", -EIO);
-	if ((!aztTocUpToDate)||(aztDiskChanged))
-	{ if ((i=aztUpdateToc())<0) RETURNM("aztcd_ioctl 3", i); /* error reading TOC */
-	}
+  return;
+}
 
-	switch (cmd)
-	{
-	case CDROMSTART:     /* Spin up the drive. Don't know, what to do,
-	                        at least close the tray */
-#if AZT_PRIVATE_IOCTLS 
-	        if (aztSendCmd(ACMD_CLOSE)) RETURNM("aztcd_ioctl 4",-1);
-	        STEN_LOW_WAIT;
+static void aztUnlockDoor(void)
+{
+#if AZT_ALLOW_TRAY_LOCK
+  aztSendCmd(ACMD_UNLOCK);
+  STEN_LOW;
 #endif
-		break;
-	case CDROMSTOP:      /* Spin down the drive */
-		if (aztSendCmd(ACMD_STOP)) RETURNM("aztcd_ioctl 5",-1);
-		STEN_LOW_WAIT;
-		/* should we do anything if it fails? */
-		aztAudioStatus = CDROM_AUDIO_NO_STATUS;
-		break;
-	case CDROMPAUSE:     /* Pause the drive */
-                if (aztAudioStatus != CDROM_AUDIO_PLAY) return -EINVAL; 
+  return;
+}
 
-		if (aztGetQChannelInfo(&qInfo) < 0)
-		{ /* didn't get q channel info */
-		  aztAudioStatus = CDROM_AUDIO_NO_STATUS;
-		  RETURNM("aztcd_ioctl 7",0);
-		}
-		azt_Play.start = qInfo.diskTime;        /* remember restart point */
+/*
+ * Read a value from the drive.  Should return quickly, so a busy wait
+ * is used to avoid excessive rescheduling. The read command itself must
+ * be issued with aztSendCmd() directly before
+ */
+static int aztGetValue(unsigned char *result)
+{       int s;
+
+	STEN_LOW;
+	if (aztTimeOutCount>=AZT_TIMEOUT)
+	{       printk("aztcd: aztGetValue timeout\n");
+		return -1;
+	}
+	s = inb(DATA_PORT) & 0xFF;
+	*result = (unsigned char) s;
+	return 0;
+}
+
+/*
+ * Read the current Q-channel info.  Also used for reading the
+ * table of contents.
+ */
+int aztGetQChannelInfo(struct azt_Toc *qp)
+{       unsigned char notUsed;
+	int st;
 
-		if (aztSendCmd(ACMD_PAUSE)) RETURNM("aztcd_ioctl 8",-1);
-		STEN_LOW_WAIT;
-		aztAudioStatus = CDROM_AUDIO_PAUSED;
-		break;
-	case CDROMRESUME:    /* Play it again, Sam */
-		if (aztAudioStatus != CDROM_AUDIO_PAUSED) return -EINVAL;
-		/* restart the drive at the saved position. */
-		i = aztPlay(&azt_Play);
-		if (i < 0)
-		{ aztAudioStatus = CDROM_AUDIO_ERROR;
-		  return -EIO;
-		}
-		aztAudioStatus = CDROM_AUDIO_PLAY;
-		break;
-	case CDROMMULTISESSION: /*multisession support -- experimental*/
-	        { struct cdrom_multisession ms;
 #ifdef AZT_DEBUG
-   		  printk("aztcd ioctl MULTISESSION\n");
-#endif
-		  st = verify_area(VERIFY_READ, (void*) arg, sizeof(struct cdrom_multisession));
-		  if (st) return st;
-		  memcpy_fromfs(&ms, (void*) arg, sizeof(struct cdrom_multisession));
-		  if (ms.addr_format == CDROM_MSF) 
-		     { ms.addr.msf.minute = azt_bcd2bin(DiskInfo.lastSession.min);
-		       ms.addr.msf.second = azt_bcd2bin(DiskInfo.lastSession.sec);
-		       ms.addr.msf.frame  = azt_bcd2bin(DiskInfo.lastSession.frame);
-		     } 
-		  else if (ms.addr_format == CDROM_LBA)
-		       ms.addr.lba = azt_msf2hsg(&DiskInfo.lastSession);
-		  else
-		       return -EINVAL;
-		  ms.xa_flag = DiskInfo.xa;
- 		  st = verify_area(VERIFY_WRITE, (void*) arg, sizeof(struct cdrom_multisession));
-		  if (st) return st;
-	  	  memcpy_tofs((void*) arg, &ms, sizeof(struct cdrom_multisession));
-#ifdef AZT_DEBUG 
- 		  if (ms.addr_format == CDROM_MSF) 
-                      printk("aztcd multisession xa:%d, msf:%02x:%02x.%02x [%02x:%02x.%02x])\n",
-			      ms.xa_flag, ms.addr.msf.minute, ms.addr.msf.second, 
-			      ms.addr.msf.frame, DiskInfo.lastSession.min,
-			      DiskInfo.lastSession.sec, DiskInfo.lastSession.frame);
-		  else
-		      printk("aztcd multisession %d, lba:0x%08x [%02x:%02x.%02x])\n",
-			      ms.xa_flag, ms.addr.lba, DiskInfo.lastSession.min,
-			      DiskInfo.lastSession.sec, DiskInfo.lastSession.frame);
-#endif
-   		  return 0;
-		}
-	case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
-		st = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
-		if (st) return st;
-		memcpy_fromfs(&ti, (void *) arg, sizeof ti);
-		if (ti.cdti_trk0 < DiskInfo.first
-			|| ti.cdti_trk0 > DiskInfo.last
-			|| ti.cdti_trk1 < ti.cdti_trk0)
-		{ return -EINVAL;
-		}
-		if (ti.cdti_trk1 > DiskInfo.last)
-		    ti.cdti_trk1 = DiskInfo.last;
-		azt_Play.start = Toc[ti.cdti_trk0].diskTime;
-		azt_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
-#ifdef AZT_DEBUG
-printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n",
-	azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame,
-	azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame);
+	printk("aztcd: starting aztGetQChannelInfo  Time:%li\n",jiffies);
 #endif
-		i = aztPlay(&azt_Play);
-		if (i < 0)
-		{ aztAudioStatus = CDROM_AUDIO_ERROR;
-		  return -EIO;
-		}
-		aztAudioStatus = CDROM_AUDIO_PLAY;
-		break;
-	case CDROMPLAYMSF:   /* Play starting at the given MSF address. */
-/*              if (aztAudioStatus == CDROM_AUDIO_PLAY) 
-		{ if (aztSendCmd(ACMD_STOP)) RETURNM("aztcd_ioctl 9",-1);
-		  STEN_LOW;
-		  aztAudioStatus = CDROM_AUDIO_NO_STATUS;
-		}
-*/
-		st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
-		if (st) return st;
-		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
-		/* convert to bcd */
-		azt_bin2bcd(&msf.cdmsf_min0);
-		azt_bin2bcd(&msf.cdmsf_sec0);
-		azt_bin2bcd(&msf.cdmsf_frame0);
-		azt_bin2bcd(&msf.cdmsf_min1);
-		azt_bin2bcd(&msf.cdmsf_sec1);
-		azt_bin2bcd(&msf.cdmsf_frame1);
-		azt_Play.start.min = msf.cdmsf_min0;
-		azt_Play.start.sec = msf.cdmsf_sec0;
-		azt_Play.start.frame = msf.cdmsf_frame0;
-		azt_Play.end.min = msf.cdmsf_min1;
-		azt_Play.end.sec = msf.cdmsf_sec1;
-		azt_Play.end.frame = msf.cdmsf_frame1;
+	if ((st=getAztStatus())==-1)        RETURNM("aztGetQChannelInfo 1",-1);
+	if (aztSendCmd(ACMD_GET_Q_CHANNEL)) RETURNM("aztGetQChannelInfo 2",-1);
+	/*STEN_LOW_WAIT; ??? Dosemu0.60's cdrom.c does not like STEN_LOW_WAIT here*/
+	if (aztGetValue(&notUsed)) RETURNM("aztGetQChannelInfo 3",-1); /*??? Nullbyte einlesen*/
+	if ((st&AST_MODE_BITS)==AST_INITIAL)
+	 { qp->ctrl_addr=0;      /* when audio stop ACMD_GET_Q_CHANNEL returns */
+	   qp->track=0;          /* only one byte with Aztech drives */
+	   qp->pointIndex=0;
+	   qp->trackTime.min=0;
+	   qp->trackTime.sec=0;
+	   qp->trackTime.frame=0;
+	   qp->diskTime.min=0;
+	   qp->diskTime.sec=0;
+	   qp->diskTime.frame=0;
+	   return 0;  
+	 }
+	else
+	 { if (aztGetValue(&qp -> ctrl_addr) < 0)       RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> track) < 0)           RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> pointIndex) < 0)      RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> trackTime.min) < 0)   RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> trackTime.sec) < 0)   RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> trackTime.frame) < 0) RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&notUsed) < 0)               RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> diskTime.min) < 0)    RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> diskTime.sec) < 0)    RETURNM("aztGetQChannelInfo 4",-1);
+	   if (aztGetValue(&qp -> diskTime.frame) < 0)  RETURNM("aztGetQChannelInfo 4",-1);
+	 }
 #ifdef AZT_DEBUG
-printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n",
-azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame,
-azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame);
+	printk("aztcd: exiting aztGetQChannelInfo  Time:%li\n",jiffies);
 #endif
-		i = aztPlay(&azt_Play);
-		if (i < 0)
-		{ aztAudioStatus = CDROM_AUDIO_ERROR;
-		  return -EIO;
-		}
-		aztAudioStatus = CDROM_AUDIO_PLAY;
-		break;
+	return 0;
+}
+
+/*
+ * Read the table of contents (TOC) and TOC header if necessary
+ */
+static int aztUpdateToc()
+{       int st;
 
-	case CDROMREADTOCHDR:        /* Read the table of contents header */
-		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr);
-		if (st) return st;
-		tocHdr.cdth_trk0 = DiskInfo.first;
-		tocHdr.cdth_trk1 = DiskInfo.last;
-		memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
-		break;
-	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
-		st = verify_area(VERIFY_READ, (void *) arg, sizeof entry);
-		if (st) return st;
-		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
-		if (st) return st;
-		memcpy_fromfs(&entry, (void *) arg, sizeof entry);
-		if ((!aztTocUpToDate)||aztDiskChanged) aztUpdateToc();
-		if (entry.cdte_track == CDROM_LEADOUT)
-		  tocPtr = &Toc[DiskInfo.last + 1];
-		else if (entry.cdte_track > DiskInfo.last
-				|| entry.cdte_track < DiskInfo.first)
-		{ return -EINVAL;
-		}
-		else 
-		  tocPtr = &Toc[entry.cdte_track];
-		entry.cdte_adr = tocPtr -> ctrl_addr;
-		entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
-		if (entry.cdte_format == CDROM_LBA)
-		  entry.cdte_addr.lba = azt_msf2hsg(&tocPtr -> diskTime);
-		else if (entry.cdte_format == CDROM_MSF)
-		{ entry.cdte_addr.msf.minute = azt_bcd2bin(tocPtr -> diskTime.min);
-		  entry.cdte_addr.msf.second = azt_bcd2bin(tocPtr -> diskTime.sec);
-		  entry.cdte_addr.msf.frame  = azt_bcd2bin(tocPtr -> diskTime.frame);
-		}
-		else
-		{ return -EINVAL;
-		}
-		memcpy_tofs((void *) arg, &entry, sizeof entry);
-		break;
-	case CDROMSUBCHNL:   /* Get subchannel info */
-		st = verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_subchnl));
-		if (st) { 
 #ifdef AZT_DEBUG
-		          printk("aztcd: exiting aztcd_ioctl - Error 1 - Command:%x\n",cmd);
+	printk("aztcd: starting aztUpdateToc  Time:%li\n",jiffies);
+#endif  
+	if (aztTocUpToDate)
+		return 0;
+
+	if (aztGetDiskInfo() < 0)
+		return -EIO;
+
+	if (aztGetToc(0) < 0)
+		return -EIO;
+
+        /*audio disk detection
+          with my Aztech drive there is no audio status bit, so I use the copy
+          protection bit of the first track. If this track is copy protected 
+          (copy bit = 0), I assume, it's an audio  disk. Strange, but works ??? */
+        if (!(Toc[DiskInfo.first].ctrl_addr & 0x40)) 
+           DiskInfo.audio=1;
+        else 
+           DiskInfo.audio=0;
+
+        /* XA detection */
+	if (! DiskInfo.audio) 
+	   { azt_Play.start.min   = 0;  /*XA detection only seems to work*/
+             azt_Play.start.sec   = 2;  /*when we play a track*/
+	     azt_Play.start.frame = 0;
+	     azt_Play.end.min     = 0;
+	     azt_Play.end.sec     = 0;
+	     azt_Play.end.frame   = 1;
+	     if (sendAztCmd(ACMD_PLAY_READ, &azt_Play)) return -1;
+	     DTEN_LOW;
+	     for (st=0;st<CD_FRAMESIZE;st++) inb(DATA_PORT);
+           } 
+        DiskInfo.xa = getAztStatus() & AST_MODE;
+        if (DiskInfo.xa) 
+           { printk("aztcd: XA support experimental - mail results to zimmerma@rz.fht-esslingen.de\n");
+           }
+        
+        /*multisession detection
+          support for multisession CDs is done automatically with Aztech drives,
+          we don't have to take care about TOC redirection; if we want the isofs
+          to take care about redirection, we have to set AZT_MULTISESSION to 1*/
+        DiskInfo.multi=0;
+#if AZT_MULTISESSION
+  	if (DiskInfo.xa) 
+  	   { aztGetMultiDiskInfo(); /*here Disk.Info.multi is set*/
+  	   }
 #endif
-		          return st;
-		        }  
-		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl));
-		if (st) { 
+        if (DiskInfo.multi)
+           { DiskInfo.lastSession.min  = Toc[DiskInfo.next].diskTime.min;
+	     DiskInfo.lastSession.sec  = Toc[DiskInfo.next].diskTime.sec;
+             DiskInfo.lastSession.frame= Toc[DiskInfo.next].diskTime.frame;
+             printk("aztcd: Multisession support experimental\n");
+           }
+        else
+           { DiskInfo.lastSession.min  = Toc[DiskInfo.first].diskTime.min;
+	     DiskInfo.lastSession.sec  = Toc[DiskInfo.first].diskTime.sec;
+             DiskInfo.lastSession.frame= Toc[DiskInfo.first].diskTime.frame;
+           }
+
+	aztTocUpToDate = 1;
 #ifdef AZT_DEBUG
-		          printk("aztcd: exiting aztcd_ioctl - Error 2 - Command:%x\n",cmd);
+	printk("aztcd: exiting aztUpdateToc  Time:%li\n",jiffies);
 #endif
-		          return st;
-		        }  
-		memcpy_fromfs(&subchnl, (void *) arg, sizeof (struct cdrom_subchnl));
-		if (aztGetQChannelInfo(&qInfo) < 0)
-		if (st) { 
+	return 0;
+}
+
+
+/* Read the table of contents header, i.e. no. of tracks and start of first 
+ * track
+ */
+static int aztGetDiskInfo()
+{ int limit;
+  unsigned char test;
+  struct azt_Toc qInfo;
+
 #ifdef AZT_DEBUG
-		          printk("aztcd: exiting aztcd_ioctl - Error 3 - Command:%x\n",cmd);
+  printk("aztcd: starting aztGetDiskInfo  Time:%li\n",jiffies);
 #endif
-     		  	  return -EIO;
-     		  	}  
-		subchnl.cdsc_audiostatus = aztAudioStatus;
-		subchnl.cdsc_adr = qInfo.ctrl_addr;
-		subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4;
-		subchnl.cdsc_trk = azt_bcd2bin(qInfo.track);
-		subchnl.cdsc_ind = azt_bcd2bin(qInfo.pointIndex);
-		if (subchnl.cdsc_format == CDROM_LBA)
-		{ subchnl.cdsc_absaddr.lba = azt_msf2hsg(&qInfo.diskTime);
-		  subchnl.cdsc_reladdr.lba = azt_msf2hsg(&qInfo.trackTime);
-		}
-		else  /*default*/
-		{ subchnl.cdsc_format = CDROM_MSF;
-		  subchnl.cdsc_absaddr.msf.minute = azt_bcd2bin(qInfo.diskTime.min);
-		  subchnl.cdsc_absaddr.msf.second = azt_bcd2bin(qInfo.diskTime.sec);
-		  subchnl.cdsc_absaddr.msf.frame  = azt_bcd2bin(qInfo.diskTime.frame);
-		  subchnl.cdsc_reladdr.msf.minute = azt_bcd2bin(qInfo.trackTime.min);
-		  subchnl.cdsc_reladdr.msf.second = azt_bcd2bin(qInfo.trackTime.sec);
-		  subchnl.cdsc_reladdr.msf.frame  = azt_bcd2bin(qInfo.trackTime.frame);
-		}
-		memcpy_tofs((void *) arg, &subchnl, sizeof (struct cdrom_subchnl));
-		break;
-	case CDROMVOLCTRL:   /* Volume control 
-	 * With my Aztech CD268-01A volume control does not work, I can only
-	   turn the channels on (any value !=0) or off (value==0). Maybe it
-           works better with your drive */
-                st=verify_area(VERIFY_READ,(void *) arg, sizeof(volctrl));
-                if (st) return (st);
-                memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl));
-		azt_Play.start.min = 0x21;
-		azt_Play.start.sec = 0x84;
-		azt_Play.start.frame = volctrl.channel0;
-		azt_Play.end.min =     volctrl.channel1;
-		azt_Play.end.sec =     volctrl.channel2;
-		azt_Play.end.frame =   volctrl.channel3;
-                sendAztCmd(ACMD_SET_VOLUME, &azt_Play);
-                STEN_LOW_WAIT;
-                break;
-	case CDROMEJECT:
-                aztUnlockDoor(); /* Assume user knows what they're doing */
-	       /* all drives can at least stop! */
-		if (aztAudioStatus == CDROM_AUDIO_PLAY) 
-		{ if (aztSendCmd(ACMD_STOP)) RETURNM("azt_ioctl 10",-1);
-		  STEN_LOW_WAIT;
-		}
-		if (aztSendCmd(ACMD_EJECT)) RETURNM("azt_ioctl 11",-1);
-		STEN_LOW_WAIT;
-		aztAudioStatus = CDROM_AUDIO_NO_STATUS;
-		break;
-	case CDROMEJECT_SW:
-		azt_auto_eject = (char) arg;
-		break;	
-        case CDROMRESET:
-        	outb(ACMD_SOFT_RESET,CMD_PORT);   /*send reset*/
-	        STEN_LOW;
-	        if (inb(DATA_PORT)!=AFL_OP_OK)    /*OP_OK?*/
-	          { printk("aztcd: AZTECH CD-ROM drive does not respond\n");
-                  }
-                break;
-/*Take care, the following code is not compatible with other CD-ROM drivers,
-  use it at your own risk with cdplay.c. Set AZT_PRIVATE_IOCTLS to 0 in aztcd.h,
-  if you do not want to use it!
-*/                  
-#if AZT_PRIVATE_IOCTLS 
-	case CDROMREADCOOKED: /*read data in mode 1 (2048 Bytes)*/
-	case CDROMREADRAW:    /*read data in mode 2 (2336 Bytes)*/
-		{ st = verify_area(VERIFY_READ,  (void *) arg, sizeof msf);
-		  if (st) return st;
-		  st = verify_area(VERIFY_WRITE, (void *) arg, sizeof buf);
-		  if (st) return st;
-		  memcpy_fromfs(&msf, (void *) arg, sizeof msf);
-		  /* convert to bcd */
-		  azt_bin2bcd(&msf.cdmsf_min0);
-		  azt_bin2bcd(&msf.cdmsf_sec0);
-		  azt_bin2bcd(&msf.cdmsf_frame0);
-		  msf.cdmsf_min1=0;
-		  msf.cdmsf_sec1=0;
-		  msf.cdmsf_frame1=1; /*read only one frame*/
-		  azt_Play.start.min = msf.cdmsf_min0;
-		  azt_Play.start.sec = msf.cdmsf_sec0;
-		  azt_Play.start.frame = msf.cdmsf_frame0;
-		  azt_Play.end.min = msf.cdmsf_min1;
-		  azt_Play.end.sec = msf.cdmsf_sec1;
-		  azt_Play.end.frame = msf.cdmsf_frame1;
-		  if (cmd==CDROMREADRAW)
-		  { if (DiskInfo.xa)
-		       { return -1;         /*XA Disks can't be read raw*/
-		       }
-		    else   
-		       { if (sendAztCmd(ACMD_PLAY_READ_RAW, &azt_Play)) return -1;
-		         DTEN_LOW;
-		         insb(DATA_PORT,buf,CD_FRAMESIZE_RAW);
-		         memcpy_tofs((void *) arg, &buf, CD_FRAMESIZE_RAW);
-		       }  
-		  }
-		  else /*CDROMREADCOOKED*/
-		  { if (sendAztCmd(ACMD_PLAY_READ, &azt_Play)) return -1;
-		    DTEN_LOW;
-		    insb(DATA_PORT,buf,CD_FRAMESIZE);
-		    memcpy_tofs((void *) arg, &buf, CD_FRAMESIZE);
-		  }
-		 } 
-                break;
-	case CDROMSEEK:    /*seek msf address*/
-		st = verify_area(VERIFY_READ,  (void *) arg, sizeof msf);
-		if (st) return st;
-		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
-		/* convert to bcd */
-		azt_bin2bcd(&msf.cdmsf_min0);
-		azt_bin2bcd(&msf.cdmsf_sec0);
-		azt_bin2bcd(&msf.cdmsf_frame0);
-		azt_Play.start.min = msf.cdmsf_min0;
-		azt_Play.start.sec = msf.cdmsf_sec0;
-		azt_Play.start.frame = msf.cdmsf_frame0;
-		if (aztSeek(&azt_Play)) return -1;
-                break;
-#endif /*end of incompatible code*/       
-	case CDROMREADMODE1: /*set read data in mode 1*/
-                return aztSetDiskType(AZT_MODE_1);
-	case CDROMREADMODE2: /*set read data in mode 2*/
-                return aztSetDiskType(AZT_MODE_2);	    
-	default:
-		return -EINVAL;
+  if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) RETURNM("aztGetDiskInfo 1",-1);
+  STEN_LOW_WAIT;
+  test=0;
+  for (limit=300;limit>0;limit--)
+   {  if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetDiskInfo 2",-1);
+      if (qInfo.pointIndex==0xA0)   /*Number of FirstTrack*/
+	{ DiskInfo.first = qInfo.diskTime.min;
+	  DiskInfo.first = azt_bcd2bin(DiskInfo.first);
+	  test=test|0x01;
+	}
+      if (qInfo.pointIndex==0xA1)   /*Number of LastTrack*/
+	{ DiskInfo.last  = qInfo.diskTime.min;
+	  DiskInfo.last  = azt_bcd2bin(DiskInfo.last);
+	  test=test|0x02;
+	}
+      if (qInfo.pointIndex==0xA2)   /*DiskLength*/
+	{ DiskInfo.diskLength.min=qInfo.diskTime.min;
+	  DiskInfo.diskLength.sec=qInfo.diskTime.sec;
+	  DiskInfo.diskLength.frame=qInfo.diskTime.frame;
+	  test=test|0x04;
+	}
+      if ((qInfo.pointIndex==DiskInfo.first)&&(test&0x01))   /*StartTime of First Track*/
+	{ DiskInfo.firstTrack.min=qInfo.diskTime.min;
+	  DiskInfo.firstTrack.sec=qInfo.diskTime.sec;
+	  DiskInfo.firstTrack.frame=qInfo.diskTime.frame;
+	  test=test|0x08;
 	}
+      if (test==0x0F) break;
+   }
 #ifdef AZT_DEBUG
-        printk("aztcd: exiting aztcd_ioctl Command:%x  Time:%li\n",cmd,jiffies);
+  printk ("aztcd: exiting aztGetDiskInfo  Time:%li\n",jiffies);
+  printk("Disk Info: first %d last %d length %02X:%02X.%02X dez  first %02X:%02X.%02X dez\n",
+  	  DiskInfo.first,
+	  DiskInfo.last,
+	  DiskInfo.diskLength.min,
+	  DiskInfo.diskLength.sec,
+	  DiskInfo.diskLength.frame,
+	  DiskInfo.firstTrack.min,
+	  DiskInfo.firstTrack.sec,
+	  DiskInfo.firstTrack.frame);
 #endif
-	return 0;
+  if (test!=0x0F) return -1;
+  return 0;
 }
 
-
+#if AZT_MULTISESSION
 /*
- * Take care of the different block sizes between cdrom and Linux.
- * When Linux gets variable block sizes this will probably go away.
+ * Get Multisession Disk Info
  */
-static void azt_transfer(void)
-{ 
-#ifdef AZT_TEST
-  printk("aztcd: executing azt_transfer Time:%li\n",jiffies);
-#endif
-  if (CURRENT_VALID) {
-    while (CURRENT -> nr_sectors) {
-      int bn = CURRENT -> sector / 4;
-      int i;
-      for (i = 0; i < AZT_BUF_SIZ && azt_buf_bn[i] != bn; ++i)
-	;
-      if (i < AZT_BUF_SIZ) {
-	int offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
-	int nr_sectors = 4 - (CURRENT -> sector & 3);
-	if (azt_buf_out != i) {
-	  azt_buf_out = i;
-	  if (azt_buf_bn[i] != bn) {
-	    azt_buf_out = -1;
-	    continue;
-	  }
-	}
-	if (nr_sectors > CURRENT -> nr_sectors)
-	  nr_sectors = CURRENT -> nr_sectors;
-	memcpy(CURRENT -> buffer, azt_buf + offs, nr_sectors * 512);
-	CURRENT -> nr_sectors -= nr_sectors;
-	CURRENT -> sector += nr_sectors;
-	CURRENT -> buffer += nr_sectors * 512;
-      } else {
-	azt_buf_out = -1;
-	break;
-      }
-    }
-  }
-}
-
+static int aztGetMultiDiskInfo(void)
+{ int limit, k=5;
+  unsigned char test;
+  struct azt_Toc qInfo;
 
-static void do_aztcd_request(void)
-{
-#ifdef AZT_TEST
-  printk(" do_aztcd_request(%ld+%ld) Time:%li\n", CURRENT -> sector, CURRENT -> nr_sectors,jiffies);
-#endif
-  if (DiskInfo.audio) 
-    { printk("aztcd: Error, tried to mount an Audio CD\n");
-      end_request(0);
-      return;
-    }
-  azt_transfer_is_active = 1;
-  while (CURRENT_VALID) {
-    if (CURRENT->bh) {
-      if (!buffer_locked(CURRENT->bh))
-	panic(DEVICE_NAME ": block not locked");
-    }
-    azt_transfer();
-    if (CURRENT -> nr_sectors == 0) {
-      end_request(1);
-    } else {
-      azt_buf_out = -1;         /* Want to read a block not in buffer */
-      if (azt_state == AZT_S_IDLE) {
-	if ((!aztTocUpToDate)||aztDiskChanged) {
-	  if (aztUpdateToc() < 0) {
-	    while (CURRENT_VALID)
-	      end_request(0);
-	    break;
-	  }
-	}
-	azt_state = AZT_S_START;
-	AztTries = 5;
-	SET_TIMER(azt_poll, HZ/100);
-      }
-      break;
-    }
-  }
-  azt_transfer_is_active = 0;
-#ifdef AZT_TEST2
-  printk("azt_next_bn:%x  azt_buf_in:%x azt_buf_out:%x  azt_buf_bn:%x\n", \
-	  azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
-  printk(" do_aztcd_request ends  Time:%li\n",jiffies);
+#ifdef AZT_DEBUG
+  printk("aztcd: starting aztGetMultiDiskInfo\n");
 #endif
-}
 
-static void azt_poll(void)
-{
-    int st = 0;
-    int loop_ctl = 1;
-    int skip = 0;
+  do { azt_Play.start.min   = Toc[DiskInfo.last+1].diskTime.min;
+       azt_Play.start.sec   = Toc[DiskInfo.last+1].diskTime.sec;
+       azt_Play.start.frame = Toc[DiskInfo.last+1].diskTime.frame;
+       test=0;
 
-    if (azt_error) {                            
-	if (aztSendCmd(ACMD_GET_ERROR)) RETURN("azt_poll 1");
-	STEN_LOW;
-	azt_error=inb(DATA_PORT)&0xFF;
-	printk("aztcd: I/O error 0x%02x\n", azt_error);
-	azt_invalidate_buffers();
-#ifdef WARN_IF_READ_FAILURE
-	if (AztTries == 5)
-	  printk("aztcd: Read of Block %d Failed - Maybe Audio Disk?\n", azt_next_bn);
+       for (limit=30;limit>0;limit--)   /*Seek for LeadIn of next session*/
+           { if (aztSeek(&azt_Play)) RETURNM("aztGetMultiDiskInfo 1",-1);
+             if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetMultiDiskInfo 2",-1);
+             if ((qInfo.track==0)&&(qInfo.pointIndex)) break;  /*LeadIn found*/
+             if ((azt_Play.start.sec+=10) > 59)
+                { azt_Play.start.sec=0;
+                  azt_Play.start.min++;
+                }
+           }
+       if (!limit) break;  /*Check, if a leadin track was found, if not we're
+                             at the end of the disk*/
+#ifdef AZT_DEBUG_MULTISESSION
+       printk("leadin found track %d  pointIndex %x  limit %d\n",qInfo.track,qInfo.pointIndex,limit);
 #endif
-	if (!AztTries--) {
-	  printk("aztcd: Read of Block %d Failed, Maybe Audio Disk? Giving up\n", azt_next_bn);
-	  if (azt_transfer_is_active) {
-	    AztTries = 0;
-	    loop_ctl = 0;
-	  }
-	  if (CURRENT_VALID)
-	    end_request(0);
-	  AztTries = 5;
-	}
-    azt_error = 0;
-    azt_state = AZT_S_STOP;
-    }
-
-    while (loop_ctl)
-    {
-      loop_ctl = 0;   /* each case must flip this back to 1 if we want
-			 to come back up here */
-      switch (azt_state) {
-
-	case AZT_S_IDLE:
-#ifdef AZT_TEST3
-	  if (azt_state!=azt_state_old) {
-	    azt_state_old=azt_state;
-	    printk("AZT_S_IDLE\n");
-	    }
+       for (limit=300;limit>0;limit--)
+           { if (++azt_Play.start.frame>74)
+                { azt_Play.start.frame=0;
+                  if (azt_Play.start.sec > 59)
+                     { azt_Play.start.sec=0;
+                       azt_Play.start.min++;
+                     }
+                }     
+             if (aztSeek(&azt_Play)) RETURNM("aztGetMultiDiskInfo 3",-1);
+             if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetMultiDiskInfo 4",-1);
+             if (qInfo.pointIndex==0xA0)   /*Number of NextTrack*/
+	        { DiskInfo.next = qInfo.diskTime.min;
+	          DiskInfo.next = azt_bcd2bin(DiskInfo.next);
+	          test=test|0x01;
+	        }
+             if (qInfo.pointIndex==0xA1)   /*Number of LastTrack*/
+	 	{ DiskInfo.last  = qInfo.diskTime.min;
+	   	  DiskInfo.last  = azt_bcd2bin(DiskInfo.last);
+	   	  test=test|0x02;
+	 	}
+      	     if (qInfo.pointIndex==0xA2)   /*DiskLength*/
+	 	{ DiskInfo.diskLength.min  =qInfo.diskTime.min;
+	   	  DiskInfo.diskLength.sec  =qInfo.diskTime.sec;
+	   	  DiskInfo.diskLength.frame=qInfo.diskTime.frame;
+	  	  test=test|0x04;
+	 	}
+      	     if ((qInfo.pointIndex==DiskInfo.next)&&(test&0x01))   /*StartTime of Next Track*/
+		{ DiskInfo.nextSession.min=qInfo.diskTime.min;
+	  	  DiskInfo.nextSession.sec=qInfo.diskTime.sec;
+	  	  DiskInfo.nextSession.frame=qInfo.diskTime.frame;
+	  	  test=test|0x08;
+		}
+      	     if (test==0x0F) break;
+   	   }
+#ifdef AZT_DEBUG_MULTISESSION
+       printk ("MultiDisk Info: first %d next %d last %d length %02x:%02x.%02x dez  first %02x:%02x.%02x dez  next %02x:%02x.%02x dez\n",
+     		DiskInfo.first,
+		DiskInfo.next,
+		DiskInfo.last,
+		DiskInfo.diskLength.min,
+		DiskInfo.diskLength.sec,
+		DiskInfo.diskLength.frame,
+		DiskInfo.firstTrack.min,
+		DiskInfo.firstTrack.sec,
+		DiskInfo.firstTrack.frame,
+		DiskInfo.nextSession.min,
+		DiskInfo.nextSession.sec,
+		DiskInfo.nextSession.frame);
 #endif
-	  return;
+       if (test!=0x0F) 
+           break;
+       else 
+           DiskInfo.multi=1;   /*found TOC of more than one session*/
+       aztGetToc(1);
+     } while(--k);
 
-	case AZT_S_START:
-#ifdef AZT_TEST3
-	  if (azt_state!=azt_state_old) {
-	    azt_state_old=azt_state;
-	    printk("AZT_S_START\n");
-	  }
+#ifdef AZT_DEBUG
+  printk ("aztcd: exiting aztGetMultiDiskInfo  Time:%li\n",jiffies);
+#endif
+  return 0;
+}
 #endif
-	  if(aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 2");  /*result will be checked by aztStatus() */
-	  azt_state = azt_mode == 1 ? AZT_S_READ : AZT_S_MODE;
-	  AztTimeout = 3000;
-	  break;
 
-	case AZT_S_MODE:
-#ifdef AZT_TEST3
-	  if (azt_state!=azt_state_old) {
-	    azt_state_old=azt_state;
-	    printk("AZT_S_MODE\n");
-	  }
+/*
+ * Read the table of contents (TOC)
+ */
+static int aztGetToc(int multi)
+{ int i, px;
+  int limit;
+  struct azt_Toc qInfo;
+
+#ifdef AZT_DEBUG
+  printk("aztcd: starting aztGetToc  Time:%li\n",jiffies);
 #endif
-	  if (!skip) {
-	    if ((st = aztStatus()) != -1) {
-	      if ((st & AST_DSK_CHG)||(st & AST_NOT_READY)) {
-		aztDiskChanged = 1;
-		aztTocUpToDate = 0;
-		azt_invalidate_buffers();
-		end_request(0);
-		printk("aztcd: Disk Changed or Not Ready 1 - Unmount Disk!\n");
-	      }
-	    } else break;
-	  }
-	  skip = 0;
+  if (!multi)
+     { for (i = 0; i < MAX_TRACKS; i++)
+ 	    Toc[i].pointIndex = 0;
+       i = DiskInfo.last + 3;
+     }
+  else
+     { for (i = DiskInfo.next; i < MAX_TRACKS; i++)
+     	    Toc[i].pointIndex = 0; 
+       i = DiskInfo.last + 4 - DiskInfo.next;
+     }
 
-	  if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) {
-	    aztDiskChanged = 1;
-	    aztTocUpToDate = 0;
-            printk("aztcd: Disk Changed or Not Ready 2 - Unmount Disk!\n");
-            end_request(0);
-	    printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n");
-	    if (azt_transfer_is_active) {
-	      azt_state = AZT_S_START;
-	      loop_ctl = 1;   /* goto immediately */
-	      break;
-	    }
-	    azt_state = AZT_S_IDLE;
-	    while (CURRENT_VALID)
-	      end_request(0);
-	    return;
-	  }
-				
-/*	  if (aztSendCmd(ACMD_SET_MODE)) RETURN("azt_poll 3");
-	  outb(0x01, DATA_PORT);          
-	  PA_OK;
-	  STEN_LOW;
-*/        if (aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 4");
-	  STEN_LOW;
-	  azt_mode = 1;
-	  azt_state = AZT_S_READ;
-	  AztTimeout = 3000;
+/*Is there a good reason to stop motor before TOC read?
+  if (aztSendCmd(ACMD_STOP)) RETURNM("aztGetToc 1",-1);
+      STEN_LOW_WAIT;
+*/
 
-	  break;
+  if (!multi)
+     { azt_mode = 0x05;
+       if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) RETURNM("aztGetToc 2",-1);
+       STEN_LOW_WAIT;
+     }
+  for (limit = 300; limit > 0; limit--)
+      { if (multi)
+           { if (++azt_Play.start.sec > 59)
+                { azt_Play.start.sec=0;
+                  azt_Play.start.min++;
+                }
+             if (aztSeek(&azt_Play)) RETURNM("aztGetToc 3",-1);
+           }
+	if (aztGetQChannelInfo(&qInfo) < 0)
+	    break;
 
+	px = azt_bcd2bin(qInfo.pointIndex);
 
-	case AZT_S_READ:
-#ifdef AZT_TEST3
-	  if (azt_state!=azt_state_old)  {
-	    azt_state_old=azt_state;
-	    printk("AZT_S_READ\n");
-	  }
-#endif
-	  if (!skip) {
-	      if ((st = aztStatus()) != -1) {
-		if ((st & AST_DSK_CHG)||(st & AST_NOT_READY)) {
-		aztDiskChanged = 1;
-		aztTocUpToDate = 0;
-		azt_invalidate_buffers();
-		printk("aztcd: Disk Changed or Not Ready 3 - Unmount Disk!\n");
-                end_request(0);               
-		}
-	      } else break;
-	  } 
-	    
-	  skip = 0;
-	  if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) {
-	    aztDiskChanged = 1;
-	    aztTocUpToDate = 0;
-	    printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n");
-	    if (azt_transfer_is_active) {
-	      azt_state = AZT_S_START;
-	      loop_ctl = 1;
-	      break;
-	    }
-	    azt_state = AZT_S_IDLE;
-	    while (CURRENT_VALID)
-	    end_request(0);
-	    return;
-	  }
+	if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
+  	    if (Toc[px].pointIndex == 0)
+	       { Toc[px] = qInfo;
+		 i--;
+	       }
 
-	  if (CURRENT_VALID) {
-	    struct azt_Play_msf msf;
-	    int i;
-	    azt_next_bn = CURRENT -> sector / 4;
-	    azt_hsg2msf(azt_next_bn, &msf.start);
-	    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*/
-#ifdef AZT_TEST3
-	    printk("---reading msf-address %x:%x:%x  %x:%x:%x\n",msf.start.min,msf.start.sec,msf.start.frame,msf.end.min,msf.end.sec,msf.end.frame);
-	    printk("azt_next_bn:%x  azt_buf_in:%x azt_buf_out:%x  azt_buf_bn:%x\n", \
-		    azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
-#endif 
-            if (azt_read_mode==AZT_MODE_2)
-               { sendAztCmd(ACMD_PLAY_READ_RAW, &msf); /*XA disks in raw mode*/
-               }
-            else
-               { sendAztCmd(ACMD_PLAY_READ, &msf);     /*others in cooked mode*/
-               }
-	    azt_state = AZT_S_DATA;
-	    AztTimeout = READ_TIMEOUT;
-	  } else {
-	    azt_state = AZT_S_STOP;
-	    loop_ctl = 1;
+	if (i <= 0)
 	    break;
-	  }
-
-	  break;
+      }
 
+  Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
+  Toc[DiskInfo.last].trackTime    = DiskInfo.diskLength;
 
-	case AZT_S_DATA:
-#ifdef AZT_TEST3
-	  if (azt_state!=azt_state_old)  {
-	    azt_state_old=azt_state;
-	    printk("AZT_S_DATA\n");
-	  }
+#ifdef AZT_DEBUG_MULTISESSION 
+  printk("aztcd: exiting aztGetToc\n");
+  for (i = 1; i <= DiskInfo.last+1; i++)
+       printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez  %02X:%02X.%02X dez\n",
+               i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
+               Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
+               Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
+  for (i = 100; i < 103; i++)
+       printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez  %02X:%02X.%02X dez\n",
+               i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
+               Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
+               Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
 #endif
 
-	  st = inb(STATUS_PORT) & AFL_STATUSorDATA; 
+  return limit > 0 ? 0 : -1;
+}
 
-	  switch (st) {
 
-	    case AFL_DATA:
-#ifdef AZT_TEST3
-	      if (st!=azt_st_old)  {
-		azt_st_old=st; 
-		printk("---AFL_DATA st:%x\n",st);
-	      }
+/*##########################################################################
+  Kernel Interface Functions
+  ##########################################################################
+*/
+void aztcd_setup(char *str, int *ints)
+{  if (ints[0] > 0)
+      azt_port = ints[1];
+   if (ints[0] > 1)
+      azt_cont = ints[2];
+}
+
+/* 
+ * Checking if the media has been changed not yet implemented
+*/
+static int check_aztcd_media_change(kdev_t full_dev)
+{ return 0;
+}
+
+/*
+ * Kernel IO-controls
+*/
+static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+{       int i, st;
+	struct azt_Toc qInfo;
+	struct cdrom_ti ti;
+	struct cdrom_tochdr tocHdr;
+	struct cdrom_msf msf;
+	struct cdrom_tocentry entry;
+	struct azt_Toc *tocPtr;            
+	struct cdrom_subchnl subchnl;
+        struct cdrom_volctrl volctrl;
+
+#ifdef AZT_DEBUG
+	printk("aztcd: starting aztcd_ioctl - Command:%x   Time: %li\n",cmd, jiffies);
+        printk("aztcd Status %x\n", getAztStatus());
 #endif
-	      if (!AztTries--) {
-		printk("aztcd: Read of Block %d Failed, Maybe Audio Disk ? Giving up\n", azt_next_bn);
-		if (azt_transfer_is_active) {
-		  AztTries = 0;
-		  break;
+	if (!ip) RETURNM("aztcd_ioctl 1",-EINVAL);
+	if (getAztStatus()<0) RETURNM("aztcd_ioctl 2", -EIO);
+	if ((!aztTocUpToDate)||(aztDiskChanged))
+	{ if ((i=aztUpdateToc())<0) RETURNM("aztcd_ioctl 3", i); /* error reading TOC */
+	}
+
+	switch (cmd)
+	{
+	case CDROMSTART:     /* Spin up the drive. Don't know, what to do,
+	                        at least close the tray */
+#if AZT_PRIVATE_IOCTLS 
+	        if (aztSendCmd(ACMD_CLOSE)) RETURNM("aztcd_ioctl 4",-1);
+	        STEN_LOW_WAIT;
+#endif
+		break;
+	case CDROMSTOP:      /* Spin down the drive */
+		if (aztSendCmd(ACMD_STOP)) RETURNM("aztcd_ioctl 5",-1);
+		STEN_LOW_WAIT;
+		/* should we do anything if it fails? */
+		aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+		break;
+	case CDROMPAUSE:     /* Pause the drive */
+                if (aztAudioStatus != CDROM_AUDIO_PLAY) return -EINVAL; 
+
+		if (aztGetQChannelInfo(&qInfo) < 0)
+		{ /* didn't get q channel info */
+		  aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+		  RETURNM("aztcd_ioctl 7",0);
 		}
-		if (CURRENT_VALID)
-		  end_request(0);
-		AztTries = 5;
-	      }
-	      azt_state = AZT_S_START;
-	      AztTimeout = READ_TIMEOUT;
-	      loop_ctl = 1;
-	      break;
+		azt_Play.start = qInfo.diskTime;        /* remember restart point */
 
-	    case AFL_STATUSorDATA:
-#ifdef AZT_TEST3
-	      if (st!=azt_st_old)  {
-		azt_st_old=st;
-		printk("---AFL_STATUSorDATA st:%x\n",st);
-	      }
+		if (aztSendCmd(ACMD_PAUSE)) RETURNM("aztcd_ioctl 8",-1);
+		STEN_LOW_WAIT;
+		aztAudioStatus = CDROM_AUDIO_PAUSED;
+		break;
+	case CDROMRESUME:    /* Play it again, Sam */
+		if (aztAudioStatus != CDROM_AUDIO_PAUSED) return -EINVAL;
+		/* restart the drive at the saved position. */
+		i = aztPlay(&azt_Play);
+		if (i < 0)
+		{ aztAudioStatus = CDROM_AUDIO_ERROR;
+		  return -EIO;
+		}
+		aztAudioStatus = CDROM_AUDIO_PLAY;
+		break;
+	case CDROMMULTISESSION: /*multisession support -- experimental*/
+	        { struct cdrom_multisession ms;
+#ifdef AZT_DEBUG
+   		  printk("aztcd ioctl MULTISESSION\n");
 #endif
-	      break;
+		  st = verify_area(VERIFY_READ, (void*) arg, sizeof(struct cdrom_multisession));
+		  if (st) return st;
+		  memcpy_fromfs(&ms, (void*) arg, sizeof(struct cdrom_multisession));
+		  if (ms.addr_format == CDROM_MSF) 
+		     { ms.addr.msf.minute = azt_bcd2bin(DiskInfo.lastSession.min);
+		       ms.addr.msf.second = azt_bcd2bin(DiskInfo.lastSession.sec);
+		       ms.addr.msf.frame  = azt_bcd2bin(DiskInfo.lastSession.frame);
+		     } 
+		  else if (ms.addr_format == CDROM_LBA)
+		       ms.addr.lba = azt_msf2hsg(&DiskInfo.lastSession);
+		  else
+		       return -EINVAL;
+		  ms.xa_flag = DiskInfo.xa;
+ 		  st = verify_area(VERIFY_WRITE, (void*) arg, sizeof(struct cdrom_multisession));
+		  if (st) return st;
+	  	  memcpy_tofs((void*) arg, &ms, sizeof(struct cdrom_multisession));
+#ifdef AZT_DEBUG 
+ 		  if (ms.addr_format == CDROM_MSF) 
+                      printk("aztcd multisession xa:%d, msf:%02x:%02x.%02x [%02x:%02x.%02x])\n",
+			      ms.xa_flag, ms.addr.msf.minute, ms.addr.msf.second, 
+			      ms.addr.msf.frame, DiskInfo.lastSession.min,
+			      DiskInfo.lastSession.sec, DiskInfo.lastSession.frame);
+		  else
+		      printk("aztcd multisession %d, lba:0x%08x [%02x:%02x.%02x])\n",
+			      ms.xa_flag, ms.addr.lba, DiskInfo.lastSession.min,
+			      DiskInfo.lastSession.sec, DiskInfo.lastSession.frame);
+#endif
+   		  return 0;
+		}
+	case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
+		if (st) return st;
+		memcpy_fromfs(&ti, (void *) arg, sizeof ti);
+		if (ti.cdti_trk0 < DiskInfo.first
+			|| ti.cdti_trk0 > DiskInfo.last
+			|| ti.cdti_trk1 < ti.cdti_trk0)
+		{ return -EINVAL;
+		}
+		if (ti.cdti_trk1 > DiskInfo.last)
+		    ti.cdti_trk1 = DiskInfo.last;
+		azt_Play.start = Toc[ti.cdti_trk0].diskTime;
+		azt_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
+#ifdef AZT_DEBUG
+printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n",
+	azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame,
+	azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame);
+#endif
+		i = aztPlay(&azt_Play);
+		if (i < 0)
+		{ aztAudioStatus = CDROM_AUDIO_ERROR;
+		  return -EIO;
+		}
+		aztAudioStatus = CDROM_AUDIO_PLAY;
+		break;
+	case CDROMPLAYMSF:   /* Play starting at the given MSF address. */
+/*              if (aztAudioStatus == CDROM_AUDIO_PLAY) 
+		{ if (aztSendCmd(ACMD_STOP)) RETURNM("aztcd_ioctl 9",-1);
+		  STEN_LOW;
+		  aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+		}
+*/
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
+		if (st) return st;
+		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+		/* convert to bcd */
+		azt_bin2bcd(&msf.cdmsf_min0);
+		azt_bin2bcd(&msf.cdmsf_sec0);
+		azt_bin2bcd(&msf.cdmsf_frame0);
+		azt_bin2bcd(&msf.cdmsf_min1);
+		azt_bin2bcd(&msf.cdmsf_sec1);
+		azt_bin2bcd(&msf.cdmsf_frame1);
+		azt_Play.start.min = msf.cdmsf_min0;
+		azt_Play.start.sec = msf.cdmsf_sec0;
+		azt_Play.start.frame = msf.cdmsf_frame0;
+		azt_Play.end.min = msf.cdmsf_min1;
+		azt_Play.end.sec = msf.cdmsf_sec1;
+		azt_Play.end.frame = msf.cdmsf_frame1;
+#ifdef AZT_DEBUG
+printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n",
+azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame,
+azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame);
+#endif
+		i = aztPlay(&azt_Play);
+		if (i < 0)
+		{ aztAudioStatus = CDROM_AUDIO_ERROR;
+		  return -EIO;
+		}
+		aztAudioStatus = CDROM_AUDIO_PLAY;
+		break;
 
-	    default:
-#ifdef AZT_TEST3
-	      if (st!=azt_st_old)  {
-		azt_st_old=st;
-		printk("---default: st:%x\n",st);
-	      }
+	case CDROMREADTOCHDR:        /* Read the table of contents header */
+		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr);
+		if (st) return st;
+		tocHdr.cdth_trk0 = DiskInfo.first;
+		tocHdr.cdth_trk1 = DiskInfo.last;
+		memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
+		break;
+	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof entry);
+		if (st) return st;
+		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
+		if (st) return st;
+		memcpy_fromfs(&entry, (void *) arg, sizeof entry);
+		if ((!aztTocUpToDate)||aztDiskChanged) aztUpdateToc();
+		if (entry.cdte_track == CDROM_LEADOUT)
+		  tocPtr = &Toc[DiskInfo.last + 1];
+		else if (entry.cdte_track > DiskInfo.last
+				|| entry.cdte_track < DiskInfo.first)
+		{ return -EINVAL;
+		}
+		else 
+		  tocPtr = &Toc[entry.cdte_track];
+		entry.cdte_adr = tocPtr -> ctrl_addr;
+		entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
+		if (entry.cdte_format == CDROM_LBA)
+		  entry.cdte_addr.lba = azt_msf2hsg(&tocPtr -> diskTime);
+		else if (entry.cdte_format == CDROM_MSF)
+		{ entry.cdte_addr.msf.minute = azt_bcd2bin(tocPtr -> diskTime.min);
+		  entry.cdte_addr.msf.second = azt_bcd2bin(tocPtr -> diskTime.sec);
+		  entry.cdte_addr.msf.frame  = azt_bcd2bin(tocPtr -> diskTime.frame);
+		}
+		else
+		{ return -EINVAL;
+		}
+		memcpy_tofs((void *) arg, &entry, sizeof entry);
+		break;
+	case CDROMSUBCHNL:   /* Get subchannel info */
+		st = verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_subchnl));
+		if (st) { 
+#ifdef AZT_DEBUG
+		          printk("aztcd: exiting aztcd_ioctl - Error 1 - Command:%x\n",cmd);
 #endif
-	      AztTries = 5;
-	      if (!CURRENT_VALID && azt_buf_in == azt_buf_out) {
-		azt_state = AZT_S_STOP;
-		loop_ctl = 1;
+		          return st;
+		        }  
+		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl));
+		if (st) { 
+#ifdef AZT_DEBUG
+		          printk("aztcd: exiting aztcd_ioctl - Error 2 - Command:%x\n",cmd);
+#endif
+		          return st;
+		        }  
+		memcpy_fromfs(&subchnl, (void *) arg, sizeof (struct cdrom_subchnl));
+		if (aztGetQChannelInfo(&qInfo) < 0)
+		if (st) { 
+#ifdef AZT_DEBUG
+		          printk("aztcd: exiting aztcd_ioctl - Error 3 - Command:%x\n",cmd);
+#endif
+     		  	  return -EIO;
+     		  	}  
+		subchnl.cdsc_audiostatus = aztAudioStatus;
+		subchnl.cdsc_adr = qInfo.ctrl_addr;
+		subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4;
+		subchnl.cdsc_trk = azt_bcd2bin(qInfo.track);
+		subchnl.cdsc_ind = azt_bcd2bin(qInfo.pointIndex);
+		if (subchnl.cdsc_format == CDROM_LBA)
+		{ subchnl.cdsc_absaddr.lba = azt_msf2hsg(&qInfo.diskTime);
+		  subchnl.cdsc_reladdr.lba = azt_msf2hsg(&qInfo.trackTime);
+		}
+		else  /*default*/
+		{ subchnl.cdsc_format = CDROM_MSF;
+		  subchnl.cdsc_absaddr.msf.minute = azt_bcd2bin(qInfo.diskTime.min);
+		  subchnl.cdsc_absaddr.msf.second = azt_bcd2bin(qInfo.diskTime.sec);
+		  subchnl.cdsc_absaddr.msf.frame  = azt_bcd2bin(qInfo.diskTime.frame);
+		  subchnl.cdsc_reladdr.msf.minute = azt_bcd2bin(qInfo.trackTime.min);
+		  subchnl.cdsc_reladdr.msf.second = azt_bcd2bin(qInfo.trackTime.sec);
+		  subchnl.cdsc_reladdr.msf.frame  = azt_bcd2bin(qInfo.trackTime.frame);
+		}
+		memcpy_tofs((void *) arg, &subchnl, sizeof (struct cdrom_subchnl));
 		break;
-	      }
-	      if (azt_read_count<=0)
-		printk("aztcd: warning - try to read 0 frames\n");
-	      while (azt_read_count)      /*??? fast read ahead loop*/
-	       { azt_buf_bn[azt_buf_in] = -1;
-		 DTEN_LOW;                      /*??? unsolved problem, very
-						      seldom we get timeouts
-						      here, don't now the real
-						      reason. With my drive this
-						      sometimes also happens with
-						      Aztech's original driver under
-						      DOS. Is it a hardware bug? 
-						      I tried to recover from such
-						      situations here. Zimmermann*/
-		 if (aztTimeOutCount>=AZT_TIMEOUT) 
-		  { printk("read_count:%d CURRENT->nr_sectors:%ld azt_buf_in:%d\n", azt_read_count,CURRENT->nr_sectors,azt_buf_in);
-		    printk("azt_transfer_is_active:%x\n",azt_transfer_is_active);
-		    azt_read_count=0;
-		    azt_state = AZT_S_STOP;
-		    loop_ctl = 1;
-		    end_request(1);  /*should we have here (1) or (0)? */
-		  }
-		 else
-		  { if (azt_read_mode==AZT_MODE_2)
-		       { insb(DATA_PORT, azt_buf + CD_FRAMESIZE_RAW * azt_buf_in, CD_FRAMESIZE_RAW);
+	case CDROMVOLCTRL:   /* Volume control 
+	 * With my Aztech CD268-01A volume control does not work, I can only
+	   turn the channels on (any value !=0) or off (value==0). Maybe it
+           works better with your drive */
+                st=verify_area(VERIFY_READ,(void *) arg, sizeof(volctrl));
+                if (st) return (st);
+                memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl));
+		azt_Play.start.min = 0x21;
+		azt_Play.start.sec = 0x84;
+		azt_Play.start.frame = volctrl.channel0;
+		azt_Play.end.min =     volctrl.channel1;
+		azt_Play.end.sec =     volctrl.channel2;
+		azt_Play.end.frame =   volctrl.channel3;
+                sendAztCmd(ACMD_SET_VOLUME, &azt_Play);
+                STEN_LOW_WAIT;
+                break;
+	case CDROMEJECT:
+                aztUnlockDoor(); /* Assume user knows what they're doing */
+	       /* all drives can at least stop! */
+		if (aztAudioStatus == CDROM_AUDIO_PLAY) 
+		{ if (aztSendCmd(ACMD_STOP)) RETURNM("azt_ioctl 10",-1);
+		  STEN_LOW_WAIT;
+		}
+		if (aztSendCmd(ACMD_EJECT)) RETURNM("azt_ioctl 11",-1);
+		STEN_LOW_WAIT;
+		aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+		break;
+	case CDROMEJECT_SW:
+		azt_auto_eject = (char) arg;
+		break;	
+        case CDROMRESET:
+        	outb(ACMD_SOFT_RESET,CMD_PORT);   /*send reset*/
+	        STEN_LOW;
+	        if (inb(DATA_PORT)!=AFL_OP_OK)    /*OP_OK?*/
+	          { printk("aztcd: AZTECH CD-ROM drive does not respond\n");
+                  }
+                break;
+/*Take care, the following code is not compatible with other CD-ROM drivers,
+  use it at your own risk with cdplay.c. Set AZT_PRIVATE_IOCTLS to 0 in aztcd.h,
+  if you do not want to use it!
+*/                  
+#if AZT_PRIVATE_IOCTLS 
+	case CDROMREADCOOKED: /*read data in mode 1 (2048 Bytes)*/
+	case CDROMREADRAW:    /*read data in mode 2 (2336 Bytes)*/
+		{ st = verify_area(VERIFY_READ,  (void *) arg, sizeof msf);
+		  if (st) return st;
+		  st = verify_area(VERIFY_WRITE, (void *) arg, sizeof buf);
+		  if (st) return st;
+		  memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+		  /* convert to bcd */
+		  azt_bin2bcd(&msf.cdmsf_min0);
+		  azt_bin2bcd(&msf.cdmsf_sec0);
+		  azt_bin2bcd(&msf.cdmsf_frame0);
+		  msf.cdmsf_min1=0;
+		  msf.cdmsf_sec1=0;
+		  msf.cdmsf_frame1=1; /*read only one frame*/
+		  azt_Play.start.min = msf.cdmsf_min0;
+		  azt_Play.start.sec = msf.cdmsf_sec0;
+		  azt_Play.start.frame = msf.cdmsf_frame0;
+		  azt_Play.end.min = msf.cdmsf_min1;
+		  azt_Play.end.sec = msf.cdmsf_sec1;
+		  azt_Play.end.frame = msf.cdmsf_frame1;
+		  if (cmd==CDROMREADRAW)
+		  { if (DiskInfo.xa)
+		       { return -1;         /*XA Disks can't be read raw*/
 		       }
-		    else
-		       { insb(DATA_PORT, azt_buf + CD_FRAMESIZE * azt_buf_in, CD_FRAMESIZE);
-                       }
-		    azt_read_count--;
-#ifdef AZT_TEST3
-		    printk("AZT_S_DATA; ---I've read data- read_count: %d\n",azt_read_count);
-		    printk("azt_next_bn:%d  azt_buf_in:%d azt_buf_out:%d  azt_buf_bn:%d\n", \
-			 azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
-#endif
-		    azt_buf_bn[azt_buf_in] = azt_next_bn++;
-		    if (azt_buf_out == -1)
-		      azt_buf_out = azt_buf_in;
-		    azt_buf_in = azt_buf_in + 1 == AZT_BUF_SIZ ? 0 : azt_buf_in + 1;
+		    else   
+		       { if (sendAztCmd(ACMD_PLAY_READ_RAW, &azt_Play)) return -1;
+		         DTEN_LOW;
+		         insb(DATA_PORT,buf,CD_FRAMESIZE_RAW);
+		         memcpy_tofs((void *) arg, &buf, CD_FRAMESIZE_RAW);
+		       }  
+		  }
+		  else /*CDROMREADCOOKED*/
+		  { if (sendAztCmd(ACMD_PLAY_READ, &azt_Play)) return -1;
+		    DTEN_LOW;
+		    insb(DATA_PORT,buf,CD_FRAMESIZE);
+		    memcpy_tofs((void *) arg, &buf, CD_FRAMESIZE);
 		  }
-	       }
-	      if (!azt_transfer_is_active) {
-		while (CURRENT_VALID) {
-		  azt_transfer();
-		  if (CURRENT -> nr_sectors == 0)
-		    end_request(1);
-		  else
-		    break;
-		}
-	      }
-
-	      if (CURRENT_VALID
-		&& (CURRENT -> sector / 4 < azt_next_bn ||
-		CURRENT -> sector / 4 > azt_next_bn + AZT_BUF_SIZ)) {
-		azt_state = AZT_S_STOP;
-		loop_ctl = 1;
-		break;
-	      }
-	      AztTimeout = READ_TIMEOUT;   
-	      if (azt_read_count==0) {
-		azt_state = AZT_S_STOP; 
-		loop_ctl = 1;
-		break;           
-	      } 
-	      break;
-	    }
-    break;
-
-
-	case AZT_S_STOP:
-#ifdef AZT_TEST3
-	  if (azt_state!=azt_state_old) {
-	    azt_state_old=azt_state;
-	    printk("AZT_S_STOP\n");
-	  }
+		 } 
+                break;
+	case CDROMSEEK:    /*seek msf address*/
+		st = verify_area(VERIFY_READ,  (void *) arg, sizeof msf);
+		if (st) return st;
+		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+		/* convert to bcd */
+		azt_bin2bcd(&msf.cdmsf_min0);
+		azt_bin2bcd(&msf.cdmsf_sec0);
+		azt_bin2bcd(&msf.cdmsf_frame0);
+		azt_Play.start.min = msf.cdmsf_min0;
+		azt_Play.start.sec = msf.cdmsf_sec0;
+		azt_Play.start.frame = msf.cdmsf_frame0;
+		if (aztSeek(&azt_Play)) return -1;
+                break;
+#endif /*end of incompatible code*/       
+	case CDROMREADMODE1: /*set read data in mode 1*/
+                return aztSetDiskType(AZT_MODE_1);
+	case CDROMREADMODE2: /*set read data in mode 2*/
+                return aztSetDiskType(AZT_MODE_2);	    
+	default:
+		return -EINVAL;
+	}
+#ifdef AZT_DEBUG
+        printk("aztcd: exiting aztcd_ioctl Command:%x  Time:%li\n",cmd,jiffies);
 #endif
-	  if (azt_read_count!=0) printk("aztcd: discard data=%x frames\n",azt_read_count);
-	  while (azt_read_count!=0) {
-	    int i;
-	    if ( !(inb(STATUS_PORT) & AFL_DATA) ) {
-	      if (azt_read_mode==AZT_MODE_2)
-	         for (i=0; i<CD_FRAMESIZE_RAW; i++) inb(DATA_PORT);
-	      else   
-	         for (i=0; i<CD_FRAMESIZE; i++) inb(DATA_PORT);
-	    }
-	    azt_read_count--;
-	  }  
-	  if (aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 5");
-	  azt_state = AZT_S_STOPPING;
-	  AztTimeout = 1000;
-	  break;
+	return 0;
+}
 
-	case AZT_S_STOPPING:
-#ifdef AZT_TEST3
-	  if (azt_state!=azt_state_old) {
-	    azt_state_old=azt_state;
-	    printk("AZT_S_STOPPING\n");
-	  }
+/*
+ * Take care of the different block sizes between cdrom and Linux.
+ * When Linux gets variable block sizes this will probably go away.
+ */
+static void azt_transfer(void)
+{ 
+#ifdef AZT_TEST
+  printk("aztcd: executing azt_transfer Time:%li\n",jiffies);
 #endif
-
-	  if ((st = aztStatus()) == -1 && AztTimeout)
-	    break;
-
-	  if ((st != -1) && ((st & AST_DSK_CHG)||(st & AST_NOT_READY))) {
-	    aztDiskChanged = 1;
-	    aztTocUpToDate = 0;
-	    azt_invalidate_buffers();
-	    printk("aztcd: Disk Changed or Not Ready 4 - Unmount Disk!\n");
-	    end_request(0);
+  if (CURRENT_VALID) {
+    while (CURRENT -> nr_sectors) {
+      int bn = CURRENT -> sector / 4;
+      int i;
+      for (i = 0; i < AZT_BUF_SIZ && azt_buf_bn[i] != bn; ++i)
+	;
+      if (i < AZT_BUF_SIZ) {
+	int offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
+	int nr_sectors = 4 - (CURRENT -> sector & 3);
+	if (azt_buf_out != i) {
+	  azt_buf_out = i;
+	  if (azt_buf_bn[i] != bn) {
+	    azt_buf_out = -1;
+	    continue;
 	  }
+	}
+	if (nr_sectors > CURRENT -> nr_sectors)
+	  nr_sectors = CURRENT -> nr_sectors;
+	memcpy(CURRENT -> buffer, azt_buf + offs, nr_sectors * 512);
+	CURRENT -> nr_sectors -= nr_sectors;
+	CURRENT -> sector += nr_sectors;
+	CURRENT -> buffer += nr_sectors * 512;
+      } else {
+	azt_buf_out = -1;
+	break;
+      }
+    }
+  }
+}
 
-
-#ifdef AZT_TEST3
-	  printk("CURRENT_VALID %d azt_mode %d\n",
-	     CURRENT_VALID, azt_mode);
+static void do_aztcd_request(void)
+{
+#ifdef AZT_TEST
+  printk(" do_aztcd_request(%ld+%ld) Time:%li\n", CURRENT -> sector, CURRENT -> nr_sectors,jiffies);
 #endif
-
-	  if (CURRENT_VALID) {
-	    if (st != -1) {
-	      if (azt_mode == 1) {
-		azt_state = AZT_S_READ;
-		loop_ctl = 1;
-		skip = 1;
-		break;
-	      } else {
-		azt_state = AZT_S_MODE;
-		loop_ctl = 1;
-		skip = 1;
-		break;
-	      }
-	    } else {
-	      azt_state = AZT_S_START;
-	      AztTimeout = 1;
-	    }
-	  } else {
-	    azt_state = AZT_S_IDLE;
-	    return;
+  if (DiskInfo.audio) 
+    { printk("aztcd: Error, tried to mount an Audio CD\n");
+      end_request(0);
+      return;
+    }
+  azt_transfer_is_active = 1;
+  while (CURRENT_VALID) {
+    if (CURRENT->bh) {
+      if (!buffer_locked(CURRENT->bh))
+	panic(DEVICE_NAME ": block not locked");
+    }
+    azt_transfer();
+    if (CURRENT -> nr_sectors == 0) {
+      end_request(1);
+    } else {
+      azt_buf_out = -1;         /* Want to read a block not in buffer */
+      if (azt_state == AZT_S_IDLE) {
+	if ((!aztTocUpToDate)||aztDiskChanged) {
+	  if (aztUpdateToc() < 0) {
+	    while (CURRENT_VALID)
+	      end_request(0);
+	    break;
 	  }
-	  break;
-
-	default:
-	  printk("aztcd: invalid state %d\n", azt_state);
-	  return;
-      }  /* case */
-    } /* while */
-  
-
-   if (!AztTimeout--) 
-    { printk("aztcd: timeout in state %d\n", azt_state);
-      azt_state = AZT_S_STOP;
-      if (aztSendCmd(ACMD_STOP)) RETURN("azt_poll 6"); 
-      STEN_LOW_WAIT;     
-    };
-
-  SET_TIMER(azt_poll, HZ/100);
+	}
+	azt_state = AZT_S_START;
+	AztTries = 5;
+	SET_TIMER(azt_poll, HZ/100);
+      }
+      break;
+    }
+  }
+  azt_transfer_is_active = 0;
+#ifdef AZT_TEST2
+  printk("azt_next_bn:%x  azt_buf_in:%x azt_buf_out:%x  azt_buf_bn:%x\n", \
+	  azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
+  printk(" do_aztcd_request ends  Time:%li\n",jiffies);
+#endif
 }
 
+
 static void azt_invalidate_buffers(void)
 { int i;
 
@@ -1531,21 +1566,6 @@
 }
 
 
-static struct file_operations azt_fops = {
-	NULL,                   /* lseek - default */
-	block_read,             /* read - general block-dev read */
-	block_write,            /* write - general block-dev write */
-	NULL,                   /* readdir - bad */
-	NULL,                   /* select */
-	aztcd_ioctl,            /* ioctl */
-	NULL,                   /* mmap */
-	aztcd_open,             /* open */
-	aztcd_release,          /* release */
-	NULL,                   /* fsync */
-	NULL,                   /* fasync*/
-	check_aztcd_media_change, /*media change*/
-	NULL                    /* revalidate*/
-};
 
 /*
  * Test for presence of drive and initialize it.  Called at boot time.
@@ -1703,432 +1723,482 @@
         if ((azt_port==0x1f0)||(azt_port==0x170))  
 	   request_region(azt_port, 8, "aztcd");  /*IDE-interface*/
         else
-	   request_region(azt_port, 4, "aztcd");  /*proprietary inferface*/
+	   request_region(azt_port, 4, "aztcd");  /*proprietary interface*/
         
 	azt_invalidate_buffers();
-	aztPresent = 1;
-	aztCloseDoor();
-/*	printk("aztcd: End Init\n");
-*/      return (0);
-}
-
-
-static void azt_hsg2msf(long hsg, struct msf *msf)
-{       hsg += 150;
-	msf -> min = hsg / 4500;
-	hsg %= 4500;
-	msf -> sec = hsg / 75;
-	msf -> frame = hsg % 75;
-#ifdef AZT_DEBUG
-	if (msf->min  >=70) printk("aztcd: Error hsg2msf address Minutes\n");
-	if (msf->sec  >=60) printk("aztcd: Error hsg2msf address Seconds\n");
-	if (msf->frame>=75) printk("aztcd: Error hsg2msf address Frames\n");
-#endif
-	azt_bin2bcd(&msf -> min);           /* convert to BCD */
-	azt_bin2bcd(&msf -> sec);
-	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;
+	aztPresent = 1;
+	aztCloseDoor();
+/*	printk("aztcd: End Init\n");
+*/      return (0);
 }
 
-static void azt_bin2bcd(unsigned char *p)
-{       int u, t;
+#ifdef MODULE
 
-	u = *p % 10;
-	t = *p / 10;
-	*p = u | (t << 4);
+int init_module(void)
+{
+	return aztcd_init();
 }
 
-static int azt_bcd2bin(unsigned char bcd)
-{       return (bcd >> 4) * 10 + (bcd & 0xF);
-}
+void cleanup_module(void)
+{
+  if ((unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL))    
+    { printk("What's that: can't unregister aztcd\n");
+      return;
+    }
+  if ((azt_port==0x1f0)||(azt_port==0x170))  
+    { SWITCH_IDE_MASTER;
+      release_region(azt_port,8);  /*IDE-interface*/
+    }
+  else
+      release_region(azt_port,4);  /*proprietary interface*/
+  printk(KERN_INFO "aztcd module released.\n");
+}   
+#endif MODULE
 
-/*
- * Read a value from the drive.  Should return quickly, so a busy wait
- * is used to avoid excessive rescheduling. The read command itself must
- * be issued with aztSendCmd() directly before
- */
-static int aztGetValue(unsigned char *result)
-{       int s;
 
+/*##########################################################################
+  Aztcd State Machine: Controls Drive Operating State
+  ##########################################################################
+*/
+static void azt_poll(void)
+{
+    int st = 0;
+    int loop_ctl = 1;
+    int skip = 0;
+
+    if (azt_error) {                            
+	if (aztSendCmd(ACMD_GET_ERROR)) RETURN("azt_poll 1");
 	STEN_LOW;
-	if (aztTimeOutCount>=AZT_TIMEOUT)
-	{       printk("aztcd: aztGetValue timeout\n");
-		return -1;
+	azt_error=inb(DATA_PORT)&0xFF;
+	printk("aztcd: I/O error 0x%02x\n", azt_error);
+	azt_invalidate_buffers();
+#ifdef WARN_IF_READ_FAILURE
+	if (AztTries == 5)
+	  printk("aztcd: Read of Block %d Failed - Maybe Audio Disk?\n", azt_next_bn);
+#endif
+	if (!AztTries--) {
+	  printk("aztcd: Read of Block %d Failed, Maybe Audio Disk? Giving up\n", azt_next_bn);
+	  if (azt_transfer_is_active) {
+	    AztTries = 0;
+	    loop_ctl = 0;
+	  }
+	  if (CURRENT_VALID)
+	    end_request(0);
+	  AztTries = 5;
 	}
-	s = inb(DATA_PORT) & 0xFF;
-	*result = (unsigned char) s;
-	return 0;
-}
-
+    azt_error = 0;
+    azt_state = AZT_S_STOP;
+    }
 
-/*
- * Read the current Q-channel info.  Also used for reading the
- * table of contents.
- */
-int aztGetQChannelInfo(struct azt_Toc *qp)
-{       unsigned char notUsed;
-	int st;
+    while (loop_ctl)
+    {
+      loop_ctl = 0;   /* each case must flip this back to 1 if we want
+			 to come back up here */
+      switch (azt_state) {
 
-#ifdef AZT_DEBUG
-	printk("aztcd: starting aztGetQChannelInfo  Time:%li\n",jiffies);
-#endif
-	if ((st=getAztStatus())==-1)        RETURNM("aztGetQChannelInfo 1",-1);
-	if (aztSendCmd(ACMD_GET_Q_CHANNEL)) RETURNM("aztGetQChannelInfo 2",-1);
-	/*STEN_LOW_WAIT; ??? Dosemu0.60's cdrom.c does not like STEN_LOW_WAIT here*/
-	if (aztGetValue(&notUsed)) RETURNM("aztGetQChannelInfo 3",-1); /*??? Nullbyte einlesen*/
-	if ((st&AST_MODE_BITS)==AST_INITIAL)
-	 { qp->ctrl_addr=0;      /* when audio stop ACMD_GET_Q_CHANNEL returns */
-	   qp->track=0;          /* only one byte with Aztech drives */
-	   qp->pointIndex=0;
-	   qp->trackTime.min=0;
-	   qp->trackTime.sec=0;
-	   qp->trackTime.frame=0;
-	   qp->diskTime.min=0;
-	   qp->diskTime.sec=0;
-	   qp->diskTime.frame=0;
-	   return 0;  
-	 }
-	else
-	 { if (aztGetValue(&qp -> ctrl_addr) < 0)       RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> track) < 0)           RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> pointIndex) < 0)      RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> trackTime.min) < 0)   RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> trackTime.sec) < 0)   RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> trackTime.frame) < 0) RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&notUsed) < 0)               RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> diskTime.min) < 0)    RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> diskTime.sec) < 0)    RETURNM("aztGetQChannelInfo 4",-1);
-	   if (aztGetValue(&qp -> diskTime.frame) < 0)  RETURNM("aztGetQChannelInfo 4",-1);
-	 }
-#ifdef AZT_DEBUG
-	printk("aztcd: exiting aztGetQChannelInfo  Time:%li\n",jiffies);
+	case AZT_S_IDLE:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_IDLE\n");
+	    }
 #endif
-	return 0;
-}
+	  return;
 
-/*
- * Read the table of contents (TOC) and TOC header if necessary
- */
-static int aztUpdateToc()
-{       int st;
+	case AZT_S_START:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_START\n");
+	  }
+#endif
+	  if(aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 2");  /*result will be checked by aztStatus() */
+	  azt_state = azt_mode == 1 ? AZT_S_READ : AZT_S_MODE;
+	  AztTimeout = 3000;
+	  break;
 
-#ifdef AZT_DEBUG
-	printk("aztcd: starting aztUpdateToc  Time:%li\n",jiffies);
-#endif  
-	if (aztTocUpToDate)
-		return 0;
+	case AZT_S_MODE:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_MODE\n");
+	  }
+#endif
+	  if (!skip) {
+	    if ((st = aztStatus()) != -1) {
+	      if ((st & AST_DSK_CHG)||(st & AST_NOT_READY)) {
+		aztDiskChanged = 1;
+		aztTocUpToDate = 0;
+		azt_invalidate_buffers();
+		end_request(0);
+		printk("aztcd: Disk Changed or Not Ready 1 - Unmount Disk!\n");
+	      }
+	    } else break;
+	  }
+	  skip = 0;
 
-	if (aztGetDiskInfo() < 0)
-		return -EIO;
+	  if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) {
+	    aztDiskChanged = 1;
+	    aztTocUpToDate = 0;
+            printk("aztcd: Disk Changed or Not Ready 2 - Unmount Disk!\n");
+            end_request(0);
+	    printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n");
+	    if (azt_transfer_is_active) {
+	      azt_state = AZT_S_START;
+	      loop_ctl = 1;   /* goto immediately */
+	      break;
+	    }
+	    azt_state = AZT_S_IDLE;
+	    while (CURRENT_VALID)
+	      end_request(0);
+	    return;
+	  }
+				
+/*	  if (aztSendCmd(ACMD_SET_MODE)) RETURN("azt_poll 3");
+	  outb(0x01, DATA_PORT);          
+	  PA_OK;
+	  STEN_LOW;
+*/        if (aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 4");
+	  STEN_LOW;
+	  azt_mode = 1;
+	  azt_state = AZT_S_READ;
+	  AztTimeout = 3000;
 
-	if (aztGetToc(0) < 0)
-		return -EIO;
+	  break;
 
-        /*audio disk detection
-          with my Aztech drive there is no audio status bit, so I use the copy
-          protection bit of the first track. If this track is copy protected 
-          (copy bit = 0), I assume, it's an audio  disk. Strange, but works ??? */
-        if (!(Toc[DiskInfo.first].ctrl_addr & 0x40)) 
-           DiskInfo.audio=1;
-        else 
-           DiskInfo.audio=0;
 
-        /* XA detection */
-	if (! DiskInfo.audio) 
-	   { azt_Play.start.min   = 0;  /*XA detection only seems to work*/
-             azt_Play.start.sec   = 2;  /*when we play a track*/
-	     azt_Play.start.frame = 0;
-	     azt_Play.end.min     = 0;
-	     azt_Play.end.sec     = 0;
-	     azt_Play.end.frame   = 1;
-	     if (sendAztCmd(ACMD_PLAY_READ, &azt_Play)) return -1;
-	     DTEN_LOW;
-	     for (st=0;st<CD_FRAMESIZE;st++) inb(DATA_PORT);
-           } 
-        DiskInfo.xa = getAztStatus() & AST_MODE;
-        if (DiskInfo.xa) 
-           { printk("aztcd: XA support experimental - mail results to zimmerma@rz.fht-esslingen.de\n");
-           }
-        
-        /*multisession detection
-          support for multisession CDs is done automatically with Aztech drives,
-          we don't have to take care about TOC redirection; if we want the isofs
-          to take care about redirection, we have to set AZT_MULTISESSION to 1*/
-        DiskInfo.multi=0;
-#if AZT_MULTISESSION
-  	if (DiskInfo.xa) 
-  	   { aztGetMultiDiskInfo(); /*here Disk.Info.multi is set*/
-  	   }
+	case AZT_S_READ:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old)  {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_READ\n");
+	  }
 #endif
-        if (DiskInfo.multi)
-           { DiskInfo.lastSession.min  = Toc[DiskInfo.next].diskTime.min;
-	     DiskInfo.lastSession.sec  = Toc[DiskInfo.next].diskTime.sec;
-             DiskInfo.lastSession.frame= Toc[DiskInfo.next].diskTime.frame;
-             printk("aztcd: Multisession support experimental\n");
-           }
-        else
-           { DiskInfo.lastSession.min  = Toc[DiskInfo.first].diskTime.min;
-	     DiskInfo.lastSession.sec  = Toc[DiskInfo.first].diskTime.sec;
-             DiskInfo.lastSession.frame= Toc[DiskInfo.first].diskTime.frame;
-           }
+	  if (!skip) {
+	      if ((st = aztStatus()) != -1) {
+		if ((st & AST_DSK_CHG)||(st & AST_NOT_READY)) {
+		aztDiskChanged = 1;
+		aztTocUpToDate = 0;
+		azt_invalidate_buffers();
+		printk("aztcd: Disk Changed or Not Ready 3 - Unmount Disk!\n");
+                end_request(0);               
+		}
+	      } else break;
+	  } 
+	    
+	  skip = 0;
+	  if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) {
+	    aztDiskChanged = 1;
+	    aztTocUpToDate = 0;
+	    printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n");
+	    if (azt_transfer_is_active) {
+	      azt_state = AZT_S_START;
+	      loop_ctl = 1;
+	      break;
+	    }
+	    azt_state = AZT_S_IDLE;
+	    while (CURRENT_VALID)
+	    end_request(0);
+	    return;
+	  }
 
-	aztTocUpToDate = 1;
-#ifdef AZT_DEBUG
-	printk("aztcd: exiting aztUpdateToc  Time:%li\n",jiffies);
+	  if (CURRENT_VALID) {
+	    struct azt_Play_msf msf;
+	    int i;
+	    azt_next_bn = CURRENT -> sector / 4;
+	    azt_hsg2msf(azt_next_bn, &msf.start);
+	    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
-	return 0;
-}
+	    msf.end.min = 0;
+	    msf.end.sec = 0;            
+	    msf.end.frame = azt_read_count ;/*Mitsumi here reads 0xffffff sectors*/
+#ifdef AZT_TEST3
+	    printk("---reading msf-address %x:%x:%x  %x:%x:%x\n",msf.start.min,msf.start.sec,msf.start.frame,msf.end.min,msf.end.sec,msf.end.frame);
+	    printk("azt_next_bn:%x  azt_buf_in:%x azt_buf_out:%x  azt_buf_bn:%x\n", \
+		    azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
+#endif 
+            if (azt_read_mode==AZT_MODE_2)
+               { sendAztCmd(ACMD_PLAY_READ_RAW, &msf); /*XA disks in raw mode*/
+               }
+            else
+               { sendAztCmd(ACMD_PLAY_READ, &msf);     /*others in cooked mode*/
+               }
+	    azt_state = AZT_S_DATA;
+	    AztTimeout = READ_TIMEOUT;
+	  } else {
+	    azt_state = AZT_S_STOP;
+	    loop_ctl = 1;
+	    break;
+	  }
 
+	  break;
 
-/* Read the table of contents header, i.e. no. of tracks and start of first 
- * track
- */
-static int aztGetDiskInfo()
-{ int limit;
-  unsigned char test;
-  struct azt_Toc qInfo;
 
-#ifdef AZT_DEBUG
-  printk("aztcd: starting aztGetDiskInfo  Time:%li\n",jiffies);
-#endif
-  if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) RETURNM("aztGetDiskInfo 1",-1);
-  STEN_LOW_WAIT;
-  test=0;
-  for (limit=300;limit>0;limit--)
-   {  if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetDiskInfo 2",-1);
-      if (qInfo.pointIndex==0xA0)   /*Number of FirstTrack*/
-	{ DiskInfo.first = qInfo.diskTime.min;
-	  DiskInfo.first = azt_bcd2bin(DiskInfo.first);
-	  test=test|0x01;
-	}
-      if (qInfo.pointIndex==0xA1)   /*Number of LastTrack*/
-	{ DiskInfo.last  = qInfo.diskTime.min;
-	  DiskInfo.last  = azt_bcd2bin(DiskInfo.last);
-	  test=test|0x02;
-	}
-      if (qInfo.pointIndex==0xA2)   /*DiskLength*/
-	{ DiskInfo.diskLength.min=qInfo.diskTime.min;
-	  DiskInfo.diskLength.sec=qInfo.diskTime.sec;
-	  DiskInfo.diskLength.frame=qInfo.diskTime.frame;
-	  test=test|0x04;
-	}
-      if ((qInfo.pointIndex==DiskInfo.first)&&(test&0x01))   /*StartTime of First Track*/
-	{ DiskInfo.firstTrack.min=qInfo.diskTime.min;
-	  DiskInfo.firstTrack.sec=qInfo.diskTime.sec;
-	  DiskInfo.firstTrack.frame=qInfo.diskTime.frame;
-	  test=test|0x08;
-	}
-      if (test==0x0F) break;
-   }
-#ifdef AZT_DEBUG
-  printk ("aztcd: exiting aztGetDiskInfo  Time:%li\n",jiffies);
-  printk("Disk Info: first %d last %d length %02X:%02X.%02X dez  first %02X:%02X.%02X dez\n",
-  	  DiskInfo.first,
-	  DiskInfo.last,
-	  DiskInfo.diskLength.min,
-	  DiskInfo.diskLength.sec,
-	  DiskInfo.diskLength.frame,
-	  DiskInfo.firstTrack.min,
-	  DiskInfo.firstTrack.sec,
-	  DiskInfo.firstTrack.frame);
+	case AZT_S_DATA:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old)  {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_DATA\n");
+	  }
 #endif
-  if (test!=0x0F) return -1;
-  return 0;
-}
 
-#if AZT_MULTISESSION
-/*
- * Get Multisession Disk Info
- */
-static int aztGetMultiDiskInfo(void)
-{ int limit, k=5;
-  unsigned char test;
-  struct azt_Toc qInfo;
+	  st = inb(STATUS_PORT) & AFL_STATUSorDATA; 
 
-#ifdef AZT_DEBUG
-  printk("aztcd: starting aztGetMultiDiskInfo\n");
+	  switch (st) {
+
+	    case AFL_DATA:
+#ifdef AZT_TEST3
+	      if (st!=azt_st_old)  {
+		azt_st_old=st; 
+		printk("---AFL_DATA st:%x\n",st);
+	      }
 #endif
+	      if (!AztTries--) {
+		printk("aztcd: Read of Block %d Failed, Maybe Audio Disk ? Giving up\n", azt_next_bn);
+		if (azt_transfer_is_active) {
+		  AztTries = 0;
+		  break;
+		}
+		if (CURRENT_VALID)
+		  end_request(0);
+		AztTries = 5;
+	      }
+	      azt_state = AZT_S_START;
+	      AztTimeout = READ_TIMEOUT;
+	      loop_ctl = 1;
+	      break;
 
-  do { azt_Play.start.min   = Toc[DiskInfo.last+1].diskTime.min;
-       azt_Play.start.sec   = Toc[DiskInfo.last+1].diskTime.sec;
-       azt_Play.start.frame = Toc[DiskInfo.last+1].diskTime.frame;
-       test=0;
+	    case AFL_STATUSorDATA:
+#ifdef AZT_TEST3
+	      if (st!=azt_st_old)  {
+		azt_st_old=st;
+		printk("---AFL_STATUSorDATA st:%x\n",st);
+	      }
+#endif
+	      break;
 
-       for (limit=30;limit>0;limit--)   /*Seek for LeadIn of next session*/
-           { if (aztSeek(&azt_Play)) RETURNM("aztGetMultiDiskInfo 1",-1);
-             if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetMultiDiskInfo 2",-1);
-             if ((qInfo.track==0)&&(qInfo.pointIndex)) break;  /*LeadIn found*/
-             if ((azt_Play.start.sec+=10) > 59)
-                { azt_Play.start.sec=0;
-                  azt_Play.start.min++;
-                }
-           }
-       if (!limit) break;  /*Check, if a leadin track was found, if not we're
-                             at the end of the disk*/
-#ifdef AZT_DEBUG_MULTISESSION
-       printk("leadin found track %d  pointIndex %x  limit %d\n",qInfo.track,qInfo.pointIndex,limit);
+	    default:
+#ifdef AZT_TEST3
+	      if (st!=azt_st_old)  {
+		azt_st_old=st;
+		printk("---default: st:%x\n",st);
+	      }
 #endif
-       for (limit=300;limit>0;limit--)
-           { if (++azt_Play.start.frame>74)
-                { azt_Play.start.frame=0;
-                  if (azt_Play.start.sec > 59)
-                     { azt_Play.start.sec=0;
-                       azt_Play.start.min++;
-                     }
-                }     
-             if (aztSeek(&azt_Play)) RETURNM("aztGetMultiDiskInfo 3",-1);
-             if (aztGetQChannelInfo(&qInfo)<0) RETURNM("aztGetMultiDiskInfo 4",-1);
-             if (qInfo.pointIndex==0xA0)   /*Number of NextTrack*/
-	        { DiskInfo.next = qInfo.diskTime.min;
-	          DiskInfo.next = azt_bcd2bin(DiskInfo.next);
-	          test=test|0x01;
-	        }
-             if (qInfo.pointIndex==0xA1)   /*Number of LastTrack*/
-	 	{ DiskInfo.last  = qInfo.diskTime.min;
-	   	  DiskInfo.last  = azt_bcd2bin(DiskInfo.last);
-	   	  test=test|0x02;
-	 	}
-      	     if (qInfo.pointIndex==0xA2)   /*DiskLength*/
-	 	{ DiskInfo.diskLength.min  =qInfo.diskTime.min;
-	   	  DiskInfo.diskLength.sec  =qInfo.diskTime.sec;
-	   	  DiskInfo.diskLength.frame=qInfo.diskTime.frame;
-	  	  test=test|0x04;
-	 	}
-      	     if ((qInfo.pointIndex==DiskInfo.next)&&(test&0x01))   /*StartTime of Next Track*/
-		{ DiskInfo.nextSession.min=qInfo.diskTime.min;
-	  	  DiskInfo.nextSession.sec=qInfo.diskTime.sec;
-	  	  DiskInfo.nextSession.frame=qInfo.diskTime.frame;
-	  	  test=test|0x08;
-		}
-      	     if (test==0x0F) break;
-   	   }
-#ifdef AZT_DEBUG_MULTISESSION
-       printk ("MultiDisk Info: first %d next %d last %d length %02x:%02x.%02x dez  first %02x:%02x.%02x dez  next %02x:%02x.%02x dez\n",
-     		DiskInfo.first,
-		DiskInfo.next,
-		DiskInfo.last,
-		DiskInfo.diskLength.min,
-		DiskInfo.diskLength.sec,
-		DiskInfo.diskLength.frame,
-		DiskInfo.firstTrack.min,
-		DiskInfo.firstTrack.sec,
-		DiskInfo.firstTrack.frame,
-		DiskInfo.nextSession.min,
-		DiskInfo.nextSession.sec,
-		DiskInfo.nextSession.frame);
+	      AztTries = 5;
+	      if (!CURRENT_VALID && azt_buf_in == azt_buf_out) {
+		azt_state = AZT_S_STOP;
+		loop_ctl = 1;
+		break;
+	      }
+	      if (azt_read_count<=0)
+		printk("aztcd: warning - try to read 0 frames\n");
+	      while (azt_read_count)      /*??? fast read ahead loop*/
+	       { azt_buf_bn[azt_buf_in] = -1;
+		 DTEN_LOW;                      /*??? unsolved problem, very
+						      seldom we get timeouts
+						      here, don't now the real
+						      reason. With my drive this
+						      sometimes also happens with
+						      Aztech's original driver under
+						      DOS. Is it a hardware bug? 
+						      I tried to recover from such
+						      situations here. Zimmermann*/
+		 if (aztTimeOutCount>=AZT_TIMEOUT) 
+		  { printk("read_count:%d CURRENT->nr_sectors:%ld azt_buf_in:%d\n", azt_read_count,CURRENT->nr_sectors,azt_buf_in);
+		    printk("azt_transfer_is_active:%x\n",azt_transfer_is_active);
+		    azt_read_count=0;
+		    azt_state = AZT_S_STOP;
+		    loop_ctl = 1;
+		    end_request(1);  /*should we have here (1) or (0)? */
+		  }
+		 else
+		  { if (azt_read_mode==AZT_MODE_2)
+		       { insb(DATA_PORT, azt_buf + CD_FRAMESIZE_RAW * azt_buf_in, CD_FRAMESIZE_RAW);
+		       }
+		    else
+		       { insb(DATA_PORT, azt_buf + CD_FRAMESIZE * azt_buf_in, CD_FRAMESIZE);
+                       }
+		    azt_read_count--;
+#ifdef AZT_TEST3
+		    printk("AZT_S_DATA; ---I've read data- read_count: %d\n",azt_read_count);
+		    printk("azt_next_bn:%d  azt_buf_in:%d azt_buf_out:%d  azt_buf_bn:%d\n", \
+			 azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
 #endif
-       if (test!=0x0F) 
-           break;
-       else 
-           DiskInfo.multi=1;   /*found TOC of more than one session*/
-       aztGetToc(1);
-     } while(--k);
+		    azt_buf_bn[azt_buf_in] = azt_next_bn++;
+		    if (azt_buf_out == -1)
+		      azt_buf_out = azt_buf_in;
+		    azt_buf_in = azt_buf_in + 1 == AZT_BUF_SIZ ? 0 : azt_buf_in + 1;
+		  }
+	       }
+	      if (!azt_transfer_is_active) {
+		while (CURRENT_VALID) {
+		  azt_transfer();
+		  if (CURRENT -> nr_sectors == 0)
+		    end_request(1);
+		  else
+		    break;
+		}
+	      }
 
-#ifdef AZT_DEBUG
-  printk ("aztcd: exiting aztGetMultiDiskInfo  Time:%li\n",jiffies);
-#endif
-  return 0;
-}
-#endif
+	      if (CURRENT_VALID
+		&& (CURRENT -> sector / 4 < azt_next_bn ||
+		CURRENT -> sector / 4 > azt_next_bn + AZT_BUF_SIZ)) {
+		azt_state = AZT_S_STOP;
+		loop_ctl = 1;
+		break;
+	      }
+	      AztTimeout = READ_TIMEOUT;   
+	      if (azt_read_count==0) {
+		azt_state = AZT_S_STOP; 
+		loop_ctl = 1;
+		break;           
+	      } 
+	      break;
+	    }
+    break;
 
-/*
- * Read the table of contents (TOC)
- */
-static int aztGetToc(int multi)
-{ int i, px;
-  int limit;
-  struct azt_Toc qInfo;
 
-#ifdef AZT_DEBUG
-  printk("aztcd: starting aztGetToc  Time:%li\n",jiffies);
+	case AZT_S_STOP:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_STOP\n");
+	  }
 #endif
-  if (!multi)
-     { for (i = 0; i < MAX_TRACKS; i++)
- 	    Toc[i].pointIndex = 0;
-       i = DiskInfo.last + 3;
-     }
-  else
-     { for (i = DiskInfo.next; i < MAX_TRACKS; i++)
-     	    Toc[i].pointIndex = 0; 
-       i = DiskInfo.last + 4 - DiskInfo.next;
-     }
+	  if (azt_read_count!=0) printk("aztcd: discard data=%x frames\n",azt_read_count);
+	  while (azt_read_count!=0) {
+	    int i;
+	    if ( !(inb(STATUS_PORT) & AFL_DATA) ) {
+	      if (azt_read_mode==AZT_MODE_2)
+	         for (i=0; i<CD_FRAMESIZE_RAW; i++) inb(DATA_PORT);
+	      else   
+	         for (i=0; i<CD_FRAMESIZE; i++) inb(DATA_PORT);
+	    }
+	    azt_read_count--;
+	  }  
+	  if (aztSendCmd(ACMD_GET_STATUS)) RETURN("azt_poll 5");
+	  azt_state = AZT_S_STOPPING;
+	  AztTimeout = 1000;
+	  break;
 
-/*Is there a good reason to stop motor before TOC read?
-  if (aztSendCmd(ACMD_STOP)) RETURNM("aztGetToc 1",-1);
-      STEN_LOW_WAIT;
-*/
+	case AZT_S_STOPPING:
+#ifdef AZT_TEST3
+	  if (azt_state!=azt_state_old) {
+	    azt_state_old=azt_state;
+	    printk("AZT_S_STOPPING\n");
+	  }
+#endif
 
-  if (!multi)
-     { azt_mode = 0x05;
-       if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) RETURNM("aztGetToc 2",-1);
-       STEN_LOW_WAIT;
-     }
-  for (limit = 300; limit > 0; limit--)
-      { if (multi)
-           { if (++azt_Play.start.sec > 59)
-                { azt_Play.start.sec=0;
-                  azt_Play.start.min++;
-                }
-             if (aztSeek(&azt_Play)) RETURNM("aztGetToc 3",-1);
-           }
-	if (aztGetQChannelInfo(&qInfo) < 0)
+	  if ((st = aztStatus()) == -1 && AztTimeout)
 	    break;
 
-	px = azt_bcd2bin(qInfo.pointIndex);
+	  if ((st != -1) && ((st & AST_DSK_CHG)||(st & AST_NOT_READY))) {
+	    aztDiskChanged = 1;
+	    aztTocUpToDate = 0;
+	    azt_invalidate_buffers();
+	    printk("aztcd: Disk Changed or Not Ready 4 - Unmount Disk!\n");
+	    end_request(0);
+	  }
 
-	if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
-  	    if (Toc[px].pointIndex == 0)
-	       { Toc[px] = qInfo;
-		 i--;
-	       }
 
-	if (i <= 0)
-	    break;
-      }
+#ifdef AZT_TEST3
+	  printk("CURRENT_VALID %d azt_mode %d\n",
+	     CURRENT_VALID, azt_mode);
+#endif
 
-  Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
-  Toc[DiskInfo.last].trackTime    = DiskInfo.diskLength;
+	  if (CURRENT_VALID) {
+	    if (st != -1) {
+	      if (azt_mode == 1) {
+		azt_state = AZT_S_READ;
+		loop_ctl = 1;
+		skip = 1;
+		break;
+	      } else {
+		azt_state = AZT_S_MODE;
+		loop_ctl = 1;
+		skip = 1;
+		break;
+	      }
+	    } else {
+	      azt_state = AZT_S_START;
+	      AztTimeout = 1;
+	    }
+	  } else {
+	    azt_state = AZT_S_IDLE;
+	    return;
+	  }
+	  break;
 
-#ifdef AZT_DEBUG_MULTISESSION 
-  printk("aztcd: exiting aztGetToc\n");
-  for (i = 1; i <= DiskInfo.last+1; i++)
-       printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez  %02X:%02X.%02X dez\n",
-               i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
-               Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
-               Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
-  for (i = 100; i < 103; i++)
-       printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X dez  %02X:%02X.%02X dez\n",
-               i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
-               Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
-               Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
+	default:
+	  printk("aztcd: invalid state %d\n", azt_state);
+	  return;
+      }  /* case */
+    } /* while */
+  
+
+   if (!AztTimeout--) 
+    { printk("aztcd: timeout in state %d\n", azt_state);
+      azt_state = AZT_S_STOP;
+      if (aztSendCmd(ACMD_STOP)) RETURN("azt_poll 6"); 
+      STEN_LOW_WAIT;     
+    };
+
+  SET_TIMER(azt_poll, HZ/100);
+}
+
+
+/*###########################################################################
+ * Miscellaneous support functions
+  ###########################################################################
+*/
+static void azt_hsg2msf(long hsg, struct msf *msf)
+{       hsg += 150;
+	msf -> min = hsg / 4500;
+	hsg %= 4500;
+	msf -> sec = hsg / 75;
+	msf -> frame = hsg % 75;
+#ifdef AZT_DEBUG
+	if (msf->min  >=70) printk("aztcd: Error hsg2msf address Minutes\n");
+	if (msf->sec  >=60) printk("aztcd: Error hsg2msf address Seconds\n");
+	if (msf->frame>=75) printk("aztcd: Error hsg2msf address Frames\n");
 #endif
+	azt_bin2bcd(&msf -> min);           /* convert to BCD */
+	azt_bin2bcd(&msf -> sec);
+	azt_bin2bcd(&msf -> frame);
+}
 
-  return limit > 0 ? 0 : -1;
+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;
 }
 
-#ifdef MODULE
+static void azt_bin2bcd(unsigned char *p)
+{       int u, t;
 
-int init_module(void)
-{
-	return aztcd_init();
+	u = *p % 10;
+	t = *p / 10;
+	*p = u | (t << 4);
 }
 
-void cleanup_module(void)
-{
-  if ((unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL))    
-    { printk("What's that: can't unregister aztcd\n");
-      return;
-    }
-  if ((azt_port==0x1f0)||(azt_port==0x170))  
-    { SWITCH_IDE_MASTER;
-      release_region(azt_port,8);  /*IDE-interface*/
-    }
-  else
-      release_region(azt_port,4);  /*proprietary interface*/
-  printk(KERN_INFO "aztcd module released.\n");
-}   
-#endif MODULE
+static int azt_bcd2bin(unsigned char bcd)
+{       return (bcd >> 4) * 10 + (bcd & 0xF);
+}
+
+
diff -u --recursive --new-file v1.3.97/linux/drivers/char/ChangeLog linux/drivers/char/ChangeLog
--- v1.3.97/linux/drivers/char/ChangeLog	Wed Apr 24 17:00:37 1996
+++ linux/drivers/char/ChangeLog	Sat May  4 10:02:51 1996
@@ -1,3 +1,34 @@
+Wed Apr 24 14:02:04 1996  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* random.c (add_timer_randomness): Use 2nd derivitive as well to
+		better estimate entropy.
+
+		(rand_initialize): Explicitly initialize all the pointers
+		to NULL.  (Clearing pointers using memset isn't portable.)
+		Initialize the random pool with OS-dependent data.
+
+		(random_write): Add sanity checking to the arguments to
+		random_write(), so that bad arguments won't cause a kernel
+		SEGV. 
+
+		(random_read): Update the access time of the device inode
+		when you return data to the user.
+
+		(random_ioctl): Wake up the random_wait channel when there
+		are only WAIT_INPUT_BITS available.  Add more paranoia
+		checks to make sure entropy_count doesn't go beyond the
+		bounds of (0, POOLSIZE).  Add a few missing verify_area
+		checks.  Add support for the RNDCLEARPOOL ioctl, which
+		zaps the random pool.
+
+		(add_timer_randomness): Wake up the random_wait
+		channel only when there are WAIT_INPUT_BITS available.
+
+		(random_select): Allow a random refresh daemon process to
+		select on /dev/random for writing; wake up the daemon when
+		there are less than WAIT_OUTPUT_BITS bits of randomness
+		available.
+
 Tue Apr 23 22:56:07 1996    <tytso@rsts-11.mit.edu>
 
 	* tty_io.c (init_dev): Change return code when user attempts to
diff -u --recursive --new-file v1.3.97/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v1.3.97/linux/drivers/char/Config.in	Tue Apr 23 13:57:09 1996
+++ linux/drivers/char/Config.in	Thu May  2 08:06:31 1996
@@ -46,10 +46,11 @@
   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
+  bool '   Power off on shutdown' CONFIG_APM_POWER_OFF
 fi
 bool 'Watchdog Timer Support'	CONFIG_WATCHDOG
 if [ "$CONFIG_WATCHDOG" != "n" ]; then
-  bool '    Disable watchdog shutdown on close' CONFIG_WATCHDOG_NOWAYOUT
+  bool '   Disable watchdog shutdown on close' CONFIG_WATCHDOG_NOWAYOUT
   tristate '   WDT Watchdog timer' CONFIG_WDT
   if [ "$CONFIG_WDT" = "y" ]; then
      bool '       WDT501 features' CONFIG_WDT_501
diff -u --recursive --new-file v1.3.97/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- v1.3.97/linux/drivers/char/apm_bios.c	Fri Apr 19 10:07:58 1996
+++ linux/drivers/char/apm_bios.c	Thu May  2 08:06:31 1996
@@ -24,15 +24,17 @@
  *    Prohibit APM BIOS calls unless apm_enabled.
  *    (Thanks to Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>)
  * April 1996, Stephen Rothwell (Stephen.Rothwell@canb.auug.org.au)
- *    Version 1.0
+ *    Version 1.0 and 1.1
  *
  * History:
  *    0.6b: first version in official kernel, Linux 1.3.46
  *    0.7: changed /proc/apm format, Linux 1.3.58
  *    0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
  *    0.9: only call bios if bios is present, Linux 1.3.72
- *    1.0: use fixed device number, consolidate /proc/apm into
- *         this file, Linux 1.3.85
+ *    1.0: use fixed device number, consolidate /proc/apm into this file,
+ *         Linux 1.3.85
+ *    1.1: support user-space standby and suspend, power off after system
+ *         halted, Linux 1.3.98
  *
  * Reference:
  *
@@ -113,7 +115,9 @@
  * 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.
+ * blanker, and probably won't turn off the backlight when using X11.  Some
+ * problems have been reported when using this option with gpm (if you'd
+ * like to debug this, please do so).
  *
  * 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
@@ -330,7 +334,7 @@
 
 static struct timer_list	apm_timer;
 
-static char			driver_version[] = "1.0";/* no spaces */
+static char			driver_version[] = "1.1";/* no spaces */
 
 #ifdef APM_DEBUG
 static char *	apm_event_name[] = {
@@ -427,7 +431,7 @@
 	return APM_SUCCESS;
 }
 
-static int apm_set_power_state(u_short state)
+int apm_set_power_state(u_short state)
 {
 	u_short	error;
 
@@ -567,13 +571,15 @@
 	return as->events[as->event_tail];
 }
 
-static int queue_event(apm_event_t event)
+static int queue_event(apm_event_t event, struct apm_bios_struct *sender)
 {
 	struct apm_bios_struct *	as;
 	
 	if (user_list == NULL)
 		return 0;
 	for (as = user_list; as != NULL; as = as->next) {
+		if (as == sender)
+			continue;
 		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;
@@ -656,7 +662,8 @@
 	return 0;
 }
 
-static void send_event(apm_event_t event, apm_event_t undo)
+static void send_event(apm_event_t event, apm_event_t undo,
+		       struct apm_bios_struct *sender)
 {
 	callback_list_t *	call;
 	callback_list_t *	fix;
@@ -671,7 +678,7 @@
 		}
 	}
 
-	queue_event(event);
+	queue_event(event, sender);
 }
 
 static void check_events(void)
@@ -682,18 +689,19 @@
 		switch (event) {
 		case APM_SYS_STANDBY:
 		case APM_USER_STANDBY:
-			send_event(event, APM_STANDBY_RESUME);
+			send_event(event, APM_STANDBY_RESUME, NULL);
 			if (standbys_pending <= 0)
 				standby();
 			break;
 
 		case APM_USER_SUSPEND:
 #ifdef CONFIG_APM_IGNORE_USER_SUSPEND
-			apm_set_power_state(APM_STATE_REJECT);
+			if (apm_bios_info.version > 0x100)
+				apm_set_power_state(APM_STATE_REJECT);
 			break;
 #endif
 		case APM_SYS_SUSPEND:
-			send_event(event, APM_NORMAL_RESUME);
+			send_event(event, APM_NORMAL_RESUME, NULL);
 			if (suspends_pending <= 0)
 				suspend();
 			break;
@@ -702,12 +710,12 @@
 		case APM_CRITICAL_RESUME:
 		case APM_STANDBY_RESUME:
 			set_time();
-			send_event(event, 0);
+			send_event(event, 0, NULL);
 			break;
 
 		case APM_LOW_BATTERY:
 		case APM_POWER_STATUS_CHANGE:
-			send_event(event, 0);
+			send_event(event, 0, NULL);
 			break;
 
 		case APM_UPDATE_TIME:
@@ -833,6 +841,17 @@
 	while ((i >= sizeof(event)) && !queue_empty(as)) {
 		event = get_queued_event(as);
 		memcpy_tofs(buf, &event, sizeof(event));
+		switch (event) {
+		case APM_SYS_SUSPEND:
+		case APM_USER_SUSPEND:
+			as->suspends_read++;
+			break;
+
+		case APM_SYS_STANDBY:
+		case APM_USER_STANDBY:
+			as->standbys_read++;
+			break;
+		}
 		buf += sizeof(event);
 		i -= sizeof(event);
 	}
@@ -867,22 +886,30 @@
 	as = filp->private_data;
 	if (check_apm_bios_struct(as, "ioctl"))
 		return -EIO;
+	if (!as->suser)
+		return -EPERM;
 	switch (cmd) {
 	case APM_IOC_STANDBY:
-		if (as->standbys_pending > 0) {
+		if (as->standbys_read > 0) {
+			as->standbys_read--;
 			as->standbys_pending--;
 			standbys_pending--;
-			if (standbys_pending <= 0)
-				standby();
 		}
+		else
+			send_event(APM_USER_STANDBY, APM_STANDBY_RESUME, as);
+		if (standbys_pending <= 0)
+			standby();
 		break;
 	case APM_IOC_SUSPEND:
-		if (as->suspends_pending > 0) {
+		if (as->suspends_read > 0) {
+			as->suspends_read--;
 			as->suspends_pending--;
 			suspends_pending--;
-			if (suspends_pending <= 0)
-				suspend();
 		}
+		else
+			send_event(APM_USER_SUSPEND, APM_NORMAL_RESUME, as);
+		if (suspends_pending <= 0)
+			suspend();
 		break;
 	default:
 		return -EINVAL;
@@ -938,6 +965,7 @@
 	as->magic = APM_BIOS_MAGIC;
 	as->event_tail = as->event_head = 0;
 	as->suspends_pending = as->standbys_pending = 0;
+	as->suspends_read = as->standbys_read = 0;
 	as->suser = suser();
 	as->next = user_list;
 	user_list = as;
diff -u --recursive --new-file v1.3.97/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v1.3.97/linux/drivers/char/lp.c	Tue Apr  2 13:32:20 1996
+++ linux/drivers/char/lp.c	Fri May  3 11:57:38 1996
@@ -183,14 +183,14 @@
 				if (lp_table[minor].runchars > LP_STAT(minor).maxrun)
 					 LP_STAT(minor).maxrun = lp_table[minor].runchars;
 				status = LP_S(minor);
-				if ((status & LP_POUTPA)) {
-					printk(KERN_INFO "lp%d out of paper\n", minor);
-					if (LP_F(minor) & LP_ABORT)
-						return rc?rc:-ENOSPC;
-				} else if (!(status & LP_PSELECD)) {
+				if ((status & LP_OFFL) || !(status & LP_PSELECD)) {
 					printk(KERN_INFO "lp%d off-line\n", minor);
 					if (LP_F(minor) & LP_ABORT)
 						return rc?rc:-EIO;
+				} else if ((status & LP_POUTPA)) {
+					printk(KERN_INFO "lp%d out of paper\n", minor);
+					if (LP_F(minor) & LP_ABORT)
+						return rc?rc:-ENOSPC;
 				} else if (!(status & LP_PERRORP)) {
 					printk(KERN_ERR "lp%d printer error\n", minor);
 					if (LP_F(minor) & LP_ABORT)
@@ -248,18 +248,18 @@
 				 LP_STAT(minor).maxrun = lp_table[minor].runchars;
 			status = LP_S(minor);
 
-			if (status & LP_POUTPA) {
-				printk(KERN_INFO "lp%d out of paper\n", minor);
+			if ((status & LP_OFFL) || !(status & LP_PSELECD)) {
+				printk(KERN_INFO "lp%d off-line\n", minor);
 				if(LP_F(minor) & LP_ABORT)
-					return temp-buf?temp-buf:-ENOSPC;
+					return temp-buf?temp-buf:-EIO;
 				current->state = TASK_INTERRUPTIBLE;
 				current->timeout = jiffies + LP_TIMEOUT_POLLED;
 				schedule();
 			} else
-			if (!(status & LP_PSELECD)) {
-				printk(KERN_INFO "lp%d off-line\n", minor);
+			if (status & LP_POUTPA) {
+				printk(KERN_INFO "lp%d out of paper\n", minor);
 				if(LP_F(minor) & LP_ABORT)
-					return temp-buf?temp-buf:-EIO;
+					return temp-buf?temp-buf:-ENOSPC;
 				current->state = TASK_INTERRUPTIBLE;
 				current->timeout = jiffies + LP_TIMEOUT_POLLED;
 				schedule();
diff -u --recursive --new-file v1.3.97/linux/drivers/char/random.c linux/drivers/char/random.c
--- v1.3.97/linux/drivers/char/random.c	Fri Apr 12 15:51:52 1996
+++ linux/drivers/char/random.c	Sat May  4 10:02:51 1996
@@ -1,9 +1,9 @@
 /*
  * random.c -- A strong random number generator
  *
- * Version 0.96, last modified 29-Dec-95
+ * Version 0.97, last modified 24-Apr-96
  * 
- * Copyright Theodore Ts'o, 1994, 1995.  All rights reserved.
+ * Copyright Theodore Ts'o, 1994, 1995, 1996.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -148,6 +148,58 @@
  * particular randomness source.  They do this by keeping track of the
  * first and second order deltas of the event timings.
  *
+ * Ensuring unpredictability at system startup
+ * ============================================
+ * 
+ * When any operating system starts up, it will go through a sequence
+ * of actions that are fairly predictable by an adversary, especially
+ * if the start-up does not involve interaction with a human operator.
+ * This reduces the actual number of bits of unpredictability in the
+ * entropy pool below the value in entropy_count.  In order to
+ * counteract this effect, it helps to carry information in the
+ * entropy pool across shut-downs and start-ups.  To do this, put the
+ * following lines an appropriate script which is run during the boot
+ * sequence: 
+ *
+ *	echo "Initializing random number generator..."
+ *	# Carry a random seed from start-up to start-up
+ *	# Load and then save 512 bytes, which is the size of the entropy pool
+ * 	if [ -f /etc/random-seed ]; then
+ *		cat /etc/random-seed >/dev/urandom
+ * 	fi
+ *	dd if=/dev/urandom of=/etc/random-seed count=1
+ *
+ * and the following lines in an approproate script which is run as
+ * the system is shutdown:
+ * 
+ *	# Carry a random seed from shut-down to start-up
+ *	# Save 512 bytes, which is the size of the entropy pool
+ *	echo "Saving random seed..."
+ *	dd if=/dev/urandom of=/etc/random-seed count=1
+ * 
+ * For example, on many Linux systems, the appropriate scripts are
+ * usually /etc/rc.d/rc.local and /etc/rc.d/rc.0, respectively.
+ * 
+ * Effectively, these commands cause the contents of the entropy pool
+ * to be saved at shut-down time and reloaded into the entropy pool at
+ * start-up.  (The 'dd' in the addition to the bootup script is to
+ * make sure that /etc/random-seed is different for every start-up,
+ * even if the system crashes without executing rc.0.)  Even with
+ * complete knowledge of the start-up activities, predicting the state
+ * of the entropy pool requires knowledge of the previous history of
+ * the system.
+ *
+ * Configuring the /dev/random driver under Linux
+ * ==============================================
+ *
+ * The /dev/random driver under Linux uses minor numbers 8 and 9 of
+ * the /dev/mem major number (#1).  So if your system does not have
+ * /dev/random and /dev/urandom created already, they can be created
+ * by using the commands:
+ *
+ * 	mknod /dev/random c 1 8
+ * 	mknod /dev/urandom c 1 9
+ * 
  * Acknowledgements:
  * =================
  *
@@ -158,6 +210,8 @@
  * entropy pool, taken from PGP 3.0 (under development).  It has since
  * been modified by myself to provide better mixing in the case where
  * the input values to add_entropy_word() are mostly small numbers.
+ * Dale Worley has also contributed many useful ideas and suggestions
+ * to improve this driver.
  * 
  * Any flaws in the design are solely my responsibility, and should
  * not be attributed to the Phil, Colin, or any of authors of PGP.
@@ -173,6 +227,7 @@
  */
 
 #include <linux/sched.h>
+#include <linux/utsname.h>
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/string.h>
@@ -185,6 +240,10 @@
 #include <asm/io.h>
 
 /*
+ * Configuration information
+ */
+
+/*
  * The pool is stirred with a primitive polynomial of degree 128
  * over GF(2), namely x^128 + x^99 + x^59 + x^31 + x^9 + x^7 + 1.
  * For a pool of size 64, try x^64+x^62+x^38+x^10+x^6+x+1.
@@ -207,6 +266,20 @@
 #error No primitive polynomial available for chosen POOLWORDS
 #endif
 
+/*
+ * The minimum number of bits to release a "wait on input".  Should
+ * probably always be 8, since a /dev/random read can return a single
+ * byte.
+ */
+#define WAIT_INPUT_BITS 8
+/* 
+ * The limit number of bits under which to release a "wait on
+ * output".  Should probably always be the same as WAIT_INPUT_BITS, so
+ * that an output wait releases when and only when a wait on input
+ * would block.
+ */
+#define WAIT_OUTPUT_BITS WAIT_INPUT_BITS
+
 /* There is actually only one of these, globally. */
 struct random_bucket {
 	unsigned add_ptr;
@@ -218,7 +291,7 @@
 /* There is one of these per entropy source */
 struct timer_rand_state {
 	unsigned long	last_time;
-	int 		last_delta;
+	int 		last_delta,last_delta2;
 	int		dont_count_entropy:1;
 };
 
@@ -242,18 +315,55 @@
 static int random_ioctl(struct inode * inode, struct file * file,
 			unsigned int cmd, unsigned long arg);
 
+static inline void add_entropy_word(struct random_bucket *r,
+				    const __u32 input);
 
 #ifndef MIN
 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
 #endif
 	
-void rand_initialize(void)
+/*
+ * Initialize the random pool with standard stuff.
+ *
+ * NOTE: This is an OS-dependent function.
+ */
+static void init_std_data(struct random_bucket *r)
+{
+	__u32 word, *p;
+	int i;
+	
+	add_entropy_word(r, xtime.tv_sec);
+	add_entropy_word(r, xtime.tv_usec);
+
+	for (p = (__u32 *) &system_utsname,
+	     i = sizeof(system_utsname) / sizeof(__u32);
+	     i ; i--, p++) {
+		memcpy(&word, p, sizeof(__u32));
+		add_entropy_word(r, word);
+	}
+	
+}
+
+/* Clear the entropy pool and associated counters. */
+static void rand_clear_pool(void)
 {
 	random_state.add_ptr = 0;
 	random_state.entropy_count = 0;
 	random_state.pool = random_pool;
-	memset(irq_timer_state, 0, sizeof(irq_timer_state));
-	memset(blkdev_timer_state, 0, sizeof(blkdev_timer_state));
+	random_state.input_rotate = 0;
+	memset(random_pool, 0, sizeof(random_pool));
+	init_std_data(&random_state);
+}
+
+void rand_initialize(void)
+{
+	int i;
+
+	rand_clear_pool();
+	for (i = 0; i < NR_IRQS; i++)
+		irq_timer_state[i] = NULL;
+	for (i = 0; i < MAX_BLKDEV; i++)
+		blkdev_timer_state[i] = NULL;
 	extract_timer_state.dont_count_entropy = 1;
 	random_wait = NULL;
 }
@@ -353,7 +463,7 @@
 static void add_timer_randomness(struct random_bucket *r,
 				 struct timer_rand_state *state, unsigned num)
 {
-	int	delta, delta2;
+	int	delta, delta2, delta3;
 	unsigned	nbits;
 	__u32		time;
 
@@ -397,24 +507,30 @@
 	if (!state->dont_count_entropy) {
 		delta = time - state->last_time;
 		state->last_time = time;
+		if (delta < 0) delta = -delta;
 
 		delta2 = delta - state->last_delta;
 		state->last_delta = delta;
-
-		if (delta < 0) delta = -delta;
 		if (delta2 < 0) delta2 = -delta2;
-		delta = MIN(delta, delta2) >> 1;
+
+		delta3 = delta2 - state->last_delta2;
+		state->last_delta2 = delta2;
+		if (delta3 < 0) delta3 = -delta3;
+
+		delta = MIN(MIN(delta, delta2), delta3) >> 1;
 		for (nbits = 0; delta; nbits++)
 			delta >>= 1;
 
 		r->entropy_count += nbits;
-	
+
 		/* Prevent overflow */
 		if (r->entropy_count > POOLBITS)
 			r->entropy_count = POOLBITS;
 	}
 		
-	wake_up_interruptible(&random_wait);	
+	/* Wake up waiting processes, if we have enough entropy. */
+	if (r->entropy_count >= WAIT_INPUT_BITS)
+		wake_up_interruptible(&random_wait);	
 }
 
 void add_keyboard_randomness(unsigned char scancode)
@@ -450,6 +566,152 @@
 			     0x200+major);
 }
 
+#define USE_SHA
+
+#ifdef USE_SHA
+
+#define HASH_BUFFER_SIZE 5
+#define HASH_TRANSFORM SHATransform
+
+/*
+ * SHA transform algorith, taken from code written by Peter Gutman,
+ * and apparently in the public domain.
+ */
+
+/* The SHA f()-functions.  */
+
+#define f1(x,y,z)   ( z ^ ( x & ( y ^ z ) ) )           /* Rounds  0-19 */
+#define f2(x,y,z)   ( x ^ y ^ z )                       /* Rounds 20-39 */
+#define f3(x,y,z)   ( ( x & y ) | ( z & ( x | y ) ) )   /* Rounds 40-59 */
+#define f4(x,y,z)   ( x ^ y ^ z )                       /* Rounds 60-79 */
+
+/* The SHA Mysterious Constants */
+
+#define K1  0x5A827999L                                 /* Rounds  0-19 */
+#define K2  0x6ED9EBA1L                                 /* Rounds 20-39 */
+#define K3  0x8F1BBCDCL                                 /* Rounds 40-59 */
+#define K4  0xCA62C1D6L                                 /* Rounds 60-79 */
+
+#define ROTL(n,X)  ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) )
+
+#define expand(W,i) ( W[ i & 15 ] = \
+		     ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \
+			        W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) )
+
+#define subRound(a, b, c, d, e, f, k, data) \
+    ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) )
+
+
+void SHATransform(__u32 *digest, __u32 *data)
+    {
+    __u32 A, B, C, D, E;     /* Local vars */
+    __u32 eData[ 16 ];       /* Expanded data */
+
+    /* Set up first buffer and local data buffer */
+    A = digest[ 0 ];
+    B = digest[ 1 ];
+    C = digest[ 2 ];
+    D = digest[ 3 ];
+    E = digest[ 4 ];
+    memcpy( eData, data, 16*sizeof(__u32));
+
+    /* Heavy mangling, in 4 sub-rounds of 20 interations each. */
+    subRound( A, B, C, D, E, f1, K1, eData[  0 ] );
+    subRound( E, A, B, C, D, f1, K1, eData[  1 ] );
+    subRound( D, E, A, B, C, f1, K1, eData[  2 ] );
+    subRound( C, D, E, A, B, f1, K1, eData[  3 ] );
+    subRound( B, C, D, E, A, f1, K1, eData[  4 ] );
+    subRound( A, B, C, D, E, f1, K1, eData[  5 ] );
+    subRound( E, A, B, C, D, f1, K1, eData[  6 ] );
+    subRound( D, E, A, B, C, f1, K1, eData[  7 ] );
+    subRound( C, D, E, A, B, f1, K1, eData[  8 ] );
+    subRound( B, C, D, E, A, f1, K1, eData[  9 ] );
+    subRound( A, B, C, D, E, f1, K1, eData[ 10 ] );
+    subRound( E, A, B, C, D, f1, K1, eData[ 11 ] );
+    subRound( D, E, A, B, C, f1, K1, eData[ 12 ] );
+    subRound( C, D, E, A, B, f1, K1, eData[ 13 ] );
+    subRound( B, C, D, E, A, f1, K1, eData[ 14 ] );
+    subRound( A, B, C, D, E, f1, K1, eData[ 15 ] );
+    subRound( E, A, B, C, D, f1, K1, expand( eData, 16 ) );
+    subRound( D, E, A, B, C, f1, K1, expand( eData, 17 ) );
+    subRound( C, D, E, A, B, f1, K1, expand( eData, 18 ) );
+    subRound( B, C, D, E, A, f1, K1, expand( eData, 19 ) );
+
+    subRound( A, B, C, D, E, f2, K2, expand( eData, 20 ) );
+    subRound( E, A, B, C, D, f2, K2, expand( eData, 21 ) );
+    subRound( D, E, A, B, C, f2, K2, expand( eData, 22 ) );
+    subRound( C, D, E, A, B, f2, K2, expand( eData, 23 ) );
+    subRound( B, C, D, E, A, f2, K2, expand( eData, 24 ) );
+    subRound( A, B, C, D, E, f2, K2, expand( eData, 25 ) );
+    subRound( E, A, B, C, D, f2, K2, expand( eData, 26 ) );
+    subRound( D, E, A, B, C, f2, K2, expand( eData, 27 ) );
+    subRound( C, D, E, A, B, f2, K2, expand( eData, 28 ) );
+    subRound( B, C, D, E, A, f2, K2, expand( eData, 29 ) );
+    subRound( A, B, C, D, E, f2, K2, expand( eData, 30 ) );
+    subRound( E, A, B, C, D, f2, K2, expand( eData, 31 ) );
+    subRound( D, E, A, B, C, f2, K2, expand( eData, 32 ) );
+    subRound( C, D, E, A, B, f2, K2, expand( eData, 33 ) );
+    subRound( B, C, D, E, A, f2, K2, expand( eData, 34 ) );
+    subRound( A, B, C, D, E, f2, K2, expand( eData, 35 ) );
+    subRound( E, A, B, C, D, f2, K2, expand( eData, 36 ) );
+    subRound( D, E, A, B, C, f2, K2, expand( eData, 37 ) );
+    subRound( C, D, E, A, B, f2, K2, expand( eData, 38 ) );
+    subRound( B, C, D, E, A, f2, K2, expand( eData, 39 ) );
+
+    subRound( A, B, C, D, E, f3, K3, expand( eData, 40 ) );
+    subRound( E, A, B, C, D, f3, K3, expand( eData, 41 ) );
+    subRound( D, E, A, B, C, f3, K3, expand( eData, 42 ) );
+    subRound( C, D, E, A, B, f3, K3, expand( eData, 43 ) );
+    subRound( B, C, D, E, A, f3, K3, expand( eData, 44 ) );
+    subRound( A, B, C, D, E, f3, K3, expand( eData, 45 ) );
+    subRound( E, A, B, C, D, f3, K3, expand( eData, 46 ) );
+    subRound( D, E, A, B, C, f3, K3, expand( eData, 47 ) );
+    subRound( C, D, E, A, B, f3, K3, expand( eData, 48 ) );
+    subRound( B, C, D, E, A, f3, K3, expand( eData, 49 ) );
+    subRound( A, B, C, D, E, f3, K3, expand( eData, 50 ) );
+    subRound( E, A, B, C, D, f3, K3, expand( eData, 51 ) );
+    subRound( D, E, A, B, C, f3, K3, expand( eData, 52 ) );
+    subRound( C, D, E, A, B, f3, K3, expand( eData, 53 ) );
+    subRound( B, C, D, E, A, f3, K3, expand( eData, 54 ) );
+    subRound( A, B, C, D, E, f3, K3, expand( eData, 55 ) );
+    subRound( E, A, B, C, D, f3, K3, expand( eData, 56 ) );
+    subRound( D, E, A, B, C, f3, K3, expand( eData, 57 ) );
+    subRound( C, D, E, A, B, f3, K3, expand( eData, 58 ) );
+    subRound( B, C, D, E, A, f3, K3, expand( eData, 59 ) );
+
+    subRound( A, B, C, D, E, f4, K4, expand( eData, 60 ) );
+    subRound( E, A, B, C, D, f4, K4, expand( eData, 61 ) );
+    subRound( D, E, A, B, C, f4, K4, expand( eData, 62 ) );
+    subRound( C, D, E, A, B, f4, K4, expand( eData, 63 ) );
+    subRound( B, C, D, E, A, f4, K4, expand( eData, 64 ) );
+    subRound( A, B, C, D, E, f4, K4, expand( eData, 65 ) );
+    subRound( E, A, B, C, D, f4, K4, expand( eData, 66 ) );
+    subRound( D, E, A, B, C, f4, K4, expand( eData, 67 ) );
+    subRound( C, D, E, A, B, f4, K4, expand( eData, 68 ) );
+    subRound( B, C, D, E, A, f4, K4, expand( eData, 69 ) );
+    subRound( A, B, C, D, E, f4, K4, expand( eData, 70 ) );
+    subRound( E, A, B, C, D, f4, K4, expand( eData, 71 ) );
+    subRound( D, E, A, B, C, f4, K4, expand( eData, 72 ) );
+    subRound( C, D, E, A, B, f4, K4, expand( eData, 73 ) );
+    subRound( B, C, D, E, A, f4, K4, expand( eData, 74 ) );
+    subRound( A, B, C, D, E, f4, K4, expand( eData, 75 ) );
+    subRound( E, A, B, C, D, f4, K4, expand( eData, 76 ) );
+    subRound( D, E, A, B, C, f4, K4, expand( eData, 77 ) );
+    subRound( C, D, E, A, B, f4, K4, expand( eData, 78 ) );
+    subRound( B, C, D, E, A, f4, K4, expand( eData, 79 ) );
+
+    /* Build message digest */
+    digest[ 0 ] += A;
+    digest[ 1 ] += B;
+    digest[ 2 ] += C;
+    digest[ 3 ] += D;
+    digest[ 4 ] += E;
+    }
+
+#else
+#define HASH_BUFFER_SIZE 4
+#define HASH_TRANSFORM MD5Transform
+	
 /*
  * MD5 transform algorithm, taken from code written by Colin Plumb,
  * and put into the public domain
@@ -565,6 +827,8 @@
 #undef F4
 #undef MD5STEP
 
+#endif
+
 
 #if POOLWORDS % 16
 #error extract_entropy() assumes that POOLWORDS is a multiple of 16 words.
@@ -579,7 +843,7 @@
 				  int nbytes, int to_user)
 {
 	int ret, i;
-	__u32 tmp[4];
+	__u32 tmp[HASH_BUFFER_SIZE];
 	
 	add_timer_randomness(r, &extract_timer_state, nbytes);
 	
@@ -602,22 +866,28 @@
 		tmp[1] = 0xefcdab89;
 		tmp[2] = 0x98badcfe;
 		tmp[3] = 0x10325476;
+#ifdef USE_SHA
+		tmp[4] = 0xc3d2e1f0;
+#endif
 		for (i = 0; i < POOLWORDS; i += 16)
-			MD5Transform(tmp, r->pool+i);
+			HASH_TRANSFORM(tmp, r->pool+i);
 		/* Modify pool so next hash will produce different results */
 		add_entropy_word(r, tmp[0]);
 		add_entropy_word(r, tmp[1]);
 		add_entropy_word(r, tmp[2]);
 		add_entropy_word(r, tmp[3]);
+#ifdef USE_SHA
+		add_entropy_word(r, tmp[4]);
+#endif
 		/*
-		 * Run the MD5 Transform one more time, since we want
+		 * Run the hash transform one more time, since we want
 		 * to add at least minimal obscuring of the inputs to
-		 * add_entropy_word().  --- TYT
+		 * add_entropy_word().
 		 */
-		MD5Transform(tmp, r->pool);
+		HASH_TRANSFORM(tmp, r->pool);
 		
 		/* Copy data to destination buffer */
-		i = MIN(nbytes, 16);
+		i = MIN(nbytes, HASH_BUFFER_SIZE*sizeof(__u32));
 		if (to_user)
 			memcpy_tofs(buf, (__u8 const *)tmp, i);
 		else
@@ -682,6 +952,15 @@
 	current->state = TASK_RUNNING;
 	remove_wait_queue(&random_wait, &wait);
 
+	/*
+	 * If we gave the user some bytes and we have an inode pointer,
+	 * update the access time.
+	 */
+	if (inode && count != 0) {
+		inode->i_atime = CURRENT_TIME;
+		inode->i_dirt = 1;
+	}
+	
 	return (count ? count : retval);
 }
 
@@ -696,10 +975,17 @@
 random_select(struct inode *inode, struct file *file,
 		      int sel_type, select_table * wait)
 {
-	if (sel_type == SEL_IN) {
+	switch (sel_type) {
+	case SEL_IN:
 		if (random_state.entropy_count >= 8)
 			return 1;
 		select_wait(&random_wait, wait);
+		break;
+	case SEL_OUT:
+		if (random_state.entropy_count < WAIT_OUTPUT_BITS)
+			return 1;
+		select_wait(&random_wait, wait);
+		break;
 	}
 	return 0;
 }
@@ -711,6 +997,13 @@
 	int i;
 	__u32 word, *p;
 
+	if (count < 0)
+		return -EINVAL;
+
+	i = verify_area(VERIFY_READ, (void *) buffer, count);
+	if (i)
+		return i;
+
 	for (i = count, p = (__u32 *)buffer;
 	     i >= sizeof(__u32);
 	     i-= sizeof(__u32), p++) {
@@ -722,8 +1015,10 @@
 		memcpy_fromfs(&word, p, i);
 		add_entropy_word(&random_state, word);
 	}
-	if (inode)
+	if (inode) {
 		inode->i_mtime = CURRENT_TIME;
+		inode->i_dirt = 1;
+	}
 	return count;
 }
 
@@ -739,7 +1034,8 @@
 		retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
 		if (retval)
 			return(retval);
-		put_user(random_state.entropy_count, (int *) arg);
+		ent_count = random_state.entropy_count;
+		put_user(ent_count, (int *) arg);
 		return 0;
 	case RNDADDTOENTCNT:
 		if (!suser())
@@ -747,9 +1043,28 @@
 		retval = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
 		if (retval)
 			return(retval);
-		random_state.entropy_count += get_user((int *) arg);
-		if (random_state.entropy_count > POOLBITS)
+		ent_count = get_user((int *) arg);
+		/*
+		 * Add i to entropy_count, limiting the result to be
+		 * between 0 and POOLBITS.
+		 */
+		if (ent_count < -random_state.entropy_count)
+			random_state.entropy_count = 0;
+		else if (ent_count > POOLBITS)
 			random_state.entropy_count = POOLBITS;
+		else {
+			random_state.entropy_count += ent_count;
+			if (random_state.entropy_count > POOLBITS)
+				random_state.entropy_count = POOLBITS;
+			if (random_state.entropy_count < 0)
+				random_state.entropy_count = 0;
+		}
+		/*
+		 * Wake up waiting processes if we have enough
+		 * entropy.
+		 */
+		if (random_state.entropy_count >= WAIT_INPUT_BITS)
+			wake_up_interruptible(&random_wait);
 		return 0;
 	case RNDGETPOOL:
 		if (!suser())
@@ -758,18 +1073,22 @@
 		retval = verify_area(VERIFY_WRITE, (void *) p, sizeof(int));
 		if (retval)
 			return(retval);
-		put_user(random_state.entropy_count, p++);
-		retval = verify_area(VERIFY_READ, (void *) p, sizeof(int));
+		ent_count = random_state.entropy_count;
+		put_user(ent_count, p++);
+		retval = verify_area(VERIFY_WRITE, (void *) p, sizeof(int));
 		if (retval)
 			return(retval);
 		size = get_user(p);
-		put_user(POOLWORDS, p);
+		put_user(POOLWORDS, p++);
 		if (size < 0)
 			return -EINVAL;
 		if (size > POOLWORDS)
 			size = POOLWORDS;
-		memcpy_tofs(++p, random_state.pool,
-			    size*sizeof(__u32));
+		retval = verify_area(VERIFY_WRITE, (void *) p,
+				     size * sizeof(__u32));
+		if (retval)
+			return retval;
+		memcpy_tofs(p, random_state.pool, size*sizeof(__u32));
 		return 0;
 	case RNDADDENTROPY:
 		if (!suser())
@@ -779,16 +1098,42 @@
 		if (retval)
 			return(retval);
 		ent_count = get_user(p++);
+		if (ent_count < 0)
+			return -EINVAL;
 		size = get_user(p++);
-		(void) random_write(0, file, (const char *) p, size);
-		random_state.entropy_count += ent_count;
-		if (random_state.entropy_count > POOLBITS)
+		retval = random_write(0, file, (const char *) p, size);
+		if (retval)
+			return retval;
+		/*
+		 * Add ent_count to entropy_count, limiting the result to be
+		 * between 0 and POOLBITS.
+		 */
+		if (ent_count > POOLBITS)
 			random_state.entropy_count = POOLBITS;
+		else {
+			random_state.entropy_count += ent_count;
+			if (random_state.entropy_count > POOLBITS)
+				random_state.entropy_count = POOLBITS;
+			if (random_state.entropy_count < 0)
+				random_state.entropy_count = 0;
+		}
+		/*
+		 * Wake up waiting processes if we have enough
+		 * entropy.
+		 */
+		if (random_state.entropy_count >= WAIT_INPUT_BITS)
+			wake_up_interruptible(&random_wait);
 		return 0;
 	case RNDZAPENTCNT:
 		if (!suser())
 			return -EPERM;
 		random_state.entropy_count = 0;
+		return 0;
+	case RNDCLEARPOOL:
+		/* Clear the entropy pool and associated counters. */
+		if (!suser())
+			return -EPERM;
+		rand_clear_pool();
 		return 0;
 	default:
 		return -EINVAL;
diff -u --recursive --new-file v1.3.97/linux/drivers/char/rtc.c linux/drivers/char/rtc.c
--- v1.3.97/linux/drivers/char/rtc.c	Tue Apr 23 13:57:09 1996
+++ linux/drivers/char/rtc.c	Fri May  3 11:09:58 1996
@@ -30,7 +30,7 @@
  *
  */
 
-#define RTC_VERSION		"1.04"
+#define RTC_VERSION		"1.05"
 
 #define RTC_IRQ 	8	/* Can't see this changing soon.	*/
 #define RTC_IO_BASE	0x70	/* Or this...				*/
@@ -67,6 +67,8 @@
 
 static struct wait_queue *rtc_wait;
 
+static struct timer_list rtc_irq_timer;
+
 static int rtc_lseek(struct inode *inode, struct file *file, off_t offset,
 			int origin);
 
@@ -81,6 +83,7 @@
 
 void get_rtc_time (struct tm *rtc_tm);
 void get_rtc_alm_time (struct tm *alm_tm);
+void rtc_dropped_irq(unsigned long data);
 
 inline void set_rtc_irq_bit(unsigned char bit);
 inline void mask_rtc_irq_bit(unsigned char bit);
@@ -92,8 +95,10 @@
  */
 
 #define RTC_IS_OPEN		0x01	/* means /dev/rtc is in use	*/
+#define RTC_TIMER_ON		0x02	/* missed irq timer active	*/
 
 unsigned char rtc_status = 0;		/* bitmapped status byte.	*/
+unsigned long rtc_freq = 0;		/* Current periodic IRQ rate	*/
 unsigned long rtc_irq_data = 0;		/* our output to the world	*/
 
 unsigned char days_in_mo[] = 
@@ -119,6 +124,12 @@
 	rtc_irq_data &= ~0xff;
 	rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
 	wake_up_interruptible(&rtc_wait);	
+
+	if (rtc_status & RTC_TIMER_ON) {
+		del_timer(&rtc_irq_timer);
+		rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
+		add_timer(&rtc_irq_timer);
+	}
 }
 
 /*
@@ -157,12 +168,16 @@
 			break;
 		}
 		schedule();
-		continue;
 	}
 
 	if (retval == 0) {
-		memcpy_tofs(buf, &rtc_irq_data, sizeof(unsigned long));
+		unsigned long data, flags;
+		save_flags(flags);
+		cli();
+		data = rtc_irq_data;
 		rtc_irq_data = 0;
+		restore_flags(flags);
+		memcpy_tofs(buf, &data, sizeof(unsigned long));
 		retval = sizeof(unsigned long);
 	}
 
@@ -192,27 +207,30 @@
 		case RTC_PIE_OFF:	/* Mask periodic int. enab. bit	*/
 		{
 			mask_rtc_irq_bit(RTC_PIE);
+			if (rtc_status & RTC_TIMER_ON) {
+				del_timer(&rtc_irq_timer);
+				rtc_status &= ~RTC_TIMER_ON;
+			}
 			return 0;
 		}
 		case RTC_PIE_ON:	/* Allow periodic ints		*/
 		{
-			unsigned int hz;
-			unsigned char tmp;
-
-			save_flags(flags);
-			cli();
-			tmp = CMOS_READ(RTC_FREQ_SELECT) & 0x0f;
-			restore_flags(flags);
-
-			hz = (tmp ? (65536/(1<<tmp)) : 0);
 
 			/*
 			 * We don't really want Joe User enabling more
 			 * than 64Hz of interrupts on a multi-user machine.
 			 */
-			if ((hz > 64) && (!suser()))
+			if ((rtc_freq > 64) && (!suser()))
 				return -EPERM;
 
+			if (rtc_freq == 0)
+				return -EINVAL;
+
+			if (!(rtc_status & RTC_TIMER_ON)) {
+				rtc_status |= RTC_TIMER_ON;
+				rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
+				add_timer(&rtc_irq_timer);
+			}
 			set_rtc_irq_bit(RTC_PIE);
 			return 0;
 		}
@@ -382,19 +400,13 @@
 		}
 		case RTC_IRQP_READ:	/* Read the periodic IRQ rate.	*/
 		{
-			unsigned long hz;
 			int retval;
 
 			retval = verify_area(VERIFY_WRITE, (unsigned long*)arg, sizeof(unsigned long));
 			if (retval != 0)
 				return retval;
 
-			save_flags(flags);
-			cli();
-			retval = CMOS_READ(RTC_FREQ_SELECT) & 0x0f;
-			restore_flags(flags);
-			hz = (retval ? (65536/(1<<retval)) : 0);
-			memcpy_tofs((unsigned long*)arg, &hz, sizeof(unsigned long));
+			memcpy_tofs((unsigned long*)arg, &rtc_freq, sizeof(unsigned long));
 			return 0;
 		}
 		case RTC_IRQP_SET:	/* Set periodic IRQ rate.	*/
@@ -405,7 +417,7 @@
 			/* 
 			 * The max we can do is 8192Hz.
 			 */
-			if (arg > 8192)
+			if ((arg < 2) || (arg > 8192))
 				return -EINVAL;
 			/*
 			 * We don't really want Joe User generating more
@@ -423,6 +435,8 @@
 			if ((arg != 0) && (arg != (1<<tmp)))
 				return -EINVAL;
 
+			rtc_freq = arg;
+
 			save_flags(flags);
 			cli();
 			val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
@@ -480,6 +494,12 @@
 	CMOS_WRITE(tmp, RTC_CONTROL);
 	CMOS_READ(RTC_INTR_FLAGS);
 	restore_flags(flags);
+
+	if (rtc_status & RTC_TIMER_ON) {
+		rtc_status &= ~RTC_TIMER_ON;
+		del_timer(&rtc_irq_timer);
+	}
+
 	rtc_irq_data = 0;
 	rtc_status &= ~RTC_IS_OPEN;
 }
@@ -520,6 +540,8 @@
 
 int rtc_init(void)
 {
+	unsigned long flags;
+
 	printk("Real Time Clock Driver v%s\n", RTC_VERSION);
 	if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL))
 	{
@@ -530,11 +552,47 @@
 	misc_register(&rtc_dev);
 	/* Check region? Naaah! Just snarf it up. */
 	request_region(RTC_IO_BASE, RTC_IO_EXTENT, "rtc");
+	init_timer(&rtc_irq_timer);
+	rtc_irq_timer.function = rtc_dropped_irq;
 	rtc_wait = NULL;
+	save_flags(flags);
+	cli();
+	rtc_freq = CMOS_READ(RTC_FREQ_SELECT) & 0x0F;
+	restore_flags(flags);
+	rtc_freq = (rtc_freq ? (65536/(1<<rtc_freq)) : 0);
 	return 0;
 }
 
 /*
+ * 	At IRQ rates >= 4096Hz, an interrupt may get lost altogether.
+ *	(usually during an IDE disk interrupt, with IRQ unmasking off)
+ *	Since the interrupt handler doesn't get called, the IRQ status
+ *	byte doesn't get read, and the RTC stops generating interrupts.
+ *	A timer is set, and will call this function if/when that happens.
+ *	To get it out of this stalled state, we just read the status.
+ *	At least a jiffy of interrupts (rtc_freq/HZ) will have been lost.
+ *	(You *really* shouldn't be trying to use a non-realtime system 
+ *	for something that requires a steady > 1KHz signal anyways.)
+ */
+
+void rtc_dropped_irq(unsigned long data)
+{
+	unsigned long flags;
+
+	printk(KERN_INFO "rtc: lost some interrupts at %ldHz.\n", rtc_freq);
+	del_timer(&rtc_irq_timer);
+	rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
+	add_timer(&rtc_irq_timer);
+
+	save_flags(flags);
+	cli();
+	rtc_irq_data += ((rtc_freq/HZ)<<8);
+	rtc_irq_data &= ~0xff;
+	rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);	/* restart */
+	restore_flags(flags);
+}
+
+/*
  *	Info exported via "/proc/rtc".
  */
 
@@ -542,11 +600,11 @@
 {
 	char *p;
 	struct tm tm;
-	unsigned char freq, batt, ctrl;
+	unsigned char batt, ctrl;
 	unsigned long flags;
 
 	save_flags(flags);
-	freq = CMOS_READ(RTC_FREQ_SELECT) & 0x0F;
+	cli();
 	batt = CMOS_READ(RTC_VALID) & RTC_VRT;
 	ctrl = CMOS_READ(RTC_CONTROL);
 	restore_flags(flags);
@@ -559,10 +617,11 @@
 	 * There is no way to tell if the luser has the RTC set for local
 	 * time or for Universal Standard Time (GMT). Probably local though.
 	 */
-	p += sprintf(p, "date          : %04d-%02d-%02d\n",
+	p += sprintf(p,
+		"rtc_time\t: %02d:%02d:%02d\n"
+		"rtc_date\t: %04d-%02d-%02d\n",
+		tm.tm_hour, tm.tm_min, tm.tm_sec,
 		tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
-	p += sprintf(p, "time          : %02d-%02d-%02d\n",
-		tm.tm_hour, tm.tm_min, tm.tm_sec);
 
 	get_rtc_alm_time(&tm);
 
@@ -571,44 +630,41 @@
 	 * match any value for that particular field. Values that are
 	 * greater than a valid time, but less than 0xc0 shouldn't appear.
 	 */
-	p += sprintf(p, "alarm         : ");
+	p += sprintf(p, "alarm\t\t: ");
 	if (tm.tm_hour <= 24)
-		p += sprintf(p, "%02d", tm.tm_hour);
+		p += sprintf(p, "%02d:", tm.tm_hour);
 	else
-		p += sprintf(p, "**");
-	p += sprintf(p, "-");
+		p += sprintf(p, "**:");
+
 	if (tm.tm_min <= 59)
-		p += sprintf(p, "%02d", tm.tm_min);
+		p += sprintf(p, "%02d:", tm.tm_min);
 	else
-		p += sprintf(p, "**");
-	p += sprintf(p, "-");
+		p += sprintf(p, "**:");
+
 	if (tm.tm_sec <= 59)
-		p += sprintf(p, "%02d", tm.tm_sec);
+		p += sprintf(p, "%02d\n", tm.tm_sec);
 	else
-		p += sprintf(p, "**");
-	p += sprintf(p, "\n");
+		p += sprintf(p, "**\n");
 
-	p += sprintf(p, "daylight      : %s\n",
-		((ctrl & RTC_DST_EN) ? "yes" : "no" ));
-	p += sprintf(p, "bcd           : %s\n",
-		((ctrl & RTC_DM_BINARY) ? "no" : "yes" ));
-	p += sprintf(p, "24hr          : %s\n",
-		((ctrl & RTC_24H) ? "yes" : "no" ));
-	p += sprintf(p, "sqwave        : %s\n",
-		((ctrl & RTC_SQWE) ? "yes" : "no" ));
-
-	p += sprintf(p, "alarm_int     : %s\n",
-		((ctrl & RTC_AIE) ? "yes" : "no" ));
-	p += sprintf(p, "update_int    : %s\n",
-		((ctrl & RTC_UIE) ? "yes" : "no" ));
-	p += sprintf(p, "periodic_int  : %s\n",
-		((ctrl & RTC_PIE) ? "yes" : "no" ));
-
-	p += sprintf(p, "periodic_freq : %d\n",
-		(freq ? (65536/(1<<freq)) : 0));
-
-	p += sprintf(p, "battery_ok    : %s\n",
-		(batt ? "yes" : "no"));
+	p += sprintf(p,
+		"DST_enable\t: %s\n"
+		"BCD\t\t: %s\n"
+		"24hr\t\t: %s\n"
+		"sqare_wave\t: %s\n"
+		"alarm_IRQ\t: %s\n"
+		"update_IRQ\t: %s\n"
+		"periodic_IRQ\t: %s\n"
+		"periodic_freq\t: %ld\n"
+		"batt_status\t: %s\n",
+		(ctrl & RTC_DST_EN) ? "yes" : "no",
+		(ctrl & RTC_DM_BINARY) ? "no" : "yes",
+		(ctrl & RTC_24H) ? "yes" : "no",
+		(ctrl & RTC_SQWE) ? "yes" : "no",
+		(ctrl & RTC_AIE) ? "yes" : "no",
+		(ctrl & RTC_UIE) ? "yes" : "no",
+		(ctrl & RTC_PIE) ? "yes" : "no",
+		rtc_freq,
+		batt ? "okay" : "dead");
 
 	return  p - buf;
 }
@@ -622,6 +678,7 @@
 	unsigned char uip;
 
 	save_flags(flags);
+	cli();
 	uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
 	restore_flags(flags);
 	return uip;
@@ -744,7 +801,7 @@
 	val |= bit;
 	CMOS_WRITE(val, RTC_CONTROL);
 	CMOS_READ(RTC_INTR_FLAGS);
-	restore_flags(flags);
 	rtc_irq_data = 0;
+	restore_flags(flags);
 }
 
diff -u --recursive --new-file v1.3.97/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v1.3.97/linux/drivers/char/serial.c	Mon Apr 29 18:05:17 1996
+++ linux/drivers/char/serial.c	Tue Apr 30 12:42:36 1996
@@ -1610,7 +1610,7 @@
  * 	    release the bus after transmitting. This must be done when
  * 	    the transmit shift register is empty, not be done when the
  * 	    transmit holding register is empty.  This functionality
- * 	    allows RS485 driver to be written in user space. 
+ * 	    allows an RS485 driver to be written in user space. 
  */
 static int get_lsr_info(struct async_struct * info, unsigned int *value)
 {
diff -u --recursive --new-file v1.3.97/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v1.3.97/linux/drivers/net/Config.in	Sat Apr 27 15:19:53 1996
+++ linux/drivers/net/Config.in	Tue Apr 30 13:31:04 1996
@@ -2,98 +2,114 @@
 # Network device configuration
 #
 tristate 'Dummy net driver support' CONFIG_DUMMY
-tristate 'SLIP (serial line) support' CONFIG_SLIP
-if [ "$CONFIG_SLIP" != "n" ]; then
-  bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED
-  bool ' Keepalive and linefill' CONFIG_SLIP_SMART
+tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Frame relay DLCI support (EXPERIMENTAL)' CONFIG_DLCI
+  if [ "$CONFIG_DLCI" = "y" -o "$CONFIG_DLCI" = "m" ]; then
+    int '  Max open DLCI' CONFIG_DLCI_COUNT 24
+    int '  Max DLCI per device' CONFIG_DLCI_MAX 8
+    dep_tristate '  SDLA (Sangoma S502/S508) support' CONFIG_SDLA $CONFIG_DLCI
+  fi
 fi
+tristate 'PLIP (parallel port) support' CONFIG_PLIP
 tristate 'PPP (point-to-point) support' CONFIG_PPP
 if [ ! "$CONFIG_PPP" = "n" ]; then
   comment 'CCP compressors for PPP are only built as modules.'
 fi
-tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
-tristate 'WIC (Radio IP bridge)' CONFIG_WIC
-tristate 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC
-tristate 'PLIP (parallel port) support' CONFIG_PLIP
-tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
-
-tristate 'DLCI (Frame relay) support' CONFIG_DLCI
-if [ "$CONFIG_DLCI" = "y" -o "$CONFIG_DLCI" = "m" ]; then
-  int '  Max open DLCI' CONFIG_DLCI_COUNT 24
-  int '  Max DLCI per device' CONFIG_DLCI_MAX 8
-  tristate '  SDLA (Sangoma S502/S508) support' CONFIG_SDLA
-fi
-
-bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA
-bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC
-if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
-  tristate 'WD80*3 support' CONFIG_WD80x3
-  tristate 'SMC Ultra support' CONFIG_ULTRA
-  tristate 'SMC 9194 support' CONFIG_SMC9194
-fi
-bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE
-if [ "$CONFIG_LANCE" = "y" ]; then
-  bool 'AMD PCInet32 (VLB and PCI) support' CONFIG_LANCE32
-fi  
-bool '3COM cards' CONFIG_NET_VENDOR_3COM
-if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
-  tristate '3c501 support' CONFIG_EL1
-  tristate '3c503 support' CONFIG_EL2
-  if [ "$CONFIG_NET_ALPHA" = "y" ]; then
-    tristate '3c505 support' CONFIG_ELPLUS
-    tristate '3c507 support' CONFIG_EL16
-  fi
-  tristate '3c509/3c579 support' CONFIG_EL3
-  tristate '3c590 series (592/595/597) "Vortex" support' CONFIG_VORTEX
+tristate 'SLIP (serial line) support' CONFIG_SLIP
+if [ "$CONFIG_SLIP" != "n" ]; then
+  bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED
+  bool ' Keepalive and linefill' CONFIG_SLIP_SMART
 fi
-bool 'Other ISA cards' CONFIG_NET_ISA
-if [ "$CONFIG_NET_ISA" = "y" ]; then
-  tristate 'Cabletron E21xx support' CONFIG_E2100
-  tristate 'DEPCA support' CONFIG_DEPCA
-  tristate 'EtherWorks 3 support' CONFIG_EWRK3
-  tristate 'EtherExpress 16 support' CONFIG_EEXPRESS
-  if [ "$CONFIG_NET_ALPHA" = "y" ]; then
-    bool 'SEEQ8005 support' CONFIG_SEEQ8005
-    tristate 'AT1700 support' CONFIG_AT1700
-    tristate 'FMV-181/182 support' CONFIG_FMV18X
-    tristate 'EtherExpressPro support' CONFIG_EEXPRESS_PRO
-    tristate 'NI5210 support' CONFIG_NI52
-    bool 'NI6510 support' CONFIG_NI65
-    tristate 'WaveLAN support' CONFIG_WAVELAN
-    tristate 'ICL EtherTeam 16i/32 support' CONFIG_ETH16I
-  fi
-  tristate 'HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS
-  tristate 'HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN
-  tristate 'HP 10/100VG PCLAN (ISA, EISA, PCI) support' CONFIG_HP100
-  tristate 'NE2000/NE1000 support' CONFIG_NE2000
+bool 'Radio network interfaces' CONFIG_NET_RADIO
+if [ "$CONFIG_NET_RADIO" != "n" ]; then
   if [ "$CONFIG_AX25" = "y" ]; then
-    bool 'Ottawa PI and PI/2 support' CONFIG_PI
     bool 'Gracilis PackeTwin support' CONFIG_PT
+    bool 'Ottawa PI and PI/2 support' CONFIG_PI
+  fi
+  tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
+  tristate 'WaveLAN support' CONFIG_WAVELAN
+  tristate 'WIC Radio IP bridge (EXPERIMENTAL)' CONFIG_WIC
+  if [ "$CONFIG_AX25" = "y" ]; then
+    tristate 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC
   fi
-  bool 'SK_G16 support' CONFIG_SK_G16
-fi
-bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA
-if [ "$CONFIG_NET_EISA" = "y" ]; then
-  if [ "$CONFIG_NET_ALPHA" = "y" ]; then
-    tristate 'Ansel Communications EISA 3200 support' CONFIG_AC3200
-  fi
-  tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT
-  tristate 'DE425, DE434, DE435, DE500 support' CONFIG_DE4X5
-  tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP
-  tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
-# bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100
-# bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32
-  bool 'Zenith Z-Note support' CONFIG_ZNET
 fi
-bool 'Pocket and portable adaptors' CONFIG_NET_POCKET
-if [ "$CONFIG_NET_POCKET" = "y" ]; then
-  bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP
-  tristate 'D-Link DE600 pocket adaptor support' CONFIG_DE600
-  tristate 'D-Link DE620 pocket adaptor support' CONFIG_DE620
-# bool 'Silicom pocket adaptor support' CONFIG_SILICOM_PEA
-# bool 'WaveLAN PCMCIA support' CONFIG_WaveLAN
-# bool '3 Com 3c589 PCMCIA support' CONFIG_3C589
+#
+#	Ethernet
+#
+bool 'Ethernet (10 or 100Mbit)' CONFIG_NET_ETHERNET
+if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
+  bool '3COM cards' CONFIG_NET_VENDOR_3COM
+  if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
+    tristate '3c501 support' CONFIG_EL1
+    tristate '3c503 support' CONFIG_EL2
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      tristate '3c505 support' CONFIG_ELPLUS
+      tristate '3c507 support' CONFIG_EL16
+    fi
+    tristate '3c509/3c579 support' CONFIG_EL3
+    tristate '3c590 series (592/595/597) "Vortex" support' CONFIG_VORTEX
+  fi
+  bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE
+  if [ "$CONFIG_LANCE" = "y" ]; then
+    bool 'AMD PCInet32 (VLB and PCI) support' CONFIG_LANCE32
+  fi  
+  bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC
+  if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
+    tristate 'WD80*3 support' CONFIG_WD80x3
+    tristate 'SMC Ultra support' CONFIG_ULTRA
+    tristate 'SMC 9194 support' CONFIG_SMC9194
+  fi
+  bool 'Other ISA cards' CONFIG_NET_ISA
+  if [ "$CONFIG_NET_ISA" = "y" ]; then
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      tristate 'AT1700 support (EXPERIMENTAL)' CONFIG_AT1700
+    fi
+    tristate 'Cabletron E21xx support' CONFIG_E2100
+    tristate 'DEPCA support' CONFIG_DEPCA
+    tristate 'EtherWorks 3 support' CONFIG_EWRK3
+    tristate 'EtherExpress 16 support' CONFIG_EEXPRESS
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      tristate 'EtherExpressPro support' CONFIG_EEXPRESS_PRO
+      tristate 'FMV-181/182 support' CONFIG_FMV18X
+    fi
+    tristate 'HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS
+    tristate 'HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN
+    tristate 'HP 10/100VG PCLAN (ISA, EISA, PCI) support' CONFIG_HP100
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      tristate 'ICL EtherTeam 16i/32 support' CONFIG_ETH16I
+    fi
+    tristate 'NE2000/NE1000 support' CONFIG_NE2000
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      tristate 'NI5210 support' CONFIG_NI52
+      bool 'NI6510 support' CONFIG_NI65
+    fi
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      bool 'SEEQ8005 support (EXPERIMENTAL)' CONFIG_SEEQ8005
+    fi
+    bool 'SK_G16 support' CONFIG_SK_G16
+  fi
+  bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA
+  if [ "$CONFIG_NET_EISA" = "y" ]; then
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      tristate 'Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200
+    fi
+    tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT
+    tristate 'DE425, DE434, DE435, DE500 support' CONFIG_DE4X5
+    tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP
+    tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      bool 'Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET
+    fi
+  fi
+  bool 'Pocket and portable adaptors' CONFIG_NET_POCKET
+  if [ "$CONFIG_NET_POCKET" = "y" ]; then
+    bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP
+    tristate 'D-Link DE600 pocket adaptor support' CONFIG_DE600
+    tristate 'D-Link DE620 pocket adaptor support' CONFIG_DE620
+  fi
 fi
+
 bool 'Token Ring driver support' CONFIG_TR
 if [ "$CONFIG_TR" = "y" ]; then
   tristate 'IBM Tropic chipset based adaptor support' CONFIG_IBMTR
diff -u --recursive --new-file v1.3.97/linux/drivers/net/new_tunnel.c linux/drivers/net/new_tunnel.c
--- v1.3.97/linux/drivers/net/new_tunnel.c	Mon Mar 25 08:58:21 1996
+++ linux/drivers/net/new_tunnel.c	Tue Apr 30 12:42:36 1996
@@ -266,7 +266,7 @@
 		new_skb->h.iph = (struct iphdr *) skb_push(new_skb, tunnel_hlen);
 
 		/* Free the old packet, we no longer need it */
-		kfree_skb(skb, FREE_WRITE);
+		dev_kfree_skb(skb, FREE_WRITE);
 		skb = new_skb;
 	}
 
diff -u --recursive --new-file v1.3.97/linux/drivers/sbus/char/sunkbd.c linux/drivers/sbus/char/sunkbd.c
--- v1.3.97/linux/drivers/sbus/char/sunkbd.c	Sat Apr 27 15:19:53 1996
+++ linux/drivers/sbus/char/sunkbd.c	Sat May  4 19:39:23 1996
@@ -653,7 +653,7 @@
 
 	if (!obp_system_intr())
 		ctrl_alt_del();
-	/* sigh.. atempt to prevent multiple entry */
+	/* sigh.. attempt to prevent multiple entry */
 	last_keycode=1;
 	rep = 0;
 }
diff -u --recursive --new-file v1.3.97/linux/drivers/sbus/char/sunserial.c linux/drivers/sbus/char/sunserial.c
--- v1.3.97/linux/drivers/sbus/char/sunserial.c	Sat Apr 27 15:19:56 1996
+++ linux/drivers/sbus/char/sunserial.c	Sat May  4 19:39:23 1996
@@ -154,7 +154,8 @@
 	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
 	9600, 19200, 38400, 57600, 115200, 0 };
 
-/* Reading and writing Zilog8530 registers.  The delays are to make this
+/* 
+ * Reading and writing Zilog8530 registers.  The delays are to make this
  * driver work on the Sun4 which needs a settling delay after each chip
  * register access, other machines handle this in hardware via auxiliary
  * flip-flops which implement the settle time we do in software.
@@ -674,10 +675,9 @@
 /*
  * This subroutine is called when the RS_TIMER goes off.  It is used
  * by the serial driver to handle ports that do not have an interrupt
- * (irq=0).  This doesn't work very well for 16450's, but gives barely
- * passable results for a 16550A.  (Although at the expense of much
- * CPU overhead).
+ * (irq=0).  This doesn't work at all for 16450's, as a sun has a Z8530.
  */
+ 
 static void rs_timer(void)
 {
 	printk("rs_timer called\n");
@@ -1009,7 +1009,7 @@
 		left = MIN(info->xmit_cnt, left-1);
 	}
 
-	/* Last character is being transmitted now (hopefuly). */
+	/* Last character is being transmitted now (hopefully). */
 	zs_conschan->control = RES_Tx_P;
 	udelay(5);
 
@@ -1302,7 +1302,7 @@
  * 	    release the bus after transmitting. This must be done when
  * 	    the transmit shift register is empty, not be done when the
  * 	    transmit holding register is empty.  This functionality
- * 	    allows RS485 driver to be written in user space. 
+ * 	    allows an RS485 driver to be written in user space. 
  */
 static int get_lsr_info(struct sun_serial * info, unsigned int *value)
 {
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/53c7,8xx.h linux/drivers/scsi/53c7,8xx.h
--- v1.3.97/linux/drivers/scsi/53c7,8xx.h	Fri Apr 12 15:51:58 1996
+++ linux/drivers/scsi/53c7,8xx.h	Thu May  2 07:48:53 1996
@@ -54,7 +54,7 @@
  */
 
 #if defined(HOSTS_C) || defined(MODULE)
-#include <linux/scsicam.h>
+#include <scsi/scsicam.h>
 
 extern int NCR53c7xx_abort(Scsi_Cmnd *);
 extern int NCR53c7xx_detect(Scsi_Host_Template *tpnt);
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/AM53C974.h linux/drivers/scsi/AM53C974.h
--- v1.3.97/linux/drivers/scsi/AM53C974.h	Fri Apr 12 15:51:58 1996
+++ linux/drivers/scsi/AM53C974.h	Thu May  2 07:48:53 1996
@@ -27,7 +27,7 @@
 #ifndef AM53C974_H
 #define AM53C974_H
 
-#include <linux/scsicam.h>
+#include <scsi/scsicam.h>
 
 /***************************************************************************************
 * Default setting of the controller's SCSI id. Edit and uncomment this only if your    *
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/README.st linux/drivers/scsi/README.st
--- v1.3.97/linux/drivers/scsi/README.st	Tue Apr 23 13:57:10 1996
+++ linux/drivers/scsi/README.st	Fri May  3 10:12:17 1996
@@ -1,5 +1,5 @@
 This file contains brief information about the SCSI tape driver.
-Last modified: Sun Apr 21 21:19:22 1996 by root@kai.makisara.fi
+Last modified: Wed May  1 11:51:35 1996 by root@kai.makisara.fi
 
 
 BASICS
@@ -62,6 +62,11 @@
 limits). Both the auto-rewind (minor equals device number) and
 non-rewind devices (minor is 128 + device number) are implemented.
 
+Support is provided for changing the tape partition and partitioning
+of the tape with one or two partitions. By default support for
+partitioned tape is disabled for each driver and it can be enabled
+with the ioctl MTSETDRVBUFFER.
+
 By default the driver writes one filemark when the device is closed after
 writing and the last operation has been a write. Two filemarks can be
 optionally written. In both cases end of data is signified by
@@ -181,7 +186,20 @@
 	SCSI mode page 15. Note that some drives other methods for
 	control of compression. Some drives (like the Exabytes) use
 	density codes for compression control. Some drives use another
-	mode page but this page has not been implemented in the driver.
+	mode page but this page has not been implemented in the
+	driver.
+MTSETPART Moves the tape to the partition given by the argument at the
+	next tape operation. The block at which the tape is positioned
+	is the block where the tape was previously positioned in the
+	new active partition unless the next tape operation is
+	MTSEEK. In this case the tape is moved directly to the block
+	specified by MTSEEK. MTSETPART is inactive unless
+	MT_ST_CAN_PARTITIONS set.
+MTMKPART Formats the tape with one partition (argument zero) or two
+	partitions (the argument gives in megabytes the size of
+	partition 1 that is physically the first partition of the
+	tape). The drive has to support partitions with size specified
+	by the initiator. Inactive unless MT_ST_CAN_PARTITIONS set.
 MTSETDRVBUFFER
 	Is used for several purposes. The command is obtained from count
         with mask MT_SET_OPTIONS, the low order bits are used as argument.
@@ -202,11 +220,18 @@
 	     MT_ST_FAST_EOM using the SCSI spacing to EOD (global)
 	     MT_ST_AUTO_LOCK automatic locking of the drive door (global)
              MT_ST_DEF_WRITES the defaults are meant only for writes (mode)
-	     MT_ST_CAN_BSR backspacing over records can be used for
-		repositioning the tape (global)
+	     MT_ST_CAN_BSR backspacing over more than one records can
+		be used for repositioning the tape (global)
 	     MT_ST_NO_BLKLIMS the driver does not ask the block limits
 		from the drive (block size can be changed only to
 		variable) (global)
+	     MT_ST_CAN_PARTITIONS enables support for partitioned
+		tapes (global)
+	     MT_ST_SCSI2LOGICAL the logical block number is used in
+		the MTSEEK and MTIOCPOS for SCSI-2 drives instead of
+		the device dependent address. It is recommended to set
+		this flag unless there are tapes using the device
+		dependent (from the old times) (global)
 	     MT_ST_DEBUGGING debugging (global; debugging must be
 		compiled into the driver)
 	MT_ST_SETBOOLEANS
@@ -246,6 +271,15 @@
 	is set if there is no tape in the drive. GMT_EOD means either
 	end of recorded data or end of tape. GMT_EOT means end of tape.
 
+The following ioctls use the structure mtlocation that contains both
+the block number and the partition number. These ioctls are available
+only for SCSI-2 tape drives and the block number is the
+device-indepent logical block number defined by the standard.
+
+MTGETLOC Returns the current block and partition number.
+MTSETLOC Sets the tape to the block and partition specified by the
+	arguments.
+
 
 MISCELLANEOUS COMPILE OPTIONS
 
@@ -257,7 +291,11 @@
 maximum is adjusted accordingly.
 
 Immediate return from tape positioning SCSI commands can be enabled by
-defining ST_NOWAIT.
+defining ST_NOWAIT. If this is defined, the user should take care that
+the next tape operation is not started before the previous one has
+finished. The drives and SCSI adapters should handle this condition
+gracefully, but some drive/adapter combinations are known to hang the
+SCSI bus in this case.
 
 The MTEOM command is by default implemented as spacing over 32767
 filemarks. With this method the file number in the status is
@@ -268,8 +306,9 @@
 When using read ahead or buffered writes the position within the file
 may not be correct after the file is closed (correct position may
 require backspacing over more than one record). The correct position
-within file can be obtained if ST_IN_FILE_POS is defined. (The
-driver always backs over a filemark crossed by read ahead if the user
-does not request data that far.)
+within file can be obtained if ST_IN_FILE_POS is defined at compile
+time or the MT_ST_CAN_BSR bit is set for the drive with an ioctl.
+(The driver always backs over a filemark crossed by read ahead if the
+user does not request data that far.)
 
 Kai M{kisara
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/aha152x.c linux/drivers/scsi/aha152x.c
--- v1.3.97/linux/drivers/scsi/aha152x.c	Fri Mar  1 07:50:51 1996
+++ linux/drivers/scsi/aha152x.c	Thu May  2 07:27:10 1996
@@ -1,6 +1,6 @@
 /* aha152x.c -- Adaptec AHA-152x driver
  * Author: Juergen E. Fischer, fischer@et-inf.fho-emden.de
- * Copyright 1993, 1994, 1995 Juergen E. Fischer
+ * Copyright 1993, 1994, 1995, 1996 Juergen E. Fischer
  *
  *
  * This driver is based on
@@ -20,9 +20,13 @@
  * General Public License for more details.
  *
  *
- * $Id: aha152x.c,v 1.14 1996/01/17 15:11:20 fischer Exp fischer $
+ * $Id: aha152x.c,v 1.15 1996/04/30 14:52:06 fischer Exp fischer $
  *
  * $Log: aha152x.c,v $
+ * Revision 1.15  1996/04/30 14:52:06  fischer
+ * - proc info fixed
+ * - support for extended translation for >1GB disks
+ *
  * Revision 1.14  1996/01/17  15:11:20  fischer
  * - fixed lockup in MESSAGE IN phase after reconnection
  *
@@ -1205,18 +1209,23 @@
  */
 int aha152x_biosparam(Scsi_Disk * disk, kdev_t dev, int *info_array)
 {
-  int size = disk->capacity;
-
 #if defined(DEBUG_BIOSPARAM)
   if(HOSTDATA(shpnt)->debug & debug_biosparam)
-    printk("aha152x_biosparam: dev=%s, size=%d, ", kdevname(dev), size);
+    printk("aha152x_biosparam: dev=%s, size=%d, ",
+           kdevname(dev), disk->capacity);
 #endif
   
-/* I took this from other SCSI drivers, since it provides
-   the correct data for my devices. */
-  info_array[0]=64;
-  info_array[1]=32;
-  info_array[2]=size>>11;
+  if(disk->capacity<=1024*64*32) {
+    info_array[0]=64;
+    info_array[1]=32;
+    info_array[2]=disk->capacity / (64 * 32);
+  } else {
+    info_array[0] = 256;
+    info_array[1] = 63;
+    info_array[2] = disk->capacity / (256 * 63);
+    if(info_array[2] > 1023)
+      info_array[2]=1023;
+  }
 
 #if defined(DEBUG_BIOSPARAM)
   if(HOSTDATA(shpnt)->debug & debug_biosparam)
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/aha152x.h linux/drivers/scsi/aha152x.h
--- v1.3.97/linux/drivers/scsi/aha152x.h	Tue Jan 23 21:20:34 1996
+++ linux/drivers/scsi/aha152x.h	Thu May  2 07:28:01 1996
@@ -2,7 +2,7 @@
 #define _AHA152X_H
 
 /*
- * $Id: aha152x.h,v 1.14 1996/01/17 15:13:36 fischer Exp fischer $
+ * $Id: aha152x.h,v 1.15 1996/04/30 14:59:35 fischer Exp $
  */
 
 #if defined(__KERNEL__)
@@ -23,7 +23,7 @@
    (unless we support more than 1 cmd_per_lun this should do) */
 #define AHA152X_MAXQUEUE	7		
 
-#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.14 $"
+#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.15 $"
 
 extern struct proc_dir_entry proc_scsi_aha152x;
 
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v1.3.97/linux/drivers/scsi/aic7xxx.c	Sun Apr 21 19:22:11 1996
+++ linux/drivers/scsi/aic7xxx.c	Thu May  2 08:13:50 1996
@@ -1686,6 +1686,9 @@
     {
       aic7xxx_reset_current_bus(base);
     }
+    /* Ensure we don't get a RSTI interrupt from this. */
+    outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
+    outb(CLRSCSIINT, CLRINT + base);
     outb(sblkctl, SBLKCTL + base);
 
     UNPAUSE_SEQUENCER(p);
@@ -1703,6 +1706,10 @@
     {
       aic7xxx_reset_current_bus(base);
     }
+    /* Ensure we don't get a RSTI interrupt from this. */
+    outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
+    outb(CLRSCSIINT, CLRINT + base);
+
     RESTART_SEQUENCER(p);
 #ifdef AIC7XXX_DEBUG_ABORT
     printk ("aic7xxx: (reset_channel) Channel reset, sequencer restarted\n");
@@ -3320,19 +3327,27 @@
        * so we default it to 100%.
        */
       config->bus_speed = DFTHRSH_100;
-      scsi_conf = config->scsi_id | config->bus_speed;
+      scsi_conf = config->scsi_id | DFTHRSH_100;
+#if 0
       if (config->parity == AIC_ENABLED)
       {
         scsi_conf |= ENSPCHK;
       }
-
+#endif
       outb(scsi_conf, SCSICONF + base);
-      outb(config->bus_speed, DSPCISTATUS + base);
+      outb(DFTHRSH_100, DSPCISTATUS + base);
 
       /*
        * In case we are a wide card...
        */
-      outb(scsi_conf, (SCSICONF + base + 1));
+/*
+ * Try the following:
+ *
+ * 1) outb(config->scsi_id, SCSICONF + base + 1);
+ * 2) outb(scsiconf, SCSICONF + base + 1);
+ *
+ */
+      outb(config->scsi_id, SCSICONF + base + 1);
 
       printk("aic7xxx: Extended translation %sabled.\n",
 	     config->extended ? "en" : "dis");
@@ -3578,7 +3593,7 @@
     outb(config->scsi_id_b, SCSIID + base);
     scsi_conf = inb(SCSICONF + base + 1) & (ENSPCHK | STIMESEL);
     outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-    outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
+    outb(ENSELTIMO, SIMODE1 + base);
     if (p->ultra_enabled)
     {
       outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
@@ -3596,7 +3611,7 @@
   outb(config->scsi_id, SCSIID + base);
   scsi_conf = inb(SCSICONF + base) & (ENSPCHK | STIMESEL);
   outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-  outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
+  outb(ENSELTIMO, SIMODE1 + base);
   if (p->ultra_enabled)
   {
     outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
@@ -3773,6 +3788,10 @@
       udelay(1000);
       outb(0, SCSISEQ + base);
 
+      /* Ensure we don't get a RSTI interrupt from this. */
+      outb(CLRSCSIRSTI, CLRSINT1 + base);
+      outb(CLRSCSIINT, CLRINT + base);
+
       /*
        * Select Channel A.
        */
@@ -3782,6 +3801,10 @@
     outb(SCSIRSTO, SCSISEQ + base);
     udelay(1000);
     outb(0, SCSISEQ + base);
+
+    /* Ensure we don't get a RSTI interrupt from this. */
+    outb(CLRSCSIRSTI, CLRSINT1 + base);
+    outb(CLRSCSIINT, CLRINT + base);
 
     aic7xxx_delay(AIC7XXX_RESET_DELAY);
 
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/eata.h linux/drivers/scsi/eata.h
--- v1.3.97/linux/drivers/scsi/eata.h	Fri Apr 19 10:07:59 1996
+++ linux/drivers/scsi/eata.h	Thu May  2 07:48:53 1996
@@ -4,7 +4,7 @@
 #ifndef _EATA_H
 #define _EATA_H
 
-#include <linux/scsicam.h>
+#include <scsi/scsicam.h>
 
 int eata2x_detect(Scsi_Host_Template *);
 int eata2x_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/eata_dma.c linux/drivers/scsi/eata_dma.c
--- v1.3.97/linux/drivers/scsi/eata_dma.c	Fri Apr 12 15:52:00 1996
+++ linux/drivers/scsi/eata_dma.c	Thu May  2 07:48:53 1996
@@ -48,7 +48,7 @@
  * 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/29                 OS: Linux 1.3.45  *
+ *  last change: 95/04/27                 OS: Linux 1.3.95  *
  ************************************************************/
 
 /* Look in eata_dma.h for configuration and revision information */
@@ -71,7 +71,6 @@
 #include "scsi.h"
 #include "sd.h"
 #include "hosts.h"
-#include <linux/scsicam.h>
 #include "eata_dma.h"
 #include "eata_dma_proc.h" 
 
@@ -371,28 +370,13 @@
     hd->last_ccb = y;
 
     if (x >= sh->can_queue) { 
-	uint z;
-	
-	printk(KERN_EMERG "eata_dma: run out of queue slots cmdno:%ld"
-	       " intrno: %ld, can_queue: %d, x: %d, y: %d\n", 
-	       queue_counter, int_counter, sh->can_queue, x, y);
-	printk(KERN_EMERG "Status of queueslots:");
-	for(z = 0; z < sh->can_queue; z +=2) {
-	    switch(hd->ccb[z].status) {
-	    case FREE:
-		printk(KERN_EMERG "Slot %2d is FREE  \t", z);
-		break;
-	    case USED:
-		printk(KERN_EMERG "Slot %2d is USED  \t", z);
-		break;
-	    case LOCKED:
-		printk(KERN_EMERG "Slot %2d is LOCKED\t", z);
-		break;
-	    default:
-		printk(KERN_EMERG "Slot %2d is UNKNOWN\t", z);
-	    }
-	    panic("\nSystem halted.\n");
-	}
+	cmd->result = DID_BUS_BUSY << 16;
+	DBG(DBG_QUEUE && DBG_ABNORM, 
+	    printk(KERN_CRIT "eata_queue pid %ld, HBA QUEUE FULL..., "
+		   "returning DID_BUS_BUSY\n", cmd->pid));
+	done(cmd);
+	restore_flags(flags);
+	return(0);
     }
     cp = &hd->ccb[y];
     
@@ -573,7 +557,7 @@
     panic("eata_dma: abort: invalid slot status\n");
 }
 
-int eata_reset(Scsi_Cmnd * cmd)
+int eata_reset(Scsi_Cmnd * cmd, int resetflags)
 {
     ushort x, z; 
     ulong time, limit = 0;
@@ -688,6 +672,87 @@
     }
 }
 
+/* Here we try to determine the optimum queue depth for
+ * each attached device.
+ *
+ * At the moment the algorithm is rather simple
+ */
+static void eata_select_queue_depths(struct Scsi_Host *host, Scsi_Device *devicelist)
+{
+    Scsi_Device *device;
+    int devcount = 0; 
+    int factor = 0;
+    
+    
+    /* First we do a sample run go find out what we have */
+    for(device = devicelist; device != NULL; device = device->next) {
+        if (device->host == host) {
+	    devcount++;
+	    switch(device->type) {
+	    case TYPE_DISK:
+	    case TYPE_MOD:
+	        factor += TYPE_DISK_QUEUE;
+		break;
+	    case TYPE_TAPE:
+	        factor += TYPE_TAPE_QUEUE;
+		break;
+	    case TYPE_WORM:
+	    case TYPE_ROM:
+	        factor += TYPE_ROM_QUEUE;
+		break;
+	    case TYPE_PROCESSOR:
+	    case TYPE_SCANNER:
+	    default:
+	        factor += TYPE_OTHER_QUEUE;
+		break;
+	    }
+	}
+    }
+
+    DBG(DBG_REGISTER, printk(KERN_DEBUG "scsi%d: needed queueslots %d\n", 
+			     host->host_no, factor));
+
+    if(factor == 0)    /* We don't want to get a DIV BY ZERO error */
+        factor = 1;
+
+    factor = (SD(host)->queuesize * 10) / factor;
+
+    DBG(DBG_REGISTER, printk(KERN_DEBUG "scsi%d: using factor %dE-1\n", 
+			     host->host_no, factor));
+
+    /* Now that have the factor we can set the individual queuesizes */
+    for(device = devicelist; device != NULL; device = device->next) {
+        if(device->host == host) {
+	    if(SD(device->host)->bustype != IS_ISA){
+	        switch(device->type) {
+		case TYPE_DISK:
+		case TYPE_MOD:
+		    device->queue_depth = (TYPE_DISK_QUEUE * factor) / 10;
+		    break;
+		case TYPE_TAPE:
+		    device->queue_depth = (TYPE_TAPE_QUEUE * factor) / 10;
+		    break;
+		case TYPE_WORM:
+		case TYPE_ROM:
+	            device->queue_depth = (TYPE_ROM_QUEUE * factor) / 10;
+		    break;
+		case TYPE_PROCESSOR:
+		case TYPE_SCANNER:
+		default:
+		    device->queue_depth = (TYPE_OTHER_QUEUE * factor) / 10;
+		    break;
+		}
+	    } else /* ISA forces us to limit the QS because of bounce buffers*/
+	        device->queue_depth = 2; /* I know this is cruel */
+
+	    printk(KERN_INFO "scsi%d: queue depth for target %d on channel %d set "
+		   "to %d\n", host->host_no, device->id, device->channel,
+		   device->queue_depth);
+	}
+    }
+}
+
+
 char * get_board_data(u32 base, u32 irq, u32 id)
 {
     struct eata_ccb *cp;
@@ -1040,17 +1105,11 @@
      * SCSI midlevel code should support different HBA ids on every channel
      */
     sh->this_id = gc->scsi_id[3];
+
+    hd->queuesize = ntohs(gc->queuesiz);
     sh->can_queue = ntohs(gc->queuesiz);
-    
-    if (gc->OCS_enabled == TRUE) {
-	if(hd->bustype != IS_ISA)
-	    sh->cmd_per_lun = sh->can_queue/C_P_L_DIV; 
-	else
-	    sh->cmd_per_lun = 8; /* We artificially limit this to conserve 
-				  * memory, which would be needed for ISA 
-				  * bounce buffers */
-    } else 
-	sh->cmd_per_lun = 1;
+    sh->cmd_per_lun = 0;
+    sh->select_queue_depths = eata_select_queue_depths;
     
     /* FIXME:
      * SG should be allocated more dynamically 
@@ -1062,7 +1121,7 @@
      */
     if (gc->SG_64K == TRUE && ntohs(gc->SGsiz) == 64 && hd->bustype != IS_ISA){
 	sh->sg_tablesize = SG_SIZE_BIG;
-	sh->use_clustering = FALSE;
+	sh->use_clustering = TRUE;
     } else {
 	sh->sg_tablesize = ntohs(gc->SGsiz);
 	sh->use_clustering = TRUE;
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/eata_dma.h linux/drivers/scsi/eata_dma.h
--- v1.3.97/linux/drivers/scsi/eata_dma.h	Mon Dec 11 07:27:14 1995
+++ linux/drivers/scsi/eata_dma.h	Thu May  2 07:48:53 1996
@@ -73,7 +73,7 @@
 #define eata_release NULL  
 #endif
 
-#include <linux/scsicam.h>
+#include <scsi/scsicam.h>
 
 #define EATA_DMA {                   \
         NULL, NULL,                  \
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/eata_generic.h linux/drivers/scsi/eata_generic.h
--- v1.3.97/linux/drivers/scsi/eata_generic.h	Mon Dec 11 07:27:14 1995
+++ linux/drivers/scsi/eata_generic.h	Thu May  2 07:48:53 1996
@@ -64,22 +64,12 @@
 #define MAX_PCI_BUS	16	       /* Maximum # Of Busses Allowed	 */
 
 #define SG_SIZE		64 
-#define SG_SIZE_BIG	509	       /* max. 509 elements, one 4k page */
+#define SG_SIZE_BIG	252	       /* max. 8096 elements, 64k */
 
-#define C_P_L_DIV	2 /* 1 <= C_P_L_DIV <= 8	    
-			   * You can use this parameter to fine-tune
-			   * the driver. Depending on the number of 
-			   * devices and their speed and ability to queue 
-			   * commands, you will get the best results with a
-			   * value
-			   * ~= numdevices-(devices_unable_to_queue_commands/2)
-			   * The reason for this is that the disk driver 
-			   * tends to flood the queue, so that other 
-			   * drivers have problems to queue commands 
-			   * themselves. This can for example result in 
-			   * the effect that the tape stops during disk 
-			   * accesses. 
-			   */
+#define TYPE_DISK_QUEUE  16
+#define TYPE_TAPE_QUEUE  4
+#define TYPE_ROM_QUEUE   4
+#define TYPE_OTHER_QUEUE 2
 
 #define FREE	   0
 #define OK	   0
@@ -350,6 +340,7 @@
     __u32  last_ccb;		 /* Last used ccb	       */
     __u32  cplen;		 /* size of CP in words	       */
     __u16  cppadlen;		 /* pad length of cp in words  */
+    int    queuesize;
     __u8   hostid;		 /* SCSI ID of HBA	       */
     __u8   devflags;		 /* bits set for detected devices */
     __u8   moresupport;		 /* HBA supports MORE flag     */
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/eata_pio.c linux/drivers/scsi/eata_pio.c
--- v1.3.97/linux/drivers/scsi/eata_pio.c	Fri Apr 12 15:52:00 1996
+++ linux/drivers/scsi/eata_pio.c	Thu May  2 07:48:53 1996
@@ -10,9 +10,9 @@
  *      -supports all EATA-PIO boards                       *
  *      -only supports DASD devices                         *
  *                                                          *
- *  (c)1993,94,95 Michael Neuffer, Alfred Arnold            *
- *                neuffer@goofy.zdv.uni-mainz.de            *
- *                a.arnold@kfa-juelich.de                   * 
+ *  (c)1993-96 Michael Neuffer, Alfred Arnold               *
+ *             neuffer@goofy.zdv.uni-mainz.de               *
+ *             a.arnold@kfa-juelich.de                      * 
  *                                                          *
  *  This program is free software; you can redistribute it  *
  *  and/or modify it under the terms of the GNU General     *
@@ -32,7 +32,7 @@
  *  Cambridge, MA 02139, USA.                               *
  *                                                          *
  ************************************************************
- *  last change: 95/08/04                 OS: Linux 1.3.15  *
+ *  last change: 95/03/28                 OS: Linux 1.3.80  *
  ************************************************************/
 
 /* Look in eata_pio.h for configuration information */
@@ -240,7 +240,8 @@
 	if (cp->status == LOCKED) {
 	    cp->status = FREE;
 	    eata_stat = inb(base + HA_RSTATUS);
-	    printk("eata_pio: int_handler, freeing locked queueslot\n");
+	    printk(KERN_NOTICE "eata_pio: int_handler, freeing locked "
+                   "queueslot\n");
 	    DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
 	    restore_flags(flags);
 	    return;
@@ -248,7 +249,8 @@
 	
 #if DBG_INTR2
 	if (stat != 0x50) 
-	    printk("stat: %#.2x, result: %#.8x\n", stat, cmd->result); 
+	    printk(KERN_DEBUG "stat: %#.2x, result: %#.8x\n", stat, 
+                   cmd->result); 
 	DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
 #endif
 	
@@ -301,12 +303,13 @@
     
     if (hd->ccb[y].status!=FREE) { 
 	
-	DBG(DBG_QUEUE, printk("can_queue %d, x %d, y %d\n",sh->can_queue,x,y));
+	DBG(DBG_QUEUE, printk(KERN_EMERG "can_queue %d, x %d, y %d\n",
+                              sh->can_queue,x,y));
 #if DEBUG_EATA
-	panic("eata_pio: run out of queue slots cmdno:%ld intrno: %ld\n", 
-	      queue_counter, int_counter);
+	panic(KERN_EMERG "eata_pio: run out of queue slots cmdno:%ld "
+              "intrno: %ld\n", queue_counter, int_counter);
 #else
-	panic("eata_pio: run out of queue slots....\n");
+	panic(KERN_EMERG "eata_pio: run out of queue slots....\n");
 #endif
     }
     
@@ -317,8 +320,8 @@
 	
     cp->status = USED;      /* claim free slot */
 
-    DBG(DBG_QUEUE, printk("eata_pio_queue pid %ld, target: %x, lun: %x, y %d\n",
-			  cmd->pid, cmd->target, cmd->lun, y));
+    DBG(DBG_QUEUE, printk(KERN_DEBUG "eata_pio_queue pid %ld, target: %x, lun:"
+                          " %x, y %d\n", cmd->pid, cmd->target, cmd->lun, y));
     DBG(DBG_QUEUE && DBG_DELAY, DEL2(250));
     
     cmd->scsi_done = (void *)done;
@@ -378,8 +381,8 @@
     if (eata_pio_send_command(base, EATA_CMD_PIO_SEND_CP)) 
     {
 	cmd->result = DID_BUS_BUSY << 16;
-	printk("eata_pio_queue target %d, pid %ld, HBA busy, returning "
-               "DID_BUS_BUSY, done.\n", cmd->target, cmd->pid);
+	printk(KERN_NOTICE "eata_pio_queue target %d, pid %ld, HBA busy, "
+               "returning DID_BUS_BUSY, done.\n", cmd->target, cmd->pid);
         done(cmd);
         cp->status = FREE;      
         restore_flags(flags);
@@ -390,8 +393,8 @@
     outb(EATA_CMD_PIO_TRUNC, base + HA_WCOMMAND);
     for (x = 0; x < hd->cppadlen; x++) outw(0, base + HA_RDATA);
     
-    DBG(DBG_QUEUE,printk("Queued base %#.4lx pid: %ld target: %x lun: %x "
-			 "slot %d irq %d\n", (long)sh->base, cmd->pid, 
+    DBG(DBG_QUEUE,printk(KERN_DEBUG "Queued base %#.4lx pid: %ld target: %x "
+                         "lun: %x slot %d irq %d\n", (long)sh->base, cmd->pid, 
 			 cmd->target, cmd->lun, y, sh->irq));
     DBG(DBG_QUEUE && DBG_DELAY, DEL2(200));
     
@@ -407,38 +410,39 @@
     save_flags(flags);
     cli();
     
-    DBG(DBG_ABNORM, printk("eata_pio_abort called pid: %ld target: %x lun: %x"
-                           " reason %x\n", cmd->pid, cmd->target, cmd->lun, 
-                           cmd->abort_reason));
+    DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_abort called pid: %ld "
+                           "target: %x lun: %x reason %x\n", cmd->pid, 
+                           cmd->target, cmd->lun, cmd->abort_reason));
     DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
     
     
     while (inb((uint)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY)
 	if (--loop == 0) {
-	    printk("eata_pio: abort, timeout error.\n");
+	    printk(KERN_WARNING "eata_pio: abort, timeout error.\n");
 	    restore_flags(flags);
 	    DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
 	    return (SCSI_ABORT_ERROR);
 	}
     if (CD(cmd)->status == FREE) {
-	DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_NOT_RUNNING\n")); 
+	DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_NOT_RUNNING\n")); 
 	restore_flags(flags);
 	return (SCSI_ABORT_NOT_RUNNING);
     }
     if (CD(cmd)->status == USED) {
-	DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_BUSY\n"));
+	DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_BUSY\n"));
 	restore_flags(flags);
 	return (SCSI_ABORT_BUSY);  /* SNOOZE */ 
     }
     if (CD(cmd)->status == RESET) {
 	restore_flags(flags);
-	printk("eata_pio: abort, command reset error.\n");
+	printk(KERN_WARNING "eata_pio: abort, command reset error.\n");
 	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
 	return (SCSI_ABORT_ERROR);
     }
     if (CD(cmd)->status == LOCKED) {
 	restore_flags(flags);
-	DBG(DBG_ABNORM, printk("eata_pio: abort, queue slot locked.\n"));
+	DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio: abort, queue slot "
+                               "locked.\n"));
 	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
 	return (SCSI_ABORT_NOT_RUNNING);
     }
@@ -446,7 +450,7 @@
     panic("eata_pio: abort: invalid slot status\n");
 }
 
-int eata_pio_reset(Scsi_Cmnd * cmd)
+int eata_pio_reset(Scsi_Cmnd * cmd, int dummy)
 {
     uint x, z, time, limit = 0;
     ulong flags;
@@ -456,12 +460,12 @@
     save_flags(flags);
     cli();
     hprint("reset");
-    DBG(DBG_ABNORM, printk("eata_pio_reset called pid:%ld target: %x lun: %x "
-			   "reason %x\n", cmd->pid, cmd->target, cmd->lun, 
-			   cmd->abort_reason));
+    DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset called pid:%ld target:"
+                           " %x lun: %x reason %x\n", cmd->pid, cmd->target, 
+                           cmd->lun, cmd->abort_reason));
 
     if (HD(cmd)->state == RESET) {
-	printk("eata_pio_reset: exit, already in reset.\n");
+	printk(KERN_WARNING "eata_pio_reset: exit, already in reset.\n");
 	restore_flags(flags);
 	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
 	return (SCSI_RESET_ERROR);
@@ -481,7 +485,8 @@
 	
 	sp = HD(cmd)->ccb[x].cmd;
 	HD(cmd)->ccb[x].status = RESET;
-	printk("eata_pio_reset: slot %d in reset, pid %ld.\n", x, sp->pid);
+	printk(KERN_WARNING "eata_pio_reset: slot %d in reset, pid %ld.\n", x,
+               sp->pid);
 	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
 	
 	if (sp == NULL)
@@ -492,13 +497,14 @@
     /* hard reset the HBA  */
     outb((uint) cmd->host->base+HA_WCOMMAND, EATA_CMD_RESET);
     
-    DBG(DBG_ABNORM, printk("eata_pio_reset: board reset done.\n"));
+    DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: board reset done.\n"));
     HD(cmd)->state = RESET;
     
     time = jiffies;
     while (jiffies < (time + (3 * HZ)) && limit++ < 10000000);
     
-    DBG(DBG_ABNORM, printk("eata_pio_reset: interrupts disabled, loops %d.\n", limit));
+    DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: interrupts disabled, "
+                           "loops %d.\n", limit));
     DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
     
     for (x = 0; x < cmd->host->can_queue; x++) {
@@ -511,7 +517,7 @@
 	sp->result = DID_RESET << 16;
 	
 	/* This mailbox is terminated */
-	printk("eata_pio_reset: reset ccb %d.\n",x);
+	printk(KERN_WARNING "eata_pio_reset: resetted ccb %d.\n",x);
 	HD(cmd)->ccb[x].status = FREE;
 	
 	restore_flags(flags);
@@ -523,11 +529,11 @@
     restore_flags(flags);
     
     if (success) { /* hmmm... */
-	DBG(DBG_ABNORM, printk("eata_pio_reset: exit, success.\n"));
+	DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: exit, success.\n"));
 	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
 	return (SCSI_RESET_SUCCESS);
     } else {
-	DBG(DBG_ABNORM, printk("eata_pio_reset: exit, wakeup.\n"));
+	DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: exit, wakeup.\n"));
 	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
 	return (SCSI_RESET_PUNT);
     }
@@ -594,7 +600,7 @@
 	    return (FALSE);
     
     DBG(DBG_PIO && DBG_PROBE,
-	printk("Issuing PIO READ CONFIG to HBA at %#x\n", base));
+	printk(KERN_DEBUG "Issuing PIO READ CONFIG to HBA at %#x\n", base));
     eata_pio_send_command(base, EATA_CMD_PIO_READ_CONFIG);
 
     loop = R_LIMIT;
@@ -609,8 +615,8 @@
     }
     if (!(inb(base + HA_RSTATUS) & HA_SERROR)) {            /* Error ? */
 	if (htonl(EATA_SIGNATURE) == buf->signature) {
-	    DBG(DBG_PIO&&DBG_PROBE, printk("EATA Controller found at %#4x "
-					   "EATA Level: %x\n", base, 
+	    DBG(DBG_PIO&&DBG_PROBE, printk(KERN_NOTICE "EATA Controller found "
+                                           "at %#4x EATA Level: %x\n", base, 
 					   (uint) (buf->version)));
 	    
 	    while (inb(base + HA_RSTATUS) & HA_SDRQ) 
@@ -852,7 +858,8 @@
 	    if (((pal1 == 0x12) && (pal2 == 0x14)) ||
 		((pal1 == 0x38) && (pal2 == 0xa3) && (pal3 == 0x82)) ||
 		((pal1 == 0x06) && (pal2 == 0x94) && (pal3 == 0x24))) {
-		DBG(DBG_PROBE, printk("EISA EATA id tags found: %x %x %x \n",
+		DBG(DBG_PROBE, printk(KERN_NOTICE "EISA EATA id tags found: "
+                                      "%x %x %x \n",
 				      (int)pal1, (int)pal2, (int)pal3));
 #endif
 		if (get_pio_conf_PIO(base, buf) == TRUE) {
@@ -860,7 +867,8 @@
 		    if (buf->IRQ) {
 			register_pio_HBA(base, buf, tpnt);
 		    } else
-			printk("eata_dma: No valid IRQ. HBA removed from list\n");
+			printk(KERN_NOTICE "eata_dma: No valid IRQ. HBA "
+                               "removed from list\n");
 		}
 		/* Nothing found here so we take it from the list */
 		EISAbases[i] = 0;
@@ -876,7 +884,8 @@
 {
 
 #ifndef CONFIG_PCI
-    printk("eata_pio: kernel PCI support not enabled. Skipping scan for PCI HBAs.\n");
+    printk(KERN_ERR "eata_pio: kernel PCI support not enabled. Skipping scan "
+           "for PCI HBAs.\n");
 #else
     
     u8 pci_bus, pci_device_fn;
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/eata_pio.h linux/drivers/scsi/eata_pio.h
--- v1.3.97/linux/drivers/scsi/eata_pio.h	Sat Nov 11 13:04:40 1995
+++ linux/drivers/scsi/eata_pio.h	Thu May  2 07:48:53 1996
@@ -1,6 +1,6 @@
 /********************************************************
 * Header file for eata_pio.c Linux EATA-PIO SCSI driver *
-* (c) 1993,94,95 Michael Neuffer			*
+* (c) 1993-96 Michael Neuffer  	                        *
 *********************************************************
 * last change: 95/06/21					*
 ********************************************************/
@@ -12,7 +12,7 @@
 #include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
-#include <linux/scsicam.h>
+#include <scsi/scsicam.h>
 
 #ifndef HOSTS_C
 #include "eata_generic.h"
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c
--- v1.3.97/linux/drivers/scsi/fdomain.c	Fri Apr 12 15:52:00 1996
+++ linux/drivers/scsi/fdomain.c	Thu May  2 07:48:53 1996
@@ -1913,7 +1913,7 @@
 }
 
 #include "sd.h"
-#include "scsi_ioctl.h"
+#include <scsi/scsi_ioctl.h>
 
 int fdomain_16x0_biosparam( Scsi_Disk *disk, kdev_t dev, int *info_array )
 {
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/in2000.c linux/drivers/scsi/in2000.c
--- v1.3.97/linux/drivers/scsi/in2000.c	Mon Apr 29 18:05:18 1996
+++ linux/drivers/scsi/in2000.c	Sat May  4 19:39:23 1996
@@ -29,9 +29,9 @@
  * Bill Earnest, Larry Doolittle, Roger Sunshine, John Luckey,
  * Matt Postiff, Peter Lu, zerucha@shell.portal.com, and Eric
  * Youngdale). I should also mention the driver written by
- * Hamish Mcdonald for the (GASP!) Amiga A2091 card, included
+ * Hamish Macdonald for the (GASP!) Amiga A2091 card, included
  * in the Linux-m68k distribution; it gave me a good initial
- * understandng of the proper way to run a WD33c93 chip, and I
+ * understanding of the proper way to run a WD33c93 chip, and I
  * ended up stealing lots of code from it.
  *
  * _This_ driver is (I feel) an improvement over the old one in
@@ -663,7 +663,7 @@
             if ((i = cmd->SCp.this_residual) > (IN2000_FIFO_SIZE - 16) )
                i = IN2000_FIFO_SIZE - 16;
             cmd->SCp.have_data_in = i;    /* this much data in fifo */
-            i >>= 1;                      /* Gulp. Assumimg modulo 2. */
+            i >>= 1;                      /* Gulp. Assuming modulo 2. */
             sp = (unsigned short *)cmd->SCp.ptr;
             f = hostdata->io_base + IO_FIFO;
 
@@ -1035,7 +1035,7 @@
 DB(DB_INTR,printk("{%02x:%02x-",asr,sr))
 
 /* After starting a FIFO-based transfer, the next _WD3393_ interrupt is
- * guarenteed to be in response to the completion of the transfer.
+ * guaranteed to be in response to the completion of the transfer.
  * If we were reading, there's probably data in the fifo that needs
  * to be copied into RAM - do that here. Also, we have to update
  * 'this_residual' and 'ptr' based on the contents of the
@@ -1851,7 +1851,7 @@
  *
  * We probably reached this point because of an unlikely race condition
  * between the command completing successfully and the abortion code,
- * so we won't panic, but we will notify the user in case somethign really
+ * so we won't panic, but we will notify the user in case something really
  * broke.
  */
 
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/in2000.readme linux/drivers/scsi/in2000.readme
--- v1.3.97/linux/drivers/scsi/in2000.readme	Mon Apr 29 18:05:18 1996
+++ linux/drivers/scsi/in2000.readme	Sat May  4 19:39:23 1996
@@ -17,7 +17,7 @@
 
 UPDATE NEWS: version 1.27 - 10 Apr 96
 
-   Fixed a well-hidden bug in the adapative-disconnect code
+   Fixed a well-hidden bug in the adaptive-disconnect code
    that would show up every now and then during extreme
    heavy loads involving 2 or more simultaneously active
    devices. Thanks to Joe Mack for keeping my nose to the
@@ -152,14 +152,14 @@
    I should mention that Drew Eckhardt's 'Generic NCR5380' sources
    were my main inspiration, with lots of reference to the IN2000
    driver currently distributed in the kernel source. I also owe
-   much to a driver written by Hamish Mcdonald for Linux-m68k(!).
+   much to a driver written by Hamish Macdonald for Linux-m68k(!).
    And to Eric Wright for being an ALPHA guinea pig. And to Bill
    Earnest for 2 tons of great input and information. And to David
    Willmore for extensive 'bonnie' testing.
 
    Be forewarned that while I've had good luck with it, this
    is the first time it's been loose out in the wide world.
-   It wouldn't suprise me if people uncover problems that
+   It wouldn't surprise me if people uncover problems that
    I haven't caught....
 
    Please try the driver out. Test it, beat on it. And PLEASE get back
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v1.3.97/linux/drivers/scsi/scsi.c	Sat Apr 27 15:19:57 1996
+++ linux/drivers/scsi/scsi.c	Thu May  2 07:48:54 1996
@@ -193,7 +193,7 @@
 #ifdef DEBUG
     #define SCSI_TIMEOUT (5*HZ)
 #else
-    #define SCSI_TIMEOUT (1*HZ)
+    #define SCSI_TIMEOUT (2*HZ)
 #endif
 
 #ifdef DEBUG
@@ -201,15 +201,15 @@
     #define ABORT_TIMEOUT SCSI_TIMEOUT
     #define RESET_TIMEOUT SCSI_TIMEOUT
 #else
-    #define SENSE_TIMEOUT (5*HZ/10)
-    #define RESET_TIMEOUT (5*HZ/10)
-    #define ABORT_TIMEOUT (5*HZ/10)
+    #define SENSE_TIMEOUT (1*HZ)
+    #define RESET_TIMEOUT (5*HZ)
+    #define ABORT_TIMEOUT (5*HZ)
 #endif
 
-#define MIN_RESET_DELAY (1*HZ)
+#define MIN_RESET_DELAY (3*HZ)
 
 /* Do not call reset on error if we just did a reset within 10 sec. */
-#define MIN_RESET_PERIOD (10*HZ)
+#define MIN_RESET_PERIOD (15*HZ)
 
 /* The following devices are known not to tolerate a lun != 0 scan for
  * one reason or another.  Some will respond to all luns, others will
@@ -580,7 +580,7 @@
   printk("\n");
 #endif
 
-  if (SCpnt->result) {
+if (host_byte(SCpnt->result) != DID_OK) {
     if (((driver_byte (SCpnt->result) & DRIVER_SENSE) ||
          (status_byte (SCpnt->result) & CHECK_CONDITION)) &&
         ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
@@ -816,9 +816,10 @@
  *  Flag bits for the internal_timeout array
  */
 #define NORMAL_TIMEOUT 0
-#define IN_ABORT 1
-#define IN_RESET 2
+#define IN_ABORT  1
+#define IN_RESET  2
 #define IN_RESET2 4
+#define IN_RESET3 8
 
 /*
  * This is our time out function, called when the timer expires for a
@@ -829,7 +830,7 @@
 static void scsi_times_out (Scsi_Cmnd * SCpnt)
 {
     
-    switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET | IN_RESET2))
+    switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET | IN_RESET2 | IN_RESET3))
     {
     case NORMAL_TIMEOUT:
 	{
@@ -851,14 +852,28 @@
 	 * you might conceivably want the machine up and running
 	 * esp if you have an ide disk. 
 	 */
-	printk("SCSI host %d reset (pid %ld) timed out - trying harder\n",
-	       SCpnt->host->host_no, SCpnt->pid);
+	printk("SCSI host %d channel %d reset (pid %ld) timed out - "
+               "trying harder\n",
+	       SCpnt->host->host_no, SCpnt->channel, SCpnt->pid);
 	SCpnt->internal_timeout &= ~IN_RESET;
 	SCpnt->internal_timeout |= IN_RESET2;
         scsi_reset (SCpnt,
 		    SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_BUS_RESET);
 	return;
 	
+    case (IN_ABORT | IN_RESET | IN_RESET2):
+	/* Obviously the bus reset didn't work.
+	 * Let's try even harder and call for an HBA reset.
+         * Maybe the HBA itself crashed and this will shake it loose.
+	 */
+	printk("SCSI host %d reset (pid %ld) timed out - trying to shake it loose\n",
+	       SCpnt->host->host_no, SCpnt->pid);
+	SCpnt->internal_timeout &= ~(IN_RESET | IN_RESET2);
+	SCpnt->internal_timeout |= IN_RESET3;
+        scsi_reset (SCpnt,
+		    SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_HOST_RESET);
+	return;
+		
     default:
 	printk("SCSI host %d reset (pid %ld) timed out again -\n",
 	       SCpnt->host->host_no, SCpnt->pid);
@@ -1946,7 +1961,7 @@
 {
   Scsi_Cmnd *SCpnt;
   for (SCpnt = Host->host_queue; SCpnt; SCpnt = SCpnt->next)
-    scsi_mark_device_reset(SCpnt->device);
+      scsi_mark_device_reset(SCpnt->device);
 }
 
 
@@ -1968,8 +1983,8 @@
     Scsi_Cmnd * SCpnt1;
     struct Scsi_Host * host = SCpnt->host;
 
-    printk("SCSI bus is being reset for host %d.\n",
-	   host->host_no);
+    printk("SCSI bus is being reset for host %d channel %d.\n",
+	   host->host_no, SCpnt->channel);
  
 #if 0
     /*
@@ -2096,7 +2111,7 @@
 		else scsi_mark_device_reset(SCpnt->device);
 		save_flags(flags);
 		cli();
-		SCpnt->internal_timeout &= ~IN_RESET;
+		SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
 		restore_flags(flags);
 		return 0;
 	    case SCSI_RESET_PENDING:
@@ -2108,7 +2123,7 @@
 	    case SCSI_RESET_NOT_RUNNING:
 		return 0;
 	    case SCSI_RESET_PUNT:
-                SCpnt->internal_timeout &= ~IN_RESET;
+		SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
                 scsi_request_sense (SCpnt);
                 return 0;
 	    case SCSI_RESET_WAKEUP:
@@ -2117,14 +2132,15 @@
 	        else if (temp & SCSI_RESET_BUS_RESET)
 		  scsi_mark_bus_reset(host, SCpnt->channel);
 		else scsi_mark_device_reset(SCpnt->device);
-		SCpnt->internal_timeout &= ~IN_RESET;
+		SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
 		scsi_request_sense (SCpnt);
                 /*
-                 * Since a bus reset was performed, we
+                 * If a bus reset was performed, we
                  * need to wake up each and every command
-                 * that was active on the bus.
+                 * that was active on the bus or if it was a HBA
+                 * reset all active commands on all channels
                  */
-                if( temp & SCSI_RESET_BUS_RESET )
+                if( temp & SCSI_RESET_HOST_RESET )
                 {
 		    SCpnt1 = host->host_queue;
 		    while(SCpnt1) {
@@ -2133,6 +2149,15 @@
 			    scsi_request_sense (SCpnt1);
 			SCpnt1 = SCpnt1->next;
                     }
+                } else if( temp & SCSI_RESET_BUS_RESET ) {
+                    SCpnt1 = host->host_queue;
+                    while(SCpnt1) {
+                        if(SCpnt1->request.rq_status != RQ_INACTIVE
+                           && SCpnt1 != SCpnt 
+                           && SCpnt1->channel == SCpnt->channel)
+                            scsi_request_sense (SCpnt);
+                        SCpnt1 = SCpnt1->next;
+                    }
                 }
 		return 0;
 	    case SCSI_RESET_SNOOZE:
@@ -2143,7 +2168,7 @@
 		 */
 		save_flags(flags);
 		cli();
-		SCpnt->internal_timeout &= ~IN_RESET;
+		SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
 		update_timeout(SCpnt, 0);
 		restore_flags(flags);
 		/* If you snooze, you lose... */
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v1.3.97/linux/drivers/scsi/scsi.h	Fri Apr 19 10:08:00 1996
+++ linux/drivers/scsi/scsi.h	Thu May  2 07:48:54 1996
@@ -19,7 +19,7 @@
  * Some of the public constants are being moved to this file.
  * We include it here so that what came from where is transparent.
  */
-#include <linux/scsi.h>
+#include <scsi/scsi.h>
 
 
 /*
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c
--- v1.3.97/linux/drivers/scsi/scsi_ioctl.c	Mon Apr  8 19:01:44 1996
+++ linux/drivers/scsi/scsi_ioctl.c	Thu May  2 07:48:54 1996
@@ -19,7 +19,7 @@
 #include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
-#include "scsi_ioctl.h"
+#include <scsi/scsi_ioctl.h>
 
 #define MAX_RETRIES 5   
 #define MAX_TIMEOUT 900
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/scsi_ioctl.h linux/drivers/scsi/scsi_ioctl.h
--- v1.3.97/linux/drivers/scsi/scsi_ioctl.h	Sun Sep 10 09:37:22 1995
+++ linux/drivers/scsi/scsi_ioctl.h	Thu Jan  1 02:00:00 1970
@@ -1,21 +0,0 @@
-#ifndef _SCSI_IOCTL_H
-#define _SCSI_IOCTL_H 
-
-#define SCSI_IOCTL_SEND_COMMAND 1
-#define SCSI_IOCTL_TEST_UNIT_READY 2
-#define SCSI_IOCTL_BENCHMARK_COMMAND 3
-#define SCSI_IOCTL_SYNC 4			/* Request synchronous parameters */
-/* The door lock/unlock constants are compatible with Sun constants for
-   the cdrom */
-#define SCSI_IOCTL_DOORLOCK 0x5380		/* lock the eject mechanism */
-#define SCSI_IOCTL_DOORUNLOCK 0x5381		/* unlock the mechanism	  */
-
-#define	SCSI_REMOVAL_PREVENT	1
-#define	SCSI_REMOVAL_ALLOW	0
-
-extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
-extern int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
-
-#endif
-
-
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/scsi_syms.c linux/drivers/scsi/scsi_syms.c
--- v1.3.97/linux/drivers/scsi/scsi_syms.c	Fri Apr 19 10:08:00 1996
+++ linux/drivers/scsi/scsi_syms.c	Thu May  2 07:48:54 1996
@@ -21,7 +21,7 @@
 #include <asm/dma.h>
 
 #include "scsi.h"
-#include "scsi_ioctl.h"
+#include <scsi/scsi_ioctl.h>
 #include "hosts.h"
 #include "constants.h"
 
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v1.3.97/linux/drivers/scsi/sd.c	Wed Apr 17 09:06:32 1996
+++ linux/drivers/scsi/sd.c	Thu May  2 07:48:54 1996
@@ -41,7 +41,7 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
-#include "scsi_ioctl.h"
+#include <scsi/scsi_ioctl.h>
 #include "constants.h"
 
 #include <linux/genhd.h>
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/sd_ioctl.c linux/drivers/scsi/sd_ioctl.c
--- v1.3.97/linux/drivers/scsi/sd_ioctl.c	Sun Apr 21 12:39:02 1996
+++ linux/drivers/scsi/sd_ioctl.c	Thu May  2 07:48:54 1996
@@ -15,10 +15,10 @@
 
 #include <linux/blk.h>
 #include "scsi.h"
-#include "scsi_ioctl.h"
+#include <scsi/scsi_ioctl.h>
 #include "hosts.h"
 #include "sd.h"
-#include <linux/scsicam.h>     /* must follow "hosts.h" */
+#include <scsi/scsicam.h>     /* must follow "hosts.h" */
 
 int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
 {
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c
--- v1.3.97/linux/drivers/scsi/seagate.c	Tue Apr  2 13:32:21 1996
+++ linux/drivers/scsi/seagate.c	Thu May  2 07:48:54 1996
@@ -1632,7 +1632,7 @@
 
 #include <asm/segment.h>
 #include "sd.h"
-#include "scsi_ioctl.h"
+#include <scsi/scsi_ioctl.h>
 
 int seagate_st0x_biosparam(Disk * disk, kdev_t dev, int* ip) {
   unsigned char buf[256 + sizeof(int) * 2], cmd[6], *data, *page;
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
--- v1.3.97/linux/drivers/scsi/sg.c	Tue Jan  9 11:33:30 1996
+++ linux/drivers/scsi/sg.c	Thu May  2 07:48:54 1996
@@ -24,8 +24,8 @@
 #include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
-#include "scsi_ioctl.h"
-#include "sg.h"
+#include <scsi/scsi_ioctl.h>
+#include <scsi/sg.h>
 
 static int sg_init(void);
 static int sg_attach(Scsi_Device *);
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/sg.h linux/drivers/scsi/sg.h
--- v1.3.97/linux/drivers/scsi/sg.h	Thu Jun 29 11:19:13 1995
+++ linux/drivers/scsi/sg.h	Thu Jan  1 02:00:00 1970
@@ -1,36 +0,0 @@
-/*
-   History:
-    Started: Aug 9 by Lawrence Foard (entropy@world.std.com), to allow user 
-     process control of SCSI devices.
-    Development Sponsored by Killy Corp. NY NY
-*/
-
-/* 
- An SG device is accessed by writing "packets" to it, the replies
- are then read using the read call. The same header is used for 
- reply, just ignore reply_len field.
-*/
-
-struct sg_header
- {
-  int pack_len;    /* length of incoming packet <4096 (including header) */
-  int reply_len;   /* maximum length <4096 of expected reply */
-  int pack_id;     /* id number of packet */
-  int result;      /* 0==ok, otherwise refer to errno codes */
-  unsigned int twelve_byte:1; /* Force 12 byte command length for group 6 & 7 commands  */
-  unsigned int other_flags:31;			/* for future use */
-  unsigned char sense_buffer[16]; /* used only by reads */
-  /* command follows then data for command */
- };
-
-/* ioctl's */
-#define SG_SET_TIMEOUT 0x2201  /* set timeout *(int *)arg==timeout */
-#define SG_GET_TIMEOUT 0x2202  /* get timeout return timeout */
-
-#define SG_DEFAULT_TIMEOUT (60*HZ) /* 1 minute timeout */
-#define SG_DEFAULT_RETRIES 1
-
-#define SG_MAX_QUEUE 4 /* maximum outstanding request, arbitrary, may be
-			  changed if sufficient DMA buffer room available */
-
-#define SG_BIG_BUFF 32768
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v1.3.97/linux/drivers/scsi/sr.c	Fri Apr 12 15:52:01 1996
+++ linux/drivers/scsi/sr.c	Thu May  2 07:48:54 1996
@@ -35,7 +35,7 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "sr.h"
-#include "scsi_ioctl.h"   /* For the door lock/unlock commands */
+#include <scsi/scsi_ioctl.h>   /* For the door lock/unlock commands */
 #include "constants.h"
 
 #define MAX_RETRIES 3
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c
--- v1.3.97/linux/drivers/scsi/sr_ioctl.c	Tue Apr  2 13:32:21 1996
+++ linux/drivers/scsi/sr_ioctl.c	Thu May  2 07:48:54 1996
@@ -10,7 +10,7 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "sr.h"
-#include "scsi_ioctl.h"
+#include <scsi/scsi_ioctl.h>
 
 #include <linux/cdrom.h>
 
@@ -555,12 +555,21 @@
     }
 	
     case BLKRASET:
+    {
 	if(!suser())  return -EACCES;
 	if(!(inode->i_rdev)) return -EINVAL;
 	if(arg > 0xff) return -EINVAL;
 	read_ahead[MAJOR(inode->i_rdev)] = arg;
 	return 0;
 	RO_IOCTLS(dev,arg);
+    }
+
+    case CDROMRESET:
+    {
+	invalidate_buffers(MKDEV(MAJOR(inode->i_rdev),MINOR(inode->i_rdev)));
+	return 0;
+    }
+
     default:
 	return scsi_ioctl(scsi_CDs[target].device,cmd,(void *) arg);
     }
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v1.3.97/linux/drivers/scsi/st.c	Wed Apr 24 17:00:41 1996
+++ linux/drivers/scsi/st.c	Fri May  3 10:12:16 1996
@@ -5,13 +5,13 @@
   History:
   Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
   Contribution and ideas from several people including (in alphabetical
-  order) Klaus Ehrenfried, Steve Hirsch, Wolfgang Denk, Andreas Koppenh"ofer,
-  J"org Weule, and Eric Youngdale.
+  order) Klaus Ehrenfried, Wolfgang Denk, Steve Hirsch, Andreas Koppenh"ofer,
+  Eyal Lebedinsky, J"org Weule, and Eric Youngdale.
 
   Copyright 1992 - 1996 Kai Makisara
 		 email Kai.Makisara@metla.fi
 
-  Last modified: Wed Apr 24 09:53:23 1996 by root@kai.makisara.fi
+  Last modified: Thu May  2 19:41:34 1996 by makisara@kai.makisara.fi
   Some small formal changes - aeb, 950809
 */
 
@@ -43,7 +43,7 @@
 #include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
-#include "scsi_ioctl.h"
+#include <scsi/scsi_ioctl.h>
 #include "st.h"
 #include "constants.h"
 
@@ -72,7 +72,7 @@
 #define NO_TAPE  NOT_READY
 
 #define ST_TIMEOUT (900 * HZ)
-#define ST_LONG_TIMEOUT (2000 * HZ)
+#define ST_LONG_TIMEOUT (14000 * HZ)
 
 #define TAPE_NR(x) (MINOR(x) & ~(128 | ST_MODE_MASK))
 #define TAPE_MODE(x) ((MINOR(x) & ST_MODE_MASK) >> ST_MODE_SHIFT)
@@ -107,8 +107,11 @@
 
 static int st_compression(Scsi_Tape *, int);
 
-static int st_int_ioctl(struct inode * inode,struct file * file,
-	     unsigned int cmd_in, unsigned long arg);
+static int find_partition(struct inode *);
+static int update_partition(struct inode *);
+
+static int st_int_ioctl(struct inode * inode, unsigned int cmd_in,
+			unsigned long arg);
 
 
 
@@ -424,11 +427,14 @@
   if (STp->ready != ST_READY)
     return 0;
 
-  if (STp->rw == ST_WRITING)  /* Writing */
+  if (STp->ps[STp->partition].rw == ST_WRITING)  /* Writing */
     return flush_write_buffer(STp);
 
-  if (STp->block_size == 0)
+  if (STp->block_size == 0) {
+    STp->eof = ST_NOEOF;
+    STp->eof_hit = 0;
     return 0;
+  }
 
   backspace = ((STp->buffer)->buffer_bytes +
     (STp->buffer)->read_pointer) / STp->block_size -
@@ -446,7 +452,7 @@
       }
     }
     if (!result && backspace > 0)
-      result = st_int_ioctl(inode, filp, MTBSR, backspace);
+      result = st_int_ioctl(inode, MTBSR, backspace);
   }
   else if ((STp->eof == ST_FM) && !STp->eof_hit) {
     (STp->mt_status)->mt_fileno++;
@@ -457,14 +463,49 @@
 
 }
 
+/* Set the mode parameters */
+	static int
+set_mode_densblk(struct inode * inode, Scsi_Tape *STp, ST_mode *STm)
+{
+    int set_it = FALSE;
+    unsigned long arg;
+    int dev = TAPE_NR(inode->i_rdev);
+
+    if (!STp->density_changed &&
+	STm->default_density >= 0 &&
+	STm->default_density != STp->density) {
+      arg = STm->default_density;
+      set_it = TRUE;
+    }
+    else
+      arg = STp->density;
+    arg <<= MT_ST_DENSITY_SHIFT;
+    if (!STp->blksize_changed &&
+	STm->default_blksize >= 0 &&
+	STm->default_blksize != STp->block_size) {
+      arg |= STm->default_blksize;
+      set_it = TRUE;
+    }
+    else
+      arg |= STp->block_size;
+    if (set_it &&
+	st_int_ioctl(inode, SET_DENS_AND_BLK, arg)) {
+      printk(KERN_WARNING
+	     "st%d: Can't set default block size to %d bytes and density %x.\n",
+	     dev, STm->default_blksize, STm->default_density);
+      if (modes_defined)
+	return (-EINVAL);
+    }
+    return 0;
+}
+
 
 /* Open the device */
 	static int
 scsi_tape_open(struct inode * inode, struct file * filp)
 {
     unsigned short flags;
-    int i, need_dma_buffer, new_session = FALSE, set_it;
-    unsigned int arg;
+    int i, need_dma_buffer, new_session = FALSE;
     unsigned char cmd[10];
     Scsi_Cmnd * SCpnt;
     Scsi_Tape * STp;
@@ -513,13 +554,13 @@
       STp->buffer = st_buffers[i];
     (STp->buffer)->in_use = 1;
     (STp->buffer)->writing = 0;
-    STp->in_use = 1;
 
     flags = filp->f_flags;
     STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY);
 
     STp->dirty = 0;
-    STp->rw = ST_IDLE;
+    for (i=0; i < ST_NBR_PARTITIONS; i++)
+      STp->ps[i].rw = ST_IDLE;
     STp->ready = ST_READY;
     if (STp->eof != ST_EOD)  /* Save EOD across opens */
       STp->eof = ST_NOEOF;
@@ -547,6 +588,15 @@
       (STp->mt_status)->mt_fileno = STp->drv_block = 0;
       STp->eof = ST_NOEOF;
       (STp->device)->was_reset = 0;
+      STp->partition = STp->new_partition = 0;
+      if (STp->can_partitions)
+	STp->nbr_partitions = 1;  /* This guess will be updated later if necessary */
+      for (i=0; i < ST_NBR_PARTITIONS; i++) {
+	STp->ps[i].rw = ST_IDLE;
+	STp->ps[i].moves_after_eof = 1;
+	STp->ps[i].at_sm = 0;
+	STp->ps[i].last_block_valid = FALSE;
+      }
       new_session = TRUE;
     }
 
@@ -566,7 +616,9 @@
       STp->block_size = 0;
       STp->eof = ST_NOEOF;
       (STp->mt_status)->mt_fileno = STp->drv_block = 0;
+      STp->partition = STp->new_partition = 0;
       STp->door_locked = ST_UNLOCKED;
+      STp->in_use = 1;
       if (scsi_tapes[dev].device->host->hostt->usage_count)
 	(*scsi_tapes[dev].device->host->hostt->usage_count)++;
       if(st_template.usage_count) (*st_template.usage_count)++;
@@ -645,7 +697,7 @@
 	printk(KERN_NOTICE "st%d: Blocksize %d too large for buffer.\n", dev,
 	       STp->block_size);
 	(STp->buffer)->in_use = 0;
-	STp->in_use = 0;
+	STp->buffer = NULL;
 	return (-EIO);
       }
       STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
@@ -673,52 +725,45 @@
 #endif
       if ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR) {
 	(STp->buffer)->in_use = 0;
-	STp->buffer = 0;
-	STp->in_use = 0;
+	STp->buffer = NULL;
 	return (-EROFS);
       }
     }
 
-    if (new_session) {
+    if (STp->can_partitions && STp->nbr_partitions < 1) {
+      /* This code is reached when the device is opened for the first time
+	 after the driver has been initialized with tape in the drive and the
+	 partition support has been enabled. */
+#if DEBUG
+      if (debugging)
+	printk(ST_DEB_MSG "st%d: Updating partition number in status.\n", dev);
+#endif
+      if ((STp->partition = find_partition(inode)) < 0) {
+	(STp->buffer)->in_use = 0;
+	STp->buffer = NULL;
+	return STp->partition;
+      }
+      STp->new_partition = STp->partition;
+      STp->nbr_partitions = 1;  /* This guess will be updated when necessary */
+    }
+
+    if (new_session) {  /* Change the drive parameters for the new mode */
       STp->density_changed = STp->blksize_changed = FALSE;
       STp->compression_changed = FALSE;
-      if (!(STm->defaults_for_writes)) {
-	set_it = FALSE;
-	if (STm->default_density >= 0 &&
-	    STm->default_density != STp->density) {
-	  arg = STm->default_density;
-	  set_it = TRUE;
-	}
-	else
-	  arg = STp->density;
-	arg <<= MT_ST_DENSITY_SHIFT;
-	if (STm->default_blksize >= 0 &&
-	    STm->default_blksize != STp->block_size) {
-	  arg |= STm->default_blksize;
-	  set_it = TRUE;
-	}
-	else
-	  arg |= STp->block_size;
-	if (set_it &&
-	    st_int_ioctl(inode, filp, SET_DENS_AND_BLK, arg)) {
-	  printk(KERN_WARNING
-		 "st%d: Can't set default block size to %d bytes and density %x.\n",
-		 dev, STm->default_blksize, STm->default_density);
-	  if (modes_defined) {
-	    (STp->buffer)->in_use = 0;
-	    STp->buffer = 0;
-	    STp->in_use = 0;
-	    return (-EINVAL);
-	  }
-	}
+      if (!(STm->defaults_for_writes) &&
+	  (i = set_mode_densblk(inode, STp, STm)) < 0) {
+	(STp->buffer)->in_use = 0;
+	STp->buffer = NULL;
+	return i;
       }
       if (STp->default_drvbuffer != 0xff) {
-	if (st_int_ioctl(inode, filp, MTSETDRVBUFFER, STp->default_drvbuffer))
+	if (st_int_ioctl(inode, MTSETDRVBUFFER, STp->default_drvbuffer))
 	  printk(KERN_WARNING "st%d: Can't set default drive buffering to %d.\n",
 		 dev, STp->default_drvbuffer);
       }
     }
 
+    STp->in_use = 1;
     if (scsi_tapes[dev].device->host->hostt->usage_count)
       (*scsi_tapes[dev].device->host->hostt->usage_count)++;
     if(st_template.usage_count) (*st_template.usage_count)++;
@@ -741,7 +786,16 @@
     dev = TAPE_NR(devt);
     STp = &(scsi_tapes[dev]);
 
-    if ( STp->rw == ST_WRITING && !(STp->device)->was_reset) {
+    if (STp->can_partitions &&
+	update_partition(inode) < 0) {
+#if DEBUG
+      if (debugging)
+	printk(ST_DEB_MSG "st%d: update_partition at close failed.\n", dev);
+#endif
+      goto out;
+    }
+
+    if ( STp->ps[STp->partition].rw == ST_WRITING && !(STp->device)->was_reset) {
 
       result = flush_write_buffer(STp);
 
@@ -762,7 +816,7 @@
 
 	SCpnt = st_do_scsi(NULL, STp, cmd, 0, ST_TIMEOUT, MAX_WRITE_RETRIES);
 	if (!SCpnt)
-	  return;
+	  goto out;
 
 	SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
 
@@ -790,18 +844,19 @@
 	back_over_eof(STp);
     }
 
+out:
     if (STp->rew_at_close)
-      st_int_ioctl(inode, filp, MTREW, 1);
+      st_int_ioctl(inode, MTREW, 1);
 
     if (STp->door_locked == ST_LOCKED_AUTO)
-      st_int_ioctl(inode, filp, MTUNLOCK, 0);
+      st_int_ioctl(inode, MTUNLOCK, 0);
 
     if (STp->buffer != NULL) {
       normalize_buffer(STp->buffer);
       (STp->buffer)->in_use = 0;
     }
-    STp->in_use = 0;
 
+    STp->in_use = 0;
     if (scsi_tapes[dev].device->host->hostt->usage_count)
       (*scsi_tapes[dev].device->host->hostt->usage_count)--;
     if(st_template.usage_count) (*st_template.usage_count)--;
@@ -816,13 +871,13 @@
 {
     int total, do_count, blks, retval, transfer;
     int write_threshold;
-    int doing_write = 0, set_it;
-    unsigned int arg;
+    int doing_write = 0;
     static unsigned char cmd[10];
     const char *b_point;
     Scsi_Cmnd * SCpnt = NULL;
     Scsi_Tape * STp;
     ST_mode * STm;
+    ST_partstat * STps;
     int dev = TAPE_NR(inode->i_rdev);
 
     STp = &(scsi_tapes[dev]);
@@ -846,6 +901,11 @@
     }
 #endif
 
+    if (STp->can_partitions &&
+	(retval = update_partition(inode)) < 0)
+      return retval;
+    STps = &(STp->ps[STp->partition]);
+
     if (STp->write_prot)
       return (-EACCES);
 
@@ -855,42 +915,19 @@
       return (-EOVERFLOW);
 
     if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
-       !st_int_ioctl(inode, filp, MTLOCK, 0))
+       !st_int_ioctl(inode, MTLOCK, 0))
       STp->door_locked = ST_LOCKED_AUTO;
 
-    if (STp->rw == ST_READING) {
+    if (STps->rw == ST_READING) {
       retval = flush_buffer(inode, filp, 0);
       if (retval)
-       return retval;
-      STp->rw = ST_WRITING;
+	return retval;
+      STps->rw = ST_WRITING;
     }
-    else if (STp->rw != ST_WRITING &&
+    else if (STps->rw != ST_WRITING &&
 	     (STp->mt_status)->mt_fileno == 0 && STp->drv_block == 0) {
-      set_it = FALSE;
-      if (!STp->density_changed && STm->default_density >= 0 &&
-	  STm->default_density != STp->density) {
-	  arg = STm->default_density;
-	  set_it = TRUE;
-      }
-      else
-	arg = STp->density;
-      arg <<= MT_ST_DENSITY_SHIFT;
-      if (!STp->blksize_changed && STm->default_blksize >= 0 &&
-	  STm->default_blksize != STp->block_size) {
-	arg |= STm->default_blksize;
-	set_it = TRUE;
-      }
-      else
-	arg |= STp->block_size;
-      if (set_it &&
-	  st_int_ioctl(inode, filp, SET_DENS_AND_BLK, arg)) {
-	printk(KERN_WARNING
-	       "st%d: Can't set default block size to %d bytes and density %x.\n",
-	       dev, STm->default_blksize, STm->default_density);
-	if (modes_defined)
-	  return (-EINVAL);
-      }
-
+      if ((retval = set_mode_densblk(inode, STp, STm)) < 0)
+	return retval;
       if (STm->default_compression != ST_DONT_TOUCH &&
 	  !(STp->compression_changed)) {
 	if (st_compression(STp, (STm->default_compression == ST_YES))) {
@@ -902,8 +939,8 @@
       }
     }
 
-    if (STp->moves_after_eof < 255)
-      STp->moves_after_eof++;
+    if (STps->moves_after_eof < 255)
+      STps->moves_after_eof++;
 
     if ((STp->buffer)->writing) {
       write_behind_check(STp);
@@ -943,7 +980,7 @@
     cmd[0] = WRITE_6;
     cmd[1] = (STp->block_size != 0);
 
-    STp->rw = ST_WRITING;
+    STps->rw = ST_WRITING;
 
     b_point = buf;
     while((STp->block_size == 0 && !STm->do_async_writes && count > 0) ||
@@ -1101,7 +1138,7 @@
     else if (SCpnt != NULL)
       SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
 
-    STp->at_sm &= (total != 0);
+    STps->at_sm &= (total == 0);
     return( total);
 }   
 
@@ -1116,6 +1153,7 @@
     Scsi_Cmnd * SCpnt = NULL;
     Scsi_Tape * STp;
     ST_mode * STm;
+    ST_partstat * STps;
     int dev = TAPE_NR(inode->i_rdev);
 
     STp = &(scsi_tapes[dev]);
@@ -1131,6 +1169,11 @@
     }
 #endif
 
+    if (STp->can_partitions &&
+	(total = update_partition(inode)) < 0)
+      return total;
+    STps = &(STp->ps[STp->partition]);
+
     if (STp->block_size == 0 &&
 	count > (STp->buffer)->buffer_size &&
 	!enlarge_buffer(STp->buffer, count, STp->restr_dma))
@@ -1141,17 +1184,17 @@
       return (-EIO);	/* Read must be integral number of blocks */
 
     if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
-	!st_int_ioctl(inode, filp, MTLOCK, 0))
+	!st_int_ioctl(inode, MTLOCK, 0))
       STp->door_locked = ST_LOCKED_AUTO;
 
-    if (STp->rw == ST_WRITING) {
+    if (STps->rw == ST_WRITING) {
       transfer = flush_buffer(inode, filp, 0);
       if (transfer)
 	return transfer;
-      STp->rw = ST_READING;
+      STps->rw = ST_READING;
     }
-    if (STp->moves_after_eof < 255)
-      STp->moves_after_eof++;
+    if (STps->moves_after_eof < 255)
+      STps->moves_after_eof++;
 
 #if DEBUG
     if (debugging && STp->eof != ST_NOEOF)
@@ -1162,7 +1205,7 @@
 	(STp->eof == ST_EOM_OK || STp->eof == ST_EOD))
       return (-EIO);  /* EOM or Blank Check */
 
-    STp->rw = ST_READING;
+    STps->rw = ST_READING;
 
     for (total = 0; total < count; ) {
 
@@ -1197,7 +1240,7 @@
 
 	(STp->buffer)->read_pointer = 0;
 	STp->eof_hit = 0;
-	STp->at_sm = 0;
+	STps->at_sm = 0;
 
 	if ((STp->buffer)->last_result_fatal) {
 #if DEBUG
@@ -1235,7 +1278,7 @@
 		    printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev);
 		    if (STp->drv_block >= 0)
 		      STp->drv_block += blks - transfer + 1;
-		    st_int_ioctl(inode, filp, MTBSR, 1);
+		    st_int_ioctl(inode, MTBSR, 1);
 		    return (-EIO);
 		  }
 		  /* We have some data, deliver it */
@@ -1249,7 +1292,7 @@
 		    count = total + (STp->buffer)->buffer_bytes;
 		  if (STp->drv_block >= 0)
 		    STp->drv_block += 1;
-		  if (st_int_ioctl(inode, filp, MTBSR, 1))
+		  if (st_int_ioctl(inode, MTBSR, 1))
 		    return (-EIO);
 		  SCpnt = NULL;
 		}
@@ -1291,7 +1334,7 @@
 	      STp->drv_block = (-1);
 	      if (total)
 		return total;
-	      else if (STp->moves_after_eof == 1 &&
+	      else if (STps->moves_after_eof == 1 &&
 		       (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) {
 #if DEBUG
 		if (debugging)
@@ -1348,8 +1391,8 @@
 	if (total == 0 && STp->eof == ST_FM) {
 	  STp->eof = ST_NOEOF;
 	  STp->drv_block = 0;
-	  if (STp->moves_after_eof > 1)
-	    STp->moves_after_eof = 0;
+	  if (STps->moves_after_eof > 1)
+	    STps->moves_after_eof = 0;
 	  if ((STp->mt_status)->mt_fileno >= 0)
 	    (STp->mt_status)->mt_fileno++;
 	}
@@ -1383,8 +1426,9 @@
 "st%d:    can bsr: %d, two FMs: %d, fast mteom: %d, auto lock: %d,\n",
 	 dev, STp->can_bsr, STp->two_fm, STp->fast_mteom, STp->do_auto_lock);
   printk(KERN_INFO
-"st%d:    defs for wr: %d, no block limits: %d\n",
-	 dev, STm->defaults_for_writes, STp->omit_blklims);
+"st%d:    defs for wr: %d, no block limits: %d, partitions: %d, s2 log: %d\n",
+	 dev, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
+	 STp->scsi2_logical);
 #if DEBUG
   printk(KERN_INFO
 	 "st%d:    debugging: %d\n",
@@ -1425,6 +1469,9 @@
     STp->do_auto_lock     = (options & MT_ST_AUTO_LOCK) != 0;
     STp->can_bsr          = (options & MT_ST_CAN_BSR) != 0;
     STp->omit_blklims	  = (options & MT_ST_NO_BLKLIMS) != 0;
+    if ((STp->device)->scsi_level >= SCSI_2)
+      STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0;
+    STp->scsi2_logical    = (options & MT_ST_SCSI2LOGICAL) != 0;
 #if DEBUG
     debugging = (options & MT_ST_DEBUGGING) != 0;
 #endif
@@ -1450,6 +1497,11 @@
       STp->can_bsr = value;
     if ((options & MT_ST_NO_BLKLIMS) != 0)
       STp->omit_blklims = value;
+    if ((STp->device)->scsi_level >= SCSI_2 &&
+	(options & MT_ST_CAN_PARTITIONS) != 0)
+      STp->can_partitions = value;
+    if ((options & MT_ST_SCSI2LOGICAL) != 0)
+      STp->scsi2_logical = value;
 #if DEBUG
     if ((options & MT_ST_DEBUGGING) != 0)
       debugging = value;
@@ -1541,6 +1593,9 @@
   unsigned char cmd[10];
   Scsi_Cmnd * SCpnt = NULL;
 
+  if (STp->ready != ST_READY)
+    return (-EIO);
+
   /* Read the current page contents */
   memset(cmd, 0, 10);
   cmd[0] = MODE_SENSE;
@@ -1549,6 +1604,8 @@
   cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
 
   SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], ST_TIMEOUT, 0);
+  if (SCpnt == NULL)
+    return (-EBUSY);
   dev = TAPE_NR(SCpnt->request.rq_dev);
 
   if ((STp->buffer)->last_result_fatal != 0) {
@@ -1614,24 +1671,26 @@
 
 /* Internal ioctl function */
 	static int
-st_int_ioctl(struct inode * inode,struct file * file,
+st_int_ioctl(struct inode * inode,
 	     unsigned int cmd_in, unsigned long arg)
 {
    int timeout = ST_LONG_TIMEOUT;
    long ltmp;
-   int ioctl_result;
+   int i, ioctl_result;
    unsigned char cmd[10];
    Scsi_Cmnd * SCpnt;
    Scsi_Tape * STp;
+   ST_partstat * STps;
    int fileno, blkno, at_sm, undone, datalen;
    int dev = TAPE_NR(inode->i_rdev);
 
    STp = &(scsi_tapes[dev]);
    if (STp->ready != ST_READY && cmd_in != MTLOAD)
      return (-EIO);
+   STps = &(STp->ps[STp->partition]);
    fileno = (STp->mt_status)->mt_fileno ;
    blkno = STp->drv_block;
-   at_sm = STp->at_sm;
+   at_sm = STps->at_sm;
 
    memset(cmd, 0, 10);
    datalen = 0;
@@ -1651,7 +1710,7 @@
        if (fileno >= 0)
 	 fileno += arg;
        blkno = 0;
-       at_sm &= (arg != 0);
+       at_sm &= (arg == 0);
        break; 
      case MTBSF:
      case MTBSFM:
@@ -1673,7 +1732,7 @@
        if (fileno >= 0)
 	 fileno -= arg;
        blkno = (-1);  /* We can't know the block number */
-       at_sm &= (arg != 0);
+       at_sm &= (arg == 0);
        break; 
      case MTFSR:
        cmd[0] = SPACE;
@@ -1688,7 +1747,7 @@
 #endif
        if (blkno >= 0)
 	 blkno += arg;
-       at_sm &= (arg != 0);
+       at_sm &= (arg == 0);
        break; 
      case MTBSR:
        cmd[0] = SPACE;
@@ -1707,7 +1766,7 @@
 #endif
        if (blkno >= 0)
 	 blkno -= arg;
-       at_sm &= (arg != 0);
+       at_sm &= (arg == 0);
        break; 
      case MTFSS:
        cmd[0] = SPACE;
@@ -1829,7 +1888,7 @@
      case MTEOM:
        if (!STp->fast_mteom) {
 	 /* space to the end of tape */
-	 ioctl_result = st_int_ioctl(inode, file, MTFSF, 0x3fff);
+	 ioctl_result = st_int_ioctl(inode, MTFSF, 0x3fff);
 	 fileno = (STp->mt_status)->mt_fileno ;
 	 if (STp->eof == ST_EOD || STp->eof == ST_EOM_OK)
 	   return 0;
@@ -1882,33 +1941,6 @@
 	 printk(ST_DEB_MSG "st%d: Unlocking drive door.\n", dev);
 #endif;
        break;
-     case MTSEEK:
-       if ((STp->device)->scsi_level < SCSI_2) {
-	 cmd[0] = QFA_SEEK_BLOCK;
-	 cmd[2] = (arg >> 16);
-	 cmd[3] = (arg >> 8);
-	 cmd[4] = arg;
-	 cmd[5] = 0;
-       }
-       else {
-	 cmd[0] = SEEK_10;
-	 cmd[1] = 4;
-	 cmd[3] = (arg >> 24);
-	 cmd[4] = (arg >> 16);
-	 cmd[5] = (arg >> 8);
-	 cmd[6] = arg;
-       }
-#if ST_NOWAIT
-       cmd[1] |= 1;  /* Don't wait for completion */
-       timeout = ST_TIMEOUT;
-#endif
-#if DEBUG
-       if (debugging)
-	 printk(ST_DEB_MSG "st%d: Seeking tape to block %ld.\n", dev, arg);
-#endif
-       fileno = blkno = (-1);
-       at_sm = 0;
-       break;
      case MTSETBLK:  /* Set block length */
      case MTSETDENSITY: /* Set tape density */
      case MTSETDRVBUFFER: /* Set drive buffering */
@@ -1981,27 +2013,21 @@
    SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
 
    if (cmd_in == MTFSF)
-     STp->moves_after_eof = 0;
-   else
-     STp->moves_after_eof = 1;
+     STps->moves_after_eof = 0;
+   else if (cmd_in != MTLOAD)
+     STps->moves_after_eof = 1;
    if (!ioctl_result) {  /* SCSI command successful */
-     if (cmd_in != MTSEEK) {
-       STp->drv_block = blkno;
-       (STp->mt_status)->mt_fileno = fileno;
-       STp->at_sm = at_sm;
-     }
-     else {
-       STp->drv_block = (STp->mt_status)->mt_fileno = (-1);
-       STp->at_sm = 0;
-     }
+     STp->drv_block = blkno;
+     (STp->mt_status)->mt_fileno = fileno;
+     STps->at_sm = at_sm;
      if (cmd_in == MTLOCK)
        STp->door_locked = ST_LOCKED_EXPLICIT;
      else if (cmd_in == MTUNLOCK)
        STp->door_locked = ST_UNLOCKED;
      if (cmd_in == MTBSFM)
-       ioctl_result = st_int_ioctl(inode, file, MTFSF, 1);
+       ioctl_result = st_int_ioctl(inode, MTFSF, 1);
      else if (cmd_in == MTFSFM)
-       ioctl_result = st_int_ioctl(inode, file, MTBSF, 1);
+       ioctl_result = st_int_ioctl(inode, MTBSF, 1);
      else if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
        STp->block_size = arg & MT_ST_BLKSIZE_MASK;
        if (STp->block_size != 0)
@@ -2025,8 +2051,15 @@
        STp->density = arg >> MT_ST_DENSITY_SHIFT;
      if (cmd_in == MTOFFL || cmd_in == MTUNLOAD)
        STp->rew_at_close = 0;
-     else if (cmd_in == MTLOAD)
+     else if (cmd_in == MTLOAD) {
        STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
+       for (i=0; i < ST_NBR_PARTITIONS; i++) {
+	 STp->ps[i].rw = ST_IDLE;
+	 STp->ps[i].moves_after_eof = 1;
+	 STp->ps[i].last_block_valid = FALSE;
+       }
+       STp->partition = 0;
+     }
    } else {  /* SCSI command was not completely successful */
      if (SCpnt->sense_buffer[2] & 0x40) {
        if (cmd_in != MTBSF && cmd_in != MTBSFM &&
@@ -2075,7 +2108,7 @@
 	   STp->drv_block = (-1);
        }
      }
-     else if (cmd_in == MTEOM || cmd_in == MTSEEK) {
+     else if (cmd_in == MTEOM) {
        (STp->mt_status)->mt_fileno = (-1);
        STp->drv_block = (-1);
      }
@@ -2090,19 +2123,351 @@
 }
 
 
+/* Get the tape position. If bt == 2, arg points into a kernel space mt_loc
+    structure. */
+
+	static int
+get_location(struct inode * inode, unsigned int *block, int *partition,
+	     int logical)
+{
+    Scsi_Tape *STp;
+    int dev = TAPE_NR(inode->i_rdev);
+    int result;
+    unsigned char scmd[10];
+    Scsi_Cmnd *SCpnt;
+
+    STp = &(scsi_tapes[dev]);
+    if (STp->ready != ST_READY)
+      return (-EIO);
+
+    memset (scmd, 0, 10);
+    if ((STp->device)->scsi_level < SCSI_2) {
+      scmd[0] = QFA_REQUEST_BLOCK;
+      scmd[4] = 3;
+    }
+    else {
+      scmd[0] = READ_POSITION;
+      if (!logical && !STp->scsi2_logical)
+	scmd[1] = 1;
+    }
+    SCpnt = st_do_scsi(NULL, STp, scmd, 20, ST_TIMEOUT, MAX_READY_RETRIES);
+    if (!SCpnt)
+      return (-EBUSY);
+
+    if ((STp->buffer)->last_result_fatal != 0 ||
+	((STp->buffer)->b_data[0] & 4)) {
+      *block = *partition = 0;
+#if DEBUG
+      if (debugging)
+	printk(ST_DEB_MSG "st%d: Can't read tape position.\n", dev);
+#endif
+      result = (-EIO);
+    }
+    else {
+      result = 0;
+      if ((STp->device)->scsi_level < SCSI_2) {
+	*block = ((STp->buffer)->b_data[0] << 16) 
+	+ ((STp->buffer)->b_data[1] << 8) 
+	+ (STp->buffer)->b_data[2];
+	*partition = 0;
+      }
+      else {
+	*block = ((STp->buffer)->b_data[4] << 24)
+	  + ((STp->buffer)->b_data[5] << 16) 
+	  + ((STp->buffer)->b_data[6] << 8) 
+	  + (STp->buffer)->b_data[7];
+	*partition = (STp->buffer)->b_data[1];
+	if (((STp->buffer)->b_data[0] & 0x80) &&
+	    (STp->buffer)->b_data[1] == 0) /* BOP of partition 0 */
+	  STp->drv_block = (STp->mt_status)->mt_fileno = 0;
+      }
+#if DEBUG
+      if (debugging)
+	printk(ST_DEB_MSG "st%d: Got tape pos. blk %d part %d.\n", dev,
+	       *block, *partition);
+#endif
+
+    }
+    SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
+
+    return result;
+}
+
+
+/* Set the tape block and partition. Negative partition means that only the
+   block should be set in vendor specific way. */
+	static int
+set_location(struct inode * inode, unsigned int block, int partition,
+	     int logical)
+{
+    Scsi_Tape *STp;
+    ST_partstat *STps;
+    int dev = TAPE_NR(inode->i_rdev);
+    int result, p;
+    unsigned int blk;
+    int timeout = ST_LONG_TIMEOUT;
+    unsigned char scmd[10];
+    Scsi_Cmnd *SCpnt;
+
+    STp = &(scsi_tapes[dev]);
+    if (STp->ready != ST_READY)
+      return (-EIO);
+    STps = &(STp->ps[STp->partition]);
+
+#if DEBUG
+    if (debugging)
+      printk(ST_DEB_MSG "st%d: Setting block to %d and partition to %d.\n",
+	     dev, block, partition);
+    if (partition < 0)
+      return (-EIO);
+#endif
+
+    /* Update the location at the partition we are leaving */
+    if ((!STp->can_partitions && partition != 0) ||
+	partition >= ST_NBR_PARTITIONS)
+      return (-EINVAL);
+    if (partition != STp->partition) {
+      if (get_location(inode, &blk, &p, 1))
+	STps->last_block_valid = FALSE;
+      else {
+	STps->last_block_valid = TRUE;
+	STps->last_block_visited = blk;
+#if DEBUG
+	if (debugging)
+	  printk(ST_DEB_MSG "st%d: Visited block %d for partition %d saved.\n",
+		 dev, blk, STp->partition);
+#endif
+      }
+    }
+
+    memset (scmd, 0, 10);
+    if ((STp->device)->scsi_level < SCSI_2) {
+      scmd[0] = QFA_SEEK_BLOCK;
+      scmd[2] = (block >> 16);
+      scmd[3] = (block >> 8);
+      scmd[4] = block;
+      scmd[5] = 0;
+    }
+    else {
+      scmd[0] = SEEK_10;
+      scmd[3] = (block >> 24);
+      scmd[4] = (block >> 16);
+      scmd[5] = (block >> 8);
+      scmd[6] = block;
+      if (!logical && !STp->scsi2_logical)
+	scmd[1] = 4;
+      if (STp->partition != partition) {
+	scmd[1] |= 2;
+	scmd[8] = partition;
+#if DEBUG
+	if (debugging)
+	  printk(ST_DEB_MSG "st%d: Trying to change partition from %d to %d\n",
+		 dev, STp->partition, partition);
+#endif
+      }
+     }
+#if ST_NOWAIT
+    scmd[1] |= 1;  /* Don't wait for completion */
+    timeout = ST_TIMEOUT;
+#endif
+
+    SCpnt = st_do_scsi(NULL, STp, scmd, 20, timeout, MAX_READY_RETRIES);
+    if (!SCpnt)
+      return (-EBUSY);
+
+    STp->drv_block = (STp->mt_status)->mt_fileno = (-1);
+    if ((STp->buffer)->last_result_fatal != 0) {
+      result = (-EIO);
+      if (STp->can_partitions &&
+	  (STp->device)->scsi_level >= SCSI_2 &&
+	  (p = find_partition(inode)) >= 0)
+	STp->partition = p;
+    }
+    else {
+      if (STp->can_partitions) {
+	STp->partition = partition;
+	STps = &(STp->ps[partition]);
+	if (!STps->last_block_valid ||
+	    STps->last_block_visited != block) {
+	  STps->moves_after_eof = 1;
+	  STps->at_sm = 0;
+	  STps->rw = ST_IDLE;
+	}
+      }
+      else
+	STps->at_sm = 0;
+      if (block == 0)
+	STp->drv_block = (STp->mt_status)->mt_fileno = 0;
+      result = 0;
+    }
+    SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
+
+    return result;
+}
+
+
+/* Find the current partition number for the drive status. Called from open and
+   returns either partition number of negative error code. */
+	static int
+find_partition(struct inode *inode)
+{
+    int i, partition;
+    unsigned int block;
+
+    if ((i = get_location(inode, &block, &partition, 1)) < 0)
+      return i;
+    if (partition >= ST_NBR_PARTITIONS)
+      return (-EIO);
+    return partition;
+}
+
+
+/* Change the partition if necessary */
+	static int
+update_partition(struct inode * inode)
+{
+    int dev = TAPE_NR(inode->i_rdev);
+    Scsi_Tape *STp;
+    ST_partstat *STps;
+
+    STp = &(scsi_tapes[dev]);
+    if (STp->partition == STp->new_partition)
+      return 0;
+    STps = &(STp->ps[STp->new_partition]);
+    if (!STps->last_block_valid)
+      STps->last_block_visited = 0;
+    return set_location(inode, STps->last_block_visited, STp->new_partition, 1);
+}
+
+/* Functions for reading and writing the medium partition mode page. These
+   seem to work with Wangtek 6200HS and HP C1533A. */
+
+#define PART_PAGE   0x11
+#define PART_PAGE_LENGTH 10
+
+/* Get the number of partitions on the tape. As a side effect reads the
+   mode page into the tape buffer. */
+	static int
+nbr_partitions(struct inode * inode)
+{
+    int dev = TAPE_NR(inode->i_rdev), result;
+    Scsi_Tape *STp;
+    Scsi_Cmnd * SCpnt = NULL;
+    unsigned char cmd[10];
+
+    STp = &(scsi_tapes[dev]);
+    if (STp->ready != ST_READY)
+      return (-EIO);
+
+    memset ((void *) &cmd[0], 0, 10);
+    cmd[0] = MODE_SENSE;
+    cmd[1] = 8;   /* Page format */
+    cmd[2] = PART_PAGE;
+    cmd[4] = 200;
+
+    SCpnt = st_do_scsi(SCpnt, STp, cmd, 200, ST_TIMEOUT, MAX_READY_RETRIES);
+    if (SCpnt == NULL)
+      return (-EBUSY);
+    SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
+
+    if ((STp->buffer)->last_result_fatal != 0) {
+#if DEBUG
+      if (debugging)
+	printk(ST_DEB_MSG "st%d: Can't read medium partition page.\n", dev);
+#endif
+      result = (-EIO);
+    }
+    else {
+      result = (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] + 1;
+#if DEBUG
+      if (debugging)
+	printk(ST_DEB_MSG "st%d: Number of partitions %d.\n", dev, result);
+#endif
+    }
+
+    return result;
+}
+
+
+/* Partition the tape into two partitions if size > 0 or one partition if
+   size == 0 */
+	static int
+partition_tape(struct inode * inode, int size)
+{
+    int dev = TAPE_NR(inode->i_rdev), result;
+    int length;
+    Scsi_Tape *STp;
+    Scsi_Cmnd * SCpnt = NULL;
+    unsigned char cmd[10], *bp;
+
+    if ((result = nbr_partitions(inode)) < 0)
+      return result;
+    STp = &(scsi_tapes[dev]);
+
+    /* The mode page is in the buffer. Let's modify it and write it. */
+    bp = &((STp->buffer)->b_data[0]);
+    if (size <= 0) {
+      length = 8;
+      bp[MODE_HEADER_LENGTH + 3] = 0;
+#if DEBUG
+      if (debugging)
+	printk(ST_DEB_MSG "st%d: Formatting tape with one partition.\n", dev);
+#endif
+    }
+    else {
+      length = 10;
+      bp[MODE_HEADER_LENGTH + 3] = 1;
+      bp[MODE_HEADER_LENGTH + 8] = (size >> 8) & 0xff;
+      bp[MODE_HEADER_LENGTH + 9] = size & 0xff;
+#if DEBUG
+      if (debugging)
+	printk(ST_DEB_MSG "st%d: Formatting tape with two partition (1 = %d MB).\n",
+	       dev, size);
+#endif
+    }
+    bp[MODE_HEADER_LENGTH + 6] = 0;
+    bp[MODE_HEADER_LENGTH + 7] = 0;
+    bp[MODE_HEADER_LENGTH + 4] = 0x30;   /* IDP | PSUM = MB */
+
+    bp[0] = 0;
+    bp[1] = 0;
+    bp[MODE_HEADER_LENGTH] &= 0x3f;
+    bp[MODE_HEADER_LENGTH + 1] = length - 2;
+
+    memset(cmd, 0, 10);
+    cmd[0] = MODE_SELECT;
+    cmd[1] = 0x10;
+    cmd[4] = length + MODE_HEADER_LENGTH;
+
+    SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], ST_LONG_TIMEOUT, MAX_READY_RETRIES);
+    if (SCpnt == NULL)
+      return (-EBUSY);
+    SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
+
+    if ((STp->buffer)->last_result_fatal != 0) {
+      printk(KERN_INFO "st%d: Partitioning of tape failed.\n", dev);
+      result = (-EIO);
+    }
+    else
+      result = 0;
+
+    return result;
+}
+
+
 
 /* The ioctl command */
 	static int
 st_ioctl(struct inode * inode,struct file * file,
 	 unsigned int cmd_in, unsigned long arg)
 {
-   int i, cmd_nr, cmd_type, result;
+   int i, cmd_nr, cmd_type, bt;
+   unsigned int blk;
    struct mtop mtc;
    struct mtpos mt_pos;
-   unsigned char scmd[10];
-   Scsi_Cmnd *SCpnt;
    Scsi_Tape *STp;
    ST_mode *STm;
+   ST_partstat *STps;
    int dev = TAPE_NR(inode->i_rdev);
 
    STp = &(scsi_tapes[dev]);
@@ -2113,18 +2478,11 @@
    }
 #endif
    STm = &(STp->modes[STp->current_mode]);
+   STps = &(STp->ps[STp->partition]);
 
-   /*
-    * If this is something intended for the lower layer, just pass it
-    * through.
-    */
-   if( cmd_in == SCSI_IOCTL_GET_IDLUN || cmd_in == SCSI_IOCTL_PROBE_HOST )
-     {
-       return scsi_ioctl(STp->device, cmd_in, (void *) arg);
-     }
-   
    cmd_type = _IOC_TYPE(cmd_in);
    cmd_nr   = _IOC_NR(cmd_in);
+
    if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
      if (_IOC_SIZE(cmd_in) != sizeof(mtc))
        return (-EINVAL);
@@ -2180,7 +2538,7 @@
        STp->device->was_reset = 0;
        if (STp->door_locked != ST_UNLOCKED &&
 	   STp->door_locked != ST_LOCK_FAILS) {
-	 if (st_int_ioctl(inode, file, MTLOCK, 0)) {
+	 if (st_int_ioctl(inode, MTLOCK, 0)) {
 	   printk(KERN_NOTICE "st%d: Could not relock door after bus reset.\n",
 		  dev);
 	   STp->door_locked = ST_UNLOCKED;
@@ -2190,24 +2548,69 @@
 
      if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
 	 mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
-	 mtc.mt_op != MTSETDRVBUFFER)
-       STp->rw = ST_IDLE;  /* Prevent automatic WEOF */
+	 mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSEEK &&
+	 mtc.mt_op != MTSETPART)
+       STps->rw = ST_IDLE;  /* Prevent automatic WEOF */
 
      if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
-       st_int_ioctl(inode, file, MTUNLOCK, 0);  /* Ignore result! */
+       st_int_ioctl(inode, MTUNLOCK, 0);  /* Ignore result! */
 
      if (mtc.mt_op == MTSETDRVBUFFER &&
 	 (mtc.mt_count & MT_ST_OPTIONS) != 0)
        return st_set_options(inode, mtc.mt_count);
-     else if (mtc.mt_op == MTCOMPRESSION)
+     if (mtc.mt_op == MTSETPART) {
+       if (!STp->can_partitions ||
+	   mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS)
+	 return (-EINVAL);
+       if (mtc.mt_count >= STp->nbr_partitions &&
+	   (STp->nbr_partitions = nbr_partitions(inode)) < 0)
+	 return (-EIO);
+       if (mtc.mt_count >= STp->nbr_partitions)
+	 return (-EINVAL);
+       STp->new_partition = mtc.mt_count;
+       return 0;
+     }
+     if (mtc.mt_op == MTMKPART) {
+       if (!STp->can_partitions)
+	 return (-EINVAL);
+       if ((i = st_int_ioctl(inode, MTREW, 0)) < 0 ||
+	   (i = partition_tape(inode, mtc.mt_count)) < 0)
+	 return i;
+       for (i=0; i < ST_NBR_PARTITIONS; i++) {
+	 STp->ps[i].rw = ST_IDLE;
+	 STp->ps[i].moves_after_eof = 1;
+	 STp->ps[i].at_sm = 0;
+	 STp->ps[i].last_block_valid = FALSE;
+       }
+       STp->partition = STp->new_partition = 0;
+       STp->nbr_partitions = 1;  /* Bad guess ?-) */
+       STp->drv_block = (STp->mt_status)->mt_fileno = 0;
+       return 0;
+     }
+     if (mtc.mt_op == MTSEEK) {
+       i = set_location(inode, mtc.mt_count, STp->new_partition, 0);
+       if (!STp->can_partitions)
+	   STp->ps[0].rw = ST_IDLE;
+       return i;
+     }
+     if (STp->can_partitions && STp->ready == ST_READY &&
+	 (i = update_partition(inode)) < 0)
+       return i;
+     if (mtc.mt_op == MTCOMPRESSION)
        return st_compression(STp, (mtc.mt_count & 1));
      else
-       return st_int_ioctl(inode, file, mtc.mt_op, mtc.mt_count);
+       return st_int_ioctl(inode, mtc.mt_op, mtc.mt_count);
    }
 
    if (!STm->defined)
      return (-ENXIO);
 
+   if ((i = flush_buffer(inode, file, FALSE)) < 0)
+     return i;
+   if (STp->can_partitions &&
+       (i = update_partition(inode)) < 0)
+     return i;
+
    if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
 
      if (_IOC_SIZE(cmd_in) != sizeof(struct mtget))
@@ -2216,19 +2619,15 @@
      if (i)
        return i;
 
-     i = flush_buffer(inode, file, FALSE);
-     if (i < 0)
-       return i;
-
      (STp->mt_status)->mt_dsreg =
        ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
        ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
      (STp->mt_status)->mt_blkno = STp->drv_block;
      if (STp->block_size != 0) {
-       if (STp->rw == ST_WRITING)
+       if (STps->rw == ST_WRITING)
 	 (STp->mt_status)->mt_blkno +=
 	   (STp->buffer)->buffer_bytes / STp->block_size;
-       else if (STp->rw == ST_READING)
+       else if (STps->rw == ST_READING)
 	 (STp->mt_status)->mt_blkno -= ((STp->buffer)->buffer_bytes +
 	   STp->block_size - 1) / STp->block_size;
      }
@@ -2242,6 +2641,7 @@
        else
 	 (STp->mt_status)->mt_gstat |= GMT_EOF(0xffffffff);
      }
+     (STp->mt_status)->mt_resid = STp->partition;
      if (STp->eof == ST_EOM_OK || STp->eof == ST_EOM_ERROR)
        (STp->mt_status)->mt_gstat |= GMT_EOT(0xffffffff);
      else if (STp->eof == ST_EOD)
@@ -2256,7 +2656,7 @@
        (STp->mt_status)->mt_gstat |= GMT_ONLINE(0xffffffff);
      if (STp->ready == ST_NO_TAPE)
        (STp->mt_status)->mt_gstat |= GMT_DR_OPEN(0xffffffff);
-     if (STp->at_sm)
+     if (STps->at_sm)
        (STp->mt_status)->mt_gstat |= GMT_SM(0xffffffff);
 
      memcpy_tofs((char *)arg, (char *)(STp->mt_status),
@@ -2264,67 +2664,22 @@
 
      (STp->mt_status)->mt_erreg = 0;  /* Clear after read */
      return 0;
-   }
-   else if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
-     if (STp->ready != ST_READY)
-       return (-EIO);
-#if DEBUG
-     if (debugging)
-       printk(ST_DEB_MSG "st%d: get tape position.\n", dev);
-#endif
+   } /* End of MTIOCGET */
+
+   if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
      if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos))
        return (-EINVAL);
-
-     i = flush_buffer(inode, file, 0);
-     if (i < 0)
+     if ((i = get_location(inode, &blk, &bt, 0)) < 0)
        return i;
-
-     i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct mtpos));
+     i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(mt_pos));
      if (i)
-       return i;
-
-     memset (scmd, 0, 10);
-     if ((STp->device)->scsi_level < SCSI_2) {
-       scmd[0] = QFA_REQUEST_BLOCK;
-       scmd[4] = 3;
-     }
-     else {
-       scmd[0] = READ_POSITION;
-       scmd[1] = 1;
-     }
-     SCpnt = st_do_scsi(NULL, STp, scmd, 20, ST_TIMEOUT, MAX_READY_RETRIES);
-     if (!SCpnt)
-       return (-EBUSY);
-
-     if ((STp->buffer)->last_result_fatal != 0) {
-       mt_pos.mt_blkno = (-1);
-#if DEBUG
-       if (debugging)
-	 printk(ST_DEB_MSG "st%d: Can't read tape position.\n", dev);
-#endif
-       result = (-EIO);
-     }
-     else {
-       result = 0;
-       if ((STp->device)->scsi_level < SCSI_2)
-	 mt_pos.mt_blkno = ((STp->buffer)->b_data[0] << 16) 
-	   + ((STp->buffer)->b_data[1] << 8) 
-	     + (STp->buffer)->b_data[2];
-       else
-	 mt_pos.mt_blkno = ((STp->buffer)->b_data[4] << 24)
-	   + ((STp->buffer)->b_data[5] << 16) 
-	     + ((STp->buffer)->b_data[6] << 8) 
-	       + (STp->buffer)->b_data[7];
-
-     }
-
-     SCpnt->request.rq_status = RQ_INACTIVE;  /* Mark as not busy */
-
+	return i;
+     mt_pos.mt_blkno = blk;
      memcpy_tofs((char *)arg, (char *) (&mt_pos), sizeof(struct mtpos));
-     return result;
+     return 0;
    }
-   else
-     return scsi_ioctl(STp->device, cmd_in, (void *) arg);
+
+   return scsi_ioctl(STp->device, cmd_in, (void *) arg);
 }
 
 
@@ -2464,6 +2819,8 @@
 
 static int st_attach(Scsi_Device * SDp){
    Scsi_Tape * tpnt;
+   ST_mode * STm;
+   ST_partstat * STps;
    int i;
 
    if(SDp->type != TYPE_TAPE) return 1;
@@ -2487,7 +2844,6 @@
 
    tpnt->devt = MKDEV(SCSI_TAPE_MAJOR, i);
    tpnt->dirty = 0;
-   tpnt->rw = ST_IDLE;
    tpnt->eof = ST_NOEOF;
    tpnt->waiting = NULL;
    tpnt->in_use = 0;
@@ -2496,25 +2852,38 @@
    tpnt->density = 0;
    tpnt->do_auto_lock = ST_AUTO_LOCK;
    tpnt->can_bsr = ST_IN_FILE_POS;
+   tpnt->can_partitions = 0;
    tpnt->two_fm = ST_TWO_FM;
    tpnt->fast_mteom = ST_FAST_MTEOM;
+   tpnt->scsi2_logical = 0;
    tpnt->write_threshold = st_write_threshold;
    tpnt->default_drvbuffer = 0xff; /* No forced buffering */
+   tpnt->partition = 0;
+   tpnt->new_partition = 0;
+   tpnt->nbr_partitions = 0;
    tpnt->drv_block = (-1);
-   tpnt->moves_after_eof = 1;
-   tpnt->at_sm = 0;
    (tpnt->mt_status)->mt_fileno = (tpnt->mt_status)->mt_blkno = (-1);
 
    for (i=0; i < ST_NBR_MODES; i++) {
-     tpnt->modes[i].defined = FALSE;
-     tpnt->modes[i].defaults_for_writes = 0;
-     tpnt->modes[i].do_async_writes = ST_ASYNC_WRITES;
-     tpnt->modes[i].do_buffer_writes = ST_BUFFER_WRITES;
-     tpnt->modes[i].do_read_ahead = ST_READ_AHEAD;
-     tpnt->modes[i].default_compression = ST_DONT_TOUCH;
-     tpnt->modes[i].default_blksize = (-1);  /* No forced size */
-     tpnt->modes[i].default_density = (-1);  /* No forced density */
+     STm = &(tpnt->modes[i]);
+     STm->defined = FALSE;
+     STm->defaults_for_writes = 0;
+     STm->do_async_writes = ST_ASYNC_WRITES;
+     STm->do_buffer_writes = ST_BUFFER_WRITES;
+     STm->do_read_ahead = ST_READ_AHEAD;
+     STm->default_compression = ST_DONT_TOUCH;
+     STm->default_blksize = (-1);  /* No forced size */
+     STm->default_density = (-1);  /* No forced density */
    }
+
+   for (i=0; i < ST_NBR_PARTITIONS; i++) {
+     STps = &(tpnt->ps[i]);
+     STps->rw = ST_IDLE;
+     STps->moves_after_eof = 1;
+     STps->at_sm = 0;
+     STps->last_block_valid = FALSE;
+   }
+
    tpnt->current_mode = 0;
    tpnt->modes[0].defined = TRUE;
 
@@ -2529,7 +2898,8 @@
 {
   if(SDp->type != TYPE_TAPE) return 0;
 
-  printk("Detected scsi tape st%d at scsi%d, channel %d, id %d, lun %d\n", 
+  printk(KERN_INFO
+	 "Detected scsi tape st%d at scsi%d, channel %d, id %d, lun %d\n", 
 	 st_template.dev_noticed++,
 	 SDp->host->host_no, SDp->channel, SDp->id, SDp->lun); 
   
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/st.h linux/drivers/scsi/st.h
--- v1.3.97/linux/drivers/scsi/st.h	Tue Apr 23 13:57:10 1996
+++ linux/drivers/scsi/st.h	Fri May  3 10:12:16 1996
@@ -43,6 +43,17 @@
 #define ST_MODE_SHIFT (7 - ST_NBR_MODE_BITS)
 #define ST_MODE_MASK ((ST_NBR_MODES - 1) << ST_MODE_SHIFT)
 
+/* The status related to each partition */
+typedef struct {
+  unsigned char rw;
+  unsigned char moves_after_eof;
+  unsigned char at_sm;
+  unsigned char last_block_valid;
+  u32 last_block_visited;
+} ST_partstat;
+
+#define ST_NBR_PARTITIONS 4
+
 /* The tape drive descriptor */
 typedef struct {
   kdev_t devt;
@@ -57,9 +68,11 @@
   unsigned char omit_blklims;
   unsigned char do_auto_lock;
   unsigned char can_bsr;
+  unsigned char can_partitions;
   unsigned char two_fm;
   unsigned char fast_mteom;
   unsigned char restr_dma;
+  unsigned char scsi2_logical;
   unsigned char default_drvbuffer;  /* 0xff = don't touch, value 3 bits */
   int write_threshold;
 
@@ -68,16 +81,17 @@
   int current_mode;
 
   /* Status variables */
+  int partition;
+  int new_partition;
+  int nbr_partitions;    /* zero until partition support enabled */
+  ST_partstat ps[ST_NBR_PARTITIONS];
   unsigned char dirty;
-  unsigned char rw;
   unsigned char ready;
   unsigned char eof;
   unsigned char write_prot;
   unsigned char drv_write_prot;
   unsigned char in_use;
   unsigned char eof_hit;
-  unsigned char moves_after_eof;
-  unsigned char at_sm;
   unsigned char blksize_changed;
   unsigned char density_changed;
   unsigned char compression_changed;
diff -u --recursive --new-file v1.3.97/linux/drivers/scsi/wd33c93.c linux/drivers/scsi/wd33c93.c
--- v1.3.97/linux/drivers/scsi/wd33c93.c	Sat Apr 27 15:19:57 1996
+++ linux/drivers/scsi/wd33c93.c	Sat May  4 19:39:23 1996
@@ -31,7 +31,7 @@
  *          will benefit from this. The driver defaults to what I'm
  *          'adaptive disconnect' - meaning that each command is
  *          evaluated individually as to whether or not it should
- *          be run with the option to disconnect/reslect (if the
+ *          be run with the option to disconnect/reselect (if the
  *          device chooses), or as a "SCSI-bus-hog".
  *
  *    -  Synchronous data transfers are now supported. Because of
@@ -121,7 +121,7 @@
  * -  nosync:bitmask -bitmask is a byte where the 1st 7 bits correspond with
  *                    the 7 possible SCSI devices. Set a bit to prevent sync
  *                    negotiation on that device. To maintain backwards
- *                    compatability, a command-line such as "wd33c93=255" will
+ *                    compatibility, a command-line such as "wd33c93=255" will
  *                    be automatically translated to "wd33c93=nosync:0xff".
  * -  period:ns      -ns is the minimum # of nanoseconds in a SCSI data transfer
  *                    period. Default is 500; acceptable values are 250 - 1000.
diff -u --recursive --new-file v1.3.97/linux/fs/Config.in linux/fs/Config.in
--- v1.3.97/linux/fs/Config.in	Sat Apr 27 15:19:57 1996
+++ linux/fs/Config.in	Tue Apr 30 12:42:37 1996
@@ -5,7 +5,7 @@
 comment 'Filesystems'
 
 bool	 'Quota support' CONFIG_QUOTA
-tristate 'Standard (minix) fs support' CONFIG_MINIX_FS
+tristate 'Minix fs support' CONFIG_MINIX_FS
 tristate 'Extended fs support' CONFIG_EXT_FS
 tristate 'Second extended fs support' CONFIG_EXT2_FS
 tristate 'xiafs filesystem support' CONFIG_XIA_FS
diff -u --recursive --new-file v1.3.97/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.97/linux/fs/buffer.c	Sat Apr 27 15:19:57 1996
+++ linux/fs/buffer.c	Sat May  4 09:27:58 1996
@@ -721,11 +721,17 @@
 	
 	/* Too bad, that was not enough. Try a little harder to grow some. */
 	
-	if (grow_buffers(GFP_ATOMIC, size)) {
-                needed -= PAGE_SIZE;
-		goto repeat0;
+	if (nr_free_pages > min_free_pages + 5) {
+		if (grow_buffers(GFP_BUFFER, size)) {
+	                needed -= PAGE_SIZE;
+			goto repeat0;
+		};
 	}
-	wakeup_bdflush(1);
+	
+	/* and repeat until we find something good */
+	if (!grow_buffers(GFP_ATOMIC, size))
+		wakeup_bdflush(1);
+	needed -= PAGE_SIZE;
 	goto repeat0;
 }
 
@@ -1062,11 +1068,11 @@
 static struct buffer_head * create_buffers(unsigned long page, unsigned long size)
 {
 	struct buffer_head *bh, *head;
-	unsigned long offset;
+	long offset;
 
 	head = NULL;
 	offset = PAGE_SIZE;
-	while ((offset -= size) < PAGE_SIZE) {
+	while ((offset -= size) >= 0) {
 		bh = get_unused_buffer_head();
 		if (!bh)
 			goto no_grow;
@@ -1222,21 +1228,18 @@
 {
 	if (on) {
 		struct buffer_head *tmp = bh;
-		int page_uptodate = 1;
 		set_bit(BH_Uptodate, &bh->b_state);
 		/* If a page has buffers and all these buffers are uptodate,
 		 * then the page is uptodate. */
 		do {
-			if (!test_bit(BH_Uptodate, &tmp->b_state)) {
-				page_uptodate = 0;
-				break;
-			}
+			if (!test_bit(BH_Uptodate, &tmp->b_state))
+				return;
 			tmp=tmp->b_this_page;
 		} while (tmp && tmp != bh);
-		if (page_uptodate)
-			set_bit(PG_uptodate, &mem_map[MAP_NR(bh->b_data)].flags);
-	} else
-		clear_bit(BH_Uptodate, &bh->b_state);
+		set_bit(PG_uptodate, &mem_map[MAP_NR(bh->b_data)].flags);
+		return;
+	}
+	clear_bit(BH_Uptodate, &bh->b_state);
 }
 
 /*
@@ -1360,9 +1363,9 @@
 		else
 			break;
 	}
+	tmp->b_this_page = bh;
 	free_list[isize] = bh;
 	mem_map[MAP_NR(page)].buffers = bh;
-	tmp->b_this_page = bh;
 	buffermem += PAGE_SIZE;
 	return 1;
 }
@@ -1818,14 +1821,17 @@
 	int isize = BUFSIZE_INDEX(BLOCK_SIZE);
 	long memsize = MAP_NR(high_memory) << PAGE_SHIFT;
 
-	if (memsize >= 4*1024*1024) {
-		if(memsize >= 16*1024*1024)
-			 nr_hash = 16381;
-		else
-			 nr_hash = 4093;
-	} else {
-		nr_hash = 997;
-	};
+	if (memsize >= 64*1024*1024)
+		nr_hash = 65521;
+	else if (memsize >= 32*1024*1024)
+		nr_hash = 32749;
+	else if (memsize >= 16*1024*1024)
+		nr_hash = 16381;
+	else if (memsize >= 8*1024*1024)
+		nr_hash = 8191;
+	else if (memsize >= 4*1024*1024)
+		nr_hash = 4093;
+	else nr_hash = 997;
 	
 	hash_table = (struct buffer_head **) vmalloc(nr_hash * 
 						     sizeof(struct buffer_head *));
diff -u --recursive --new-file v1.3.97/linux/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- v1.3.97/linux/fs/ext2/ialloc.c	Mon Feb 26 08:36:27 1996
+++ linux/fs/ext2/ialloc.c	Sat May  4 10:06:18 1996
@@ -151,41 +151,6 @@
 	return 0;
 }
 
-/*
- * This function sets the deletion time for the inode
- *
- * This may be used one day by an 'undelete' program
- */
-static void set_inode_dtime (struct inode * inode,
-			     struct ext2_group_desc * gdp)
-{
-	unsigned long inode_block;
-	struct buffer_head * bh;
-	struct ext2_inode * raw_inode;
-
-	inode_block = gdp->bg_inode_table + (((inode->i_ino - 1) %
-			EXT2_INODES_PER_GROUP(inode->i_sb)) /
-			EXT2_INODES_PER_BLOCK(inode->i_sb));
-	bh = bread (inode->i_sb->s_dev, inode_block, inode->i_sb->s_blocksize);
-	if (!bh)
-		ext2_panic (inode->i_sb, "set_inode_dtime",
-			    "Cannot load inode table block - "
-			    "inode=%lu, inode_block=%lu",
-			    inode->i_ino, inode_block);
-	raw_inode = ((struct ext2_inode *) bh->b_data) +
-			(((inode->i_ino - 1) %
-			EXT2_INODES_PER_GROUP(inode->i_sb)) %
-			EXT2_INODES_PER_BLOCK(inode->i_sb));
-	raw_inode->i_links_count = 0;
-	raw_inode->i_dtime = CURRENT_TIME;
-	mark_buffer_dirty(bh, 1);
-	if (IS_SYNC(inode)) {
-		ll_rw_block (WRITE, 1, &bh);
-		wait_on_buffer (bh);
-	}
-	brelse (bh);
-}
-
 void ext2_free_inode (struct inode * inode)
 {
 	struct super_block * sb;
@@ -222,7 +187,7 @@
 
 	sb = inode->i_sb;
 	lock_super (sb);
-	if (inode->i_ino < EXT2_FIRST_INO ||
+	if (inode->i_ino < EXT2_FIRST_INO(sb) ||
 	    inode->i_ino > sb->u.ext2_sb.s_es->s_inodes_count) {
 		ext2_error (sb, "free_inode",
 			    "reserved inode or nonexistent inode");
@@ -246,7 +211,6 @@
 		es->s_free_inodes_count++;
 		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 		inode->i_dirt = 0;
-		set_inode_dtime (inode, gdp);
 	}
 	mark_buffer_dirty(bh, 1);
 	if (sb->s_flags & MS_SYNCHRONOUS) {
@@ -269,30 +233,10 @@
 			       struct ext2_group_desc *gdp,
 			       int mode)
 {
-	unsigned long inode_block;
-	struct buffer_head * bh;
-	struct ext2_inode * raw_inode;
+	inode->u.ext2_i.i_version++;
+	inode->i_dirt = 1;
 
-	inode_block = gdp->bg_inode_table + (((inode->i_ino - 1) %
-			EXT2_INODES_PER_GROUP(inode->i_sb)) /
-			EXT2_INODES_PER_BLOCK(inode->i_sb));
-	bh = bread (inode->i_sb->s_dev, inode_block, inode->i_sb->s_blocksize);
-	if (!bh) {
-		ext2_error (inode->i_sb, "inc_inode_version",
-			    "Cannot load inode table block - "
-			    "inode=%lu, inode_block=%lu\n",
-			    inode->i_ino, inode_block);
-		inode->u.ext2_i.i_version = 1;
-		return;
-	}
-	raw_inode = ((struct ext2_inode *) bh->b_data) +
-			(((inode->i_ino - 1) %
-			EXT2_INODES_PER_GROUP(inode->i_sb)) %
-			EXT2_INODES_PER_BLOCK(inode->i_sb));
-	raw_inode->i_version++;
-	inode->u.ext2_i.i_version = raw_inode->i_version;
-	mark_buffer_dirty(bh, 1);
-	brelse (bh);
+	return;
 }
 
 /*
@@ -434,7 +378,7 @@
 		goto repeat;
 	}
 	j += i * EXT2_INODES_PER_GROUP(sb) + 1;
-	if (j < EXT2_FIRST_INO || j > es->s_inodes_count) {
+	if (j < EXT2_FIRST_INO(sb) || j > es->s_inodes_count) {
 		ext2_error (sb, "ext2_new_inode",
 			    "reserved inode or inode > inodes count - "
 			    "block_group = %d,inode=%d", i, j);
@@ -468,6 +412,7 @@
 	inode->i_blksize = PAGE_SIZE;	/* This is the optimal IO size (for stat), not the fs block size */
 	inode->i_blocks = 0;
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+	inode->u.ext2_i.i_new_inode = 1;
 	inode->u.ext2_i.i_flags = dir->u.ext2_i.i_flags;
 	if (S_ISLNK(mode))
 		inode->u.ext2_i.i_flags &= ~(EXT2_IMMUTABLE_FL | EXT2_APPEND_FL);
diff -u --recursive --new-file v1.3.97/linux/fs/ext2/inode.c linux/fs/ext2/inode.c
--- v1.3.97/linux/fs/ext2/inode.c	Sat Jan  6 13:12:52 1996
+++ linux/fs/ext2/inode.c	Sat May  4 10:06:18 1996
@@ -27,12 +27,17 @@
 #include <linux/locks.h>
 #include <linux/mm.h>
 
+static int ext2_update_inode(struct inode * inode, int do_sync);
+
 void ext2_put_inode (struct inode * inode)
 {
 	ext2_discard_prealloc (inode);
 	if (inode->i_nlink || inode->i_ino == EXT2_ACL_IDX_INO ||
 	    inode->i_ino == EXT2_ACL_DATA_INO)
 		return;
+	inode->u.ext2_i.i_dtime	= CURRENT_TIME;
+	inode->i_dirt = 1;
+	ext2_update_inode(inode, IS_SYNC(inode));
 	inode->i_size = 0;
 	if (inode->i_blocks)
 		ext2_truncate (inode);
@@ -495,10 +500,12 @@
 	unsigned long group_desc;
 	unsigned long desc;
 	unsigned long block;
+	unsigned long offset;
 	struct ext2_group_desc * gdp;
 
 	if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO &&
-	     inode->i_ino != EXT2_ACL_DATA_INO && inode->i_ino < EXT2_FIRST_INO) ||
+	     inode->i_ino != EXT2_ACL_DATA_INO &&
+	     inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
 	    inode->i_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) {
 		ext2_error (inode->i_sb, "ext2_read_inode",
 			    "bad inode number: %lu", inode->i_ino);
@@ -515,15 +522,20 @@
 		ext2_panic (inode->i_sb, "ext2_read_inode",
 			    "Descriptor not loaded");
 	gdp = (struct ext2_group_desc *) bh->b_data;
+	/*
+	 * Figure out the offset within the block group inode table
+	 */
+	offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *
+		EXT2_INODE_SIZE(inode->i_sb);
 	block = gdp[desc].bg_inode_table +
-		(((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb))
-		 >> EXT2_INODES_PER_BLOCK_BITS(inode->i_sb));
+		(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
 	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
 		ext2_panic (inode->i_sb, "ext2_read_inode",
 			    "unable to read i-node block - "
 			    "inode=%lu, block=%lu", inode->i_ino, block);
-	raw_inode = ((struct ext2_inode *) bh->b_data) +
-		((inode->i_ino - 1) & (EXT2_INODES_PER_BLOCK(inode->i_sb) - 1));
+	offset &= (EXT2_BLOCK_SIZE(inode->i_sb) - 1);
+	raw_inode = (struct ext2_inode *) (bh->b_data + offset);
+
 	inode->i_mode = raw_inode->i_mode;
 	inode->i_uid = raw_inode->i_uid;
 	inode->i_gid = raw_inode->i_gid;
@@ -536,6 +548,7 @@
 	inode->i_blksize = PAGE_SIZE;	/* This is the optimal IO size (for stat), not the fs block size */
 	inode->i_blocks = raw_inode->i_blocks;
 	inode->i_version = ++event;
+	inode->u.ext2_i.i_new_inode = 0;
 	inode->u.ext2_i.i_flags = raw_inode->i_flags;
 	inode->u.ext2_i.i_faddr = raw_inode->i_faddr;
 	inode->u.ext2_i.i_frag_no = raw_inode->i_frag;
@@ -579,7 +592,7 @@
 		inode->i_flags |= S_IMMUTABLE;
 }
 
-static struct buffer_head * ext2_update_inode (struct inode * inode)
+static int ext2_update_inode(struct inode * inode, int do_sync)
 {
 	struct buffer_head * bh;
 	struct ext2_inode * raw_inode;
@@ -587,9 +600,12 @@
 	unsigned long group_desc;
 	unsigned long desc;
 	unsigned long block;
+	unsigned long offset;
+	int err = 0;
 	struct ext2_group_desc * gdp;
 
-	if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino < EXT2_FIRST_INO) ||
+	if ((inode->i_ino != EXT2_ROOT_INO &&
+	     inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
 	    inode->i_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) {
 		ext2_error (inode->i_sb, "ext2_write_inode",
 			    "bad inode number: %lu", inode->i_ino);
@@ -606,15 +622,20 @@
 		ext2_panic (inode->i_sb, "ext2_write_inode",
 			    "Descriptor not loaded");
 	gdp = (struct ext2_group_desc *) bh->b_data;
+	/*
+	 * Figure out the offset within the block group inode table
+	 */
+	offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *
+		EXT2_INODE_SIZE(inode->i_sb);
 	block = gdp[desc].bg_inode_table +
-		(((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb))
-		 >> EXT2_INODES_PER_BLOCK_BITS(inode->i_sb));
+		(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
 	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
 		ext2_panic (inode->i_sb, "ext2_write_inode",
 			    "unable to read i-node block - "
 			    "inode=%lu, block=%lu", inode->i_ino, block);
-	raw_inode = ((struct ext2_inode *)bh->b_data) +
-		(inode->i_ino - 1) % EXT2_INODES_PER_BLOCK(inode->i_sb);
+	offset &= EXT2_BLOCK_SIZE(inode->i_sb) - 1;
+	raw_inode = (struct ext2_inode *) (bh->b_data + offset);
+
 	raw_inode->i_mode = inode->i_mode;
 	raw_inode->i_uid = inode->i_uid;
 	raw_inode->i_gid = inode->i_gid;
@@ -638,36 +659,27 @@
 		raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
 	mark_buffer_dirty(bh, 1);
 	inode->i_dirt = 0;
-	return bh;
-}
-
-void ext2_write_inode (struct inode * inode)
-{
-	struct buffer_head * bh;
-	bh = ext2_update_inode (inode);
-	brelse (bh);
-}
-
-int ext2_sync_inode (struct inode *inode)
-{
-	int err = 0;
-	struct buffer_head *bh;
-
-	bh = ext2_update_inode (inode);
-	if (bh && buffer_dirty(bh))
-	{
+	if (do_sync) {
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
-		if (buffer_req(bh) && !buffer_uptodate(bh))
-		{
+		if (buffer_req(bh) && !buffer_uptodate(bh)) {
 			printk ("IO error syncing ext2 inode ["
 				"%s:%08lx]\n",
 				kdevname(inode->i_dev), inode->i_ino);
 			err = -1;
 		}
 	}
-	else if (!bh)
-		err = -1;
 	brelse (bh);
 	return err;
 }
+
+void ext2_write_inode (struct inode * inode)
+{
+	ext2_update_inode (inode, 0);
+}
+
+int ext2_sync_inode (struct inode *inode)
+{
+	return ext2_update_inode (inode, 1);
+}
+
diff -u --recursive --new-file v1.3.97/linux/fs/ext2/super.c linux/fs/ext2/super.c
--- v1.3.97/linux/fs/ext2/super.c	Wed Apr 24 17:00:41 1996
+++ linux/fs/ext2/super.c	Sat May  4 10:07:07 1996
@@ -270,7 +270,7 @@
 static void ext2_setup_super (struct super_block * sb,
 			      struct ext2_super_block * es)
 {
-	if (es->s_rev_level > EXT2_CURRENT_REV) {
+	if (es->s_rev_level > EXT2_MAX_SUPP_REV) {
 			printk ("EXT2-fs warning: revision level too high, "
 				"forcing read/only mode\n");
 			sb->s_flags |= MS_RDONLY;
@@ -400,14 +400,23 @@
 	sb->u.ext2_sb.s_es = es;
 	sb->s_magic = es->s_magic;
 	if (sb->s_magic != EXT2_SUPER_MAGIC) {
-		sb->s_dev = 0;
-		unlock_super (sb);
-		brelse (bh);
 		if (!silent)
 			printk ("VFS: Can't find an ext2 filesystem on dev "
 				"%s.\n", kdevname(dev));
+	failed_mount:
+		sb->s_dev = 0;
+		unlock_super (sb);
+		if (bh)
+			brelse(bh);
+		MOD_DEC_USE_COUNT;
 		return NULL;
 	}
+	if ((es->s_rev_level > EXT2_GOOD_OLD_REV) &&
+	    (es->s_feature_incompat & !EXT2_FEATURE_INCOMPAT_SUPP)) {
+		printk("EXT2-fs: %s: couldn't mount because of "
+		       "unsupported optional features.\n", kdevname(dev));
+		goto failed_mount;
+	}
 	sb->s_blocksize_bits = sb->u.ext2_sb.s_es->s_log_block_size + 10;
 	sb->s_blocksize = 1 << sb->s_blocksize_bits;
 	if (sb->s_blocksize != BLOCK_SIZE && 
@@ -421,18 +430,27 @@
 		offset = (sb_block*BLOCK_SIZE) % sb->s_blocksize;
 		bh = bread (dev, logic_sb_block, sb->s_blocksize);
 		if(!bh) {
-		        MOD_DEC_USE_COUNT;
-			return NULL;
-		      }
+			printk("EXT2-fs: Couldn't read superblock on "
+			       "2nd try.\n");
+			goto failed_mount;
+		}
 		es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
 		sb->u.ext2_sb.s_es = es;
 		if (es->s_magic != EXT2_SUPER_MAGIC) {
-			sb->s_dev = 0;
-			unlock_super (sb);
-			brelse (bh);
 			printk ("EXT2-fs: Magic mismatch, very weird !\n");
-		        MOD_DEC_USE_COUNT;
-			return NULL;
+			goto failed_mount;
+		}
+	}
+	if (es->s_rev_level == EXT2_GOOD_OLD_REV) {
+		sb->u.ext2_sb.s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
+		sb->u.ext2_sb.s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
+	} else {
+		sb->u.ext2_sb.s_inode_size = es->s_inode_size;
+		sb->u.ext2_sb.s_first_ino = es->s_first_ino;
+		if (sb->u.ext2_sb.s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) {
+			printk ("EXT2-fs: unsupported inode size: %d\n",
+				sb->u.ext2_sb.s_inode_size);
+			goto failed_mount;
 		}
 	}
 	sb->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
@@ -446,7 +464,7 @@
 	sb->u.ext2_sb.s_frags_per_group = es->s_frags_per_group;
 	sb->u.ext2_sb.s_inodes_per_group = es->s_inodes_per_group;
 	sb->u.ext2_sb.s_inodes_per_block = sb->s_blocksize /
-					   sizeof (struct ext2_inode);
+					   EXT2_INODE_SIZE(sb);
 	sb->u.ext2_sb.s_itb_per_group = sb->u.ext2_sb.s_inodes_per_group /
 				        sb->u.ext2_sb.s_inodes_per_block;
 	sb->u.ext2_sb.s_desc_per_block = sb->s_blocksize /
@@ -465,68 +483,42 @@
 	sb->u.ext2_sb.s_rename_wait = NULL;
 	sb->u.ext2_sb.s_addr_per_block_bits =
 		log2 (EXT2_ADDR_PER_BLOCK(sb));
-	sb->u.ext2_sb.s_inodes_per_block_bits =
-		log2 (EXT2_INODES_PER_BLOCK(sb));
 	sb->u.ext2_sb.s_desc_per_block_bits =
 		log2 (EXT2_DESC_PER_BLOCK(sb));
 	if (sb->s_magic != EXT2_SUPER_MAGIC) {
-		sb->s_dev = 0;
-		unlock_super (sb);
-		brelse (bh);
 		if (!silent)
 			printk ("VFS: Can't find an ext2 filesystem on dev "
 				"%s.\n",
 				kdevname(dev));
-		MOD_DEC_USE_COUNT;
-		return NULL;
+		goto failed_mount;
 	}
 	if (sb->s_blocksize != bh->b_size) {
-		sb->s_dev = 0;
-		unlock_super (sb);
-		brelse (bh);
 		if (!silent)
 			printk ("VFS: Unsupported blocksize on dev "
 				"%s.\n", kdevname(dev));
-		MOD_DEC_USE_COUNT;
-		return NULL;
+		goto failed_mount;
 	}
 
 	if (sb->s_blocksize != sb->u.ext2_sb.s_frag_size) {
-		sb->s_dev = 0;
-		unlock_super (sb);
-		brelse (bh);
 		printk ("EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\n",
 			sb->u.ext2_sb.s_frag_size, sb->s_blocksize);
-		MOD_DEC_USE_COUNT;
-		return NULL;
+		goto failed_mount;
 	}
 
 	if (sb->u.ext2_sb.s_blocks_per_group > sb->s_blocksize * 8) {
-		sb->s_dev = 0;
-		unlock_super (sb);
-		brelse (bh);
 		printk ("EXT2-fs: #blocks per group too big: %lu\n",
 			sb->u.ext2_sb.s_blocks_per_group);
-		MOD_DEC_USE_COUNT;
-		return NULL;
+		goto failed_mount;
 	}
 	if (sb->u.ext2_sb.s_frags_per_group > sb->s_blocksize * 8) {
-		sb->s_dev = 0;
-		unlock_super (sb);
-		brelse (bh);
 		printk ("EXT2-fs: #fragments per group too big: %lu\n",
 			sb->u.ext2_sb.s_frags_per_group);
-		MOD_DEC_USE_COUNT;
-		return NULL;
+		goto failed_mount;
 	}
 	if (sb->u.ext2_sb.s_inodes_per_group > sb->s_blocksize * 8) {
-		sb->s_dev = 0;
-		unlock_super (sb);
-		brelse (bh);
 		printk ("EXT2-fs: #inodes per group too big: %lu\n",
 			sb->u.ext2_sb.s_inodes_per_group);
-		MOD_DEC_USE_COUNT;
-		return NULL;
+		goto failed_mount;
 	}
 
 	sb->u.ext2_sb.s_groups_count = (es->s_blocks_count -
@@ -537,40 +529,28 @@
 		   EXT2_DESC_PER_BLOCK(sb);
 	sb->u.ext2_sb.s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL);
 	if (sb->u.ext2_sb.s_group_desc == NULL) {
-		sb->s_dev = 0;
-		unlock_super (sb);
-		brelse (bh);
 		printk ("EXT2-fs: not enough memory\n");
-		MOD_DEC_USE_COUNT;
-		return NULL;
+		goto failed_mount;
 	}
 	for (i = 0; i < db_count; i++) {
 		sb->u.ext2_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1,
 						       sb->s_blocksize);
 		if (!sb->u.ext2_sb.s_group_desc[i]) {
-			sb->s_dev = 0;
-			unlock_super (sb);
 			for (j = 0; j < i; j++)
 				brelse (sb->u.ext2_sb.s_group_desc[j]);
 			kfree_s (sb->u.ext2_sb.s_group_desc,
 				 db_count * sizeof (struct buffer_head *));
-			brelse (bh);
 			printk ("EXT2-fs: unable to read group descriptors\n");
-			MOD_DEC_USE_COUNT;
-			return NULL;
+			goto failed_mount;
 		}
 	}
 	if (!ext2_check_descriptors (sb)) {
-		sb->s_dev = 0;
-		unlock_super (sb);
 		for (j = 0; j < db_count; j++)
 			brelse (sb->u.ext2_sb.s_group_desc[j]);
 		kfree_s (sb->u.ext2_sb.s_group_desc,
 			 db_count * sizeof (struct buffer_head *));
-		brelse (bh);
 		printk ("EXT2-fs: group descriptors corrupted !\n");
-		MOD_DEC_USE_COUNT;
-		return NULL;
+		goto failed_mount;
 	}
 	for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
 		sb->u.ext2_sb.s_inode_bitmap_number[i] = 0;
diff -u --recursive --new-file v1.3.97/linux/fs/fcntl.c linux/fs/fcntl.c
--- v1.3.97/linux/fs/fcntl.c	Sat Apr 27 15:19:58 1996
+++ linux/fs/fcntl.c	Sat May  4 19:39:22 1996
@@ -85,7 +85,7 @@
 			if (!(arg & FASYNC) && (filp->f_flags & FASYNC) &&
 			    filp->f_op->fasync)
 				filp->f_op->fasync(filp->f_inode, filp, 0);
-			/* requiered for SunOS emulation */
+			/* required for SunOS emulation */
 			if (O_NONBLOCK != O_NDELAY)
 			       if (arg & O_NDELAY)
 				   arg |= O_NONBLOCK;
diff -u --recursive --new-file v1.3.97/linux/fs/locks.c linux/fs/locks.c
--- v1.3.97/linux/fs/locks.c	Sat Apr 27 15:19:58 1996
+++ linux/fs/locks.c	Tue Apr 30 13:44:00 1996
@@ -69,8 +69,12 @@
  *  Manual, Section 2.
  *  Andy Walker (andy@lysaker.kvaerner.no), April 09, 1996.
  *
- *  Tidied up block list handling.
+ *  Tidied up block list handling. Added '/proc/locks' interface.
  *  Andy Walker (andy@lysaker.kvaerner.no), April 24, 1996.
+ *
+ *  Fixed deadlock condition for pathological code that mixes calls to
+ *  flock() and fcntl().
+ *  Andy Walker (andy@lysaker.kvaerner.no), April 29, 1996.
  */
 
 #include <linux/malloc.h>
@@ -97,13 +101,14 @@
 			   unsigned int wait);
 static int posix_lock_file(struct file *filp, struct file_lock *caller,
 			   unsigned int wait);
-static int posix_locks_deadlock(struct task_struct *my_task,
-				struct task_struct *blocked_task);
+static int locks_deadlock(struct task_struct *my_task,
+			  struct task_struct *blocked_task);
 static int locks_overlap(struct file_lock *fl1, struct file_lock *fl2);
 
 static struct file_lock *locks_alloc_lock(struct file_lock *fl);
 static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl);
 static void locks_delete_lock(struct file_lock **fl, unsigned int wait);
+static char *lock_get_status(struct file_lock *fl, char *p, int id, char *pfx);
 
 static struct file_lock *file_lock_table = NULL;
 
@@ -308,8 +313,8 @@
 	 */
 	before = &filp->f_inode->i_flock;
 	while ((fl = *before) != NULL) {
-		if (((fl->fl_flags == F_POSIX) && (fl->fl_owner == task)) ||
-		    ((fl->fl_flags == F_FLOCK) && (fl->fl_file == filp) &&
+		if (((fl->fl_flags & F_POSIX) && (fl->fl_owner == task)) ||
+		    ((fl->fl_flags & F_FLOCK) && (fl->fl_file == filp) &&
 		     (filp->f_count == 1)))
 			locks_delete_lock(before, 0);
 		else
@@ -336,7 +341,7 @@
 	/* Search the lock list for this inode for any POSIX locks.
 	 */
 	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-		if (fl->fl_flags == F_POSIX && fl->fl_owner != current)
+		if ((fl->fl_flags & F_POSIX) && (fl->fl_owner != current))
 			return (-EAGAIN);
 	}
 	return (0);
@@ -366,8 +371,8 @@
 	 * the proposed read/write.
 	 */
 	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-		if (fl->fl_flags == F_FLOCK ||
-		    (fl->fl_flags == F_POSIX && fl->fl_owner == current))
+		if ((fl->fl_flags & F_FLOCK) ||
+		    ((fl->fl_flags & F_POSIX) && (fl->fl_owner == current)))
 			continue;
 		if (fl->fl_end < offset ||
 		    fl->fl_start >= offset + count)
@@ -382,7 +387,7 @@
 				return (-EAGAIN);
 			if (current->signal & ~current->blocked)
 				return (-ERESTARTSYS);
-			if (posix_locks_deadlock(current, fl->fl_owner))
+			if (locks_deadlock(current, fl->fl_owner))
 				return (-EDEADLOCK);
 			interruptible_sleep_on(&fl->fl_wait);
 			if (current->signal & ~current->blocked)
@@ -407,6 +412,8 @@
 {
 	off_t start;
 
+	fl->fl_flags = F_POSIX;
+
 	switch (l->l_type) {
 	case F_RDLCK :
 	case F_WRLCK :
@@ -415,9 +422,11 @@
 		break;
 	case F_SHLCK :
 		fl->fl_type = F_RDLCK;
+		fl->fl_flags |= F_BROKEN;
 		break;
 	case F_EXLCK :
 		fl->fl_type = F_WRLCK;
+		fl->fl_flags |= F_BROKEN;
 		break;
 	default :
 		return (0);
@@ -443,7 +452,6 @@
 	if ((l->l_len == 0) || ((fl->fl_end = start + l->l_len - 1) < 0))
 		fl->fl_end = OFFSET_MAX;
 	
-	fl->fl_flags = F_POSIX;
 	fl->fl_file = filp;
 	fl->fl_owner = current;
 	fl->fl_wait = NULL;		/* just for cleanliness */
@@ -492,7 +500,7 @@
 	/* POSIX locks owned by the same process do not conflict with
 	 * each other.
 	 */
-	if ((sys_fl->fl_flags == F_POSIX) &&
+	if ((sys_fl->fl_flags & F_POSIX) &&
 	    (caller_fl->fl_owner == sys_fl->fl_owner))
 		return (0);
 
@@ -507,7 +515,7 @@
 	/* FLOCK locks referring to the same filp do not conflict with
 	 * each other.
 	 */
-	if ((sys_fl->fl_flags == F_FLOCK) &&
+	if ((sys_fl->fl_flags & F_FLOCK) &&
 	    (caller_fl->fl_file == sys_fl->fl_file))
 		return (0);
 
@@ -555,13 +563,15 @@
  * with blocked_task equal to that queue's owner, until either blocked_task
  * isn't found, or blocked_task is found on a queue owned by my_task.
  */
-static int posix_locks_deadlock(struct task_struct *my_task,
-				struct task_struct *blocked_task)
+static int locks_deadlock(struct task_struct *my_task,
+			  struct task_struct *blocked_task)
 {
 	struct wait_queue *dlock_wait;
 	struct file_lock *fl;
 
 next_task:
+	if (my_task == blocked_task)
+		return (1);
 	for (fl = file_lock_table; fl != NULL; fl = fl->fl_nextlink) {
 		if (fl->fl_owner == NULL || fl->fl_wait == NULL)
 			continue;
@@ -569,7 +579,7 @@
 		do {
 			if (dlock_wait->task == blocked_task) {
 				if (fl->fl_owner == my_task) {
-					return(-EDEADLOCK);
+					return (1);
 				}
 				blocked_task = fl->fl_owner;
 				goto next_task;
@@ -596,7 +606,7 @@
 	 * locks at the front of the list.
 	 */
 	before = &filp->f_inode->i_flock;
-	while ((fl = *before) && (fl->fl_flags == F_FLOCK)) {
+	while ((fl = *before) && (fl->fl_flags & F_FLOCK)) {
 		if (caller->fl_file == fl->fl_file) {
 			if (caller->fl_type == fl->fl_type)
 				return (0);
@@ -622,24 +632,31 @@
 		if (wait) {
 			if (current->signal & ~current->blocked) {
 				/* Note: new_fl is not in any queue at this
-				 * point. So we must use locks_free_lock()
+				 * point, so we must use locks_free_lock()
 				 * instead of locks_delete_lock()
 				 * 	Dmitry Gorodchanin 09/02/96.
 				 */
 				locks_free_lock(new_fl);
 				return (-ERESTARTSYS);
 			}
+			/* Try to avoid deadlocks due to pathalogical programs that
+			 * mix calls to flock() and fcntl(). Return EAGAIN, because
+			 * EDEADLOCK isn't a documented return value for flock().
+			 */
+			if (locks_deadlock(new_fl->fl_owner, fl->fl_owner)) {
+				locks_free_lock(new_fl);
+				return (-EAGAIN);
+			}
 			locks_insert_block(fl, new_fl);
 			interruptible_sleep_on(&new_fl->fl_wait);
 			wake_up(&new_fl->fl_wait);
 			if (current->signal & ~current->blocked) {
-				/* If we are here, than we were awaken
-				 * by signal, so new_fl is still in 
-				 * block queue of fl. We need remove 
-				 * new_fl and then free it. 
+				/* If we are here, than we were awakened
+				 * by a signal, so new_fl is still in the
+				 * block queue of fl. We need to remove 
+				 * new_fl and then free it.
 				 * 	Dmitry Gorodchanin 09/02/96.
 				 */
-
 				locks_delete_block(fl, new_fl);
 				locks_free_lock(new_fl);
 				return (-ERESTARTSYS);
@@ -684,9 +701,8 @@
 			if (wait) {
 				if (current->signal & ~current->blocked)
 					return (-ERESTARTSYS);
-				if (fl->fl_flags == F_POSIX)
-					if (posix_locks_deadlock(caller->fl_owner, fl->fl_owner))
-						return (-EDEADLOCK);
+				if (locks_deadlock(caller->fl_owner, fl->fl_owner))
+					return (-EDEADLOCK);
 				interruptible_sleep_on(&fl->fl_wait);
 				if (current->signal & ~current->blocked)
 					return (-ERESTARTSYS);
@@ -703,7 +719,7 @@
 
 	/* First skip FLOCK locks and locks owned by other processes.
 	 */
-	while ((fl = *before) && ((fl->fl_flags == F_FLOCK) ||
+	while ((fl = *before) && ((fl->fl_flags & F_FLOCK) ||
 				  (caller->fl_owner != fl->fl_owner))) {
 		before = &fl->fl_next;
 	}
@@ -786,7 +802,7 @@
 		/* Go on to next lock.
 		 */
 	next_lock:
-		before = &(*before)->fl_next;
+		before = &fl->fl_next;
 	}
 
 	if (!added) {
@@ -795,7 +811,6 @@
 		if ((new_fl = locks_alloc_lock(caller)) == NULL)
 			return (-ENOLCK);
 		locks_insert_lock(before, new_fl);
-
 	}
 	if (right) {
 		if (left == right) {
@@ -904,3 +919,53 @@
 
 	return;
 }
+
+
+static char *lock_get_status(struct file_lock *fl, char *p, int id, char *pfx)
+{
+	struct wait_queue *wt;
+
+	p += sprintf(p, "%d:%s ", id, pfx);
+	if (fl->fl_flags & F_POSIX) {
+		p += sprintf(p, "%s %s ",
+			     (fl->fl_flags & F_BROKEN) ? "BROKEN" : "POSIX ",
+			     ((fl->fl_file->f_inode->i_mode & (S_IXGRP | S_ISGID))
+			      == S_ISGID) ? "MANDATORY" : "ADVISORY ");
+	}
+	else {
+		p += sprintf(p, "FLOCK  ADVISORY  ");
+	}
+	p += sprintf(p, "%s ", (fl->fl_type == F_RDLCK) ? "READ " : "WRITE");
+	p += sprintf(p, "%d %04x:%ld %ld %ld ",
+		     fl->fl_owner->pid, fl->fl_file->f_inode->i_dev,
+		     fl->fl_file->f_inode->i_ino, fl->fl_start,
+		     fl->fl_end);
+	p += sprintf(p, "%08lx %08lx %08lx %08lx %08lx\n%d:%s",
+		     (long)fl, (long)fl->fl_prevlink, (long)fl->fl_nextlink,
+		     (long)fl->fl_next, (long)fl->fl_block, id, pfx);
+	if ((wt = fl->fl_wait) != NULL) {
+		do {
+			p += sprintf(p, " %d", wt->task->pid);
+			wt = wt->next;
+		} while (wt != fl->fl_wait);
+	}
+	p += sprintf(p, "\n");
+	return (p);
+}
+
+int get_locks_status(char *buf)
+{
+	struct file_lock *fl;
+	struct file_lock *bfl;
+	char *p;
+	int i;
+
+	p = buf;
+	for (fl = file_lock_table, i = 1; fl != NULL; fl = fl->fl_nextlink, i++) {
+		p = lock_get_status(fl, p, i, "");
+		for (bfl = fl; bfl->fl_block != NULL; bfl = bfl->fl_block)
+			p = lock_get_status(bfl->fl_block, p, i, " ->");
+	}
+	return  (p - buf);
+}
+
diff -u --recursive --new-file v1.3.97/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v1.3.97/linux/fs/nfs/nfsroot.c	Sun Apr 21 12:39:02 1996
+++ linux/fs/nfs/nfsroot.c	Wed May  1 10:26:55 1996
@@ -51,6 +51,8 @@
  *				without giving a path name. Fix BOOTP request
  *				for domainname (domainname is NIS domain, not
  *				DNS domain!). Skip dummy devices for BOOTP.
+ *	Jacek Zapala	:	Fixed a bug which prevented server-ip address
+ *				from nfsroot parameter from being used.
  *
  */
 
@@ -1060,6 +1062,8 @@
 			break;
 		if (*cp == '.' || octets == 3)
 			octets++;
+		if (octets < 4)
+			cp++;
 		cq = cp;
 	}
 	if (octets == 4 && (*cp == ':' || *cp == '\0')) {
diff -u --recursive --new-file v1.3.97/linux/fs/pipe.c linux/fs/pipe.c
--- v1.3.97/linux/fs/pipe.c	Wed Apr 24 17:00:41 1996
+++ linux/fs/pipe.c	Fri May  3 10:24:07 1996
@@ -161,7 +161,7 @@
 			select_wait(&PIPE_WAIT(*inode), wait);
 			return 0;
 		case SEL_OUT:
-			if (!PIPE_FULL(*inode) || !PIPE_READERS(*inode))
+			if (PIPE_EMPTY(*inode) || !PIPE_READERS(*inode))
 				return 1;
 			select_wait(&PIPE_WAIT(*inode), wait);
 			return 0;
diff -u --recursive --new-file v1.3.97/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.3.97/linux/fs/proc/array.c	Sat Apr 27 15:19:59 1996
+++ linux/fs/proc/array.c	Tue Apr 30 13:09:45 1996
@@ -967,6 +967,7 @@
 extern int get_pci_list(char*);
 extern int get_md_status (char *);
 extern int get_rtc_status (char *);
+extern int get_locks_status (char *);
 #ifdef __SMP_PROF__
 extern int get_smp_prof_list(char *);
 #endif
@@ -1041,6 +1042,8 @@
 		case PROC_RTC:
 			return get_rtc_status(page);
 #endif
+		case PROC_LOCKS:
+			return get_locks_status(page);
 	}
 	return -EBADF;
 }
diff -u --recursive --new-file v1.3.97/linux/fs/proc/mem.c linux/fs/proc/mem.c
--- v1.3.97/linux/fs/proc/mem.c	Sat Apr 27 15:19:59 1996
+++ linux/fs/proc/mem.c	Thu May  2 16:00:28 1996
@@ -50,6 +50,32 @@
 	return retval;
 }
 
+static struct task_struct * get_task(int pid)
+{
+	struct task_struct * tsk = current;
+
+	if (pid != tsk->pid) {
+		int i;
+		tsk = NULL;
+		for (i = 1 ; i < NR_TASKS ; i++)
+			if (task[i] && task[i]->pid == pid) {
+				tsk = task[i];
+				break;
+			}
+		/*
+		 * allow accesses only under the same circumstances
+		 * that we would allow ptrace to work
+		 */
+		if (tsk) {
+			if (!(tsk->flags & PF_PTRACED)
+			    || tsk->state != TASK_STOPPED
+			    || tsk->p_pptr != current)
+				tsk = NULL;
+		}
+	}
+	return tsk;
+}
+
 static int mem_read(struct inode * inode, struct file * file,char * buf, int count)
 {
 	pgd_t *page_dir;
@@ -57,17 +83,15 @@
 	pte_t pte;
 	char * page;
 	struct task_struct * tsk;
-	unsigned long addr, pid;
+	unsigned long addr;
 	char *tmp;
 	int i;
 
 	if (count < 0)
 		return -EINVAL;
-	pid = inode->i_ino;
-	pid >>= 16;
-	if (pid != current->pid)
-		return -EACCES;
-	tsk = current;
+	tsk = get_task(inode->i_ino >> 16);
+	if (!tsk)
+		return -ESRCH;
 	addr = file->f_pos;
 	count = check_range(tsk, addr, count);
 	if (count < 0)
@@ -117,18 +141,16 @@
 	pte_t pte;
 	char * page;
 	struct task_struct * tsk;
-	unsigned long addr, pid;
+	unsigned long addr;
 	char *tmp;
 	int i;
 
 	if (count < 0)
 		return -EINVAL;
 	addr = file->f_pos;
-	pid = inode->i_ino;
-	pid >>= 16;
-	if (pid != current->pid)
-		return -EACCES;
-	tsk = current;
+	tsk = get_task(inode->i_ino >> 16);
+	if (!tsk)
+		return -ESRCH;
 	tmp = buf;
 	while (count > 0) {
 		if (current->signal & ~current->blocked)
@@ -199,19 +221,13 @@
 	pte_t *src_table, *dest_table;
 	unsigned long stmp, dtmp;
 	struct vm_area_struct *src_vma = NULL;
-	int i;
 
 	/* Get the source's task information */
 
-	tsk = NULL;
-	for (i = 1 ; i < NR_TASKS ; i++)
-		if (task[i] && task[i]->pid == (inode->i_ino >> 16)) {
-			tsk = task[i];
-			break;
-		}
+	tsk = get_task(inode->i_ino >> 16);
 
 	if (!tsk)
-		return -EACCES;
+		return -ESRCH;
 
 	/* Ensure that we have a valid source area.  (Has to be mmap'ed and
 	 have valid page information.)  We can't map shared memory at the
diff -u --recursive --new-file v1.3.97/linux/fs/proc/root.c linux/fs/proc/root.c
--- v1.3.97/linux/fs/proc/root.c	Mon Apr 15 12:20:21 1996
+++ linux/fs/proc/root.c	Tue Apr 30 13:09:45 1996
@@ -358,6 +358,10 @@
 		S_IFREG | S_IRUGO, 1, 0, 0,
 	});
 #endif
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_LOCKS, 5, "locks",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
 
 	proc_register( &proc_root, &(struct proc_dir_entry)
 	   { PROC_MTAB, 6, "mounts", S_IFREG | S_IRUGO, 1, 0, 0, } );
diff -u --recursive --new-file v1.3.97/linux/include/asm-alpha/fcntl.h linux/include/asm-alpha/fcntl.h
--- v1.3.97/linux/include/asm-alpha/fcntl.h	Fri Jun 16 20:03:02 1995
+++ linux/include/asm-alpha/fcntl.h	Tue Apr 30 13:09:45 1996
@@ -52,6 +52,7 @@
 #ifdef __KERNEL__
 #define F_POSIX		1
 #define F_FLOCK		2
+#define F_BROKEN	4	/* broken flock() emulation */
 #endif /* __KERNEL__ */
 
 struct flock {
diff -u --recursive --new-file v1.3.97/linux/include/asm-i386/checksum.h linux/include/asm-i386/checksum.h
--- v1.3.97/linux/include/asm-i386/checksum.h	Tue Oct  3 11:45:59 1995
+++ linux/include/asm-i386/checksum.h	Thu May  2 06:44:19 1996
@@ -65,14 +65,30 @@
 	    notl %0
 2:
 	    "
-	: "=&r" (sum), "=&r" (iph), "=&r" (ihl)
+	/* Since the input registers which are loaded with iph and ipl
+	   are modified, we must also specify them as outputs, or gcc
+	   will assume they contain their original values. */
+	: "=r" (sum), "=r" (iph), "=r" (ihl)
 	: "1" (iph), "2" (ihl));
 	return(sum);
 }
 
+/*
+ *	Fold a partial checksum
+ */
 
-
-
+static inline unsigned int csum_fold(unsigned int sum)
+{
+	__asm__("
+		addl %1, %0
+		adcl $0xffff, %0
+		"
+		: "=r" (sum)
+		: "r" (sum << 16), "0" (sum & 0xffff0000)
+	);
+	return (~sum) >> 16;
+}
+ 
 /*
  * computes the checksum of the TCP/UDP pseudo-header
  * returns a 16-bit checksum, already complemented
@@ -85,55 +101,21 @@
 						   unsigned int sum) {
     __asm__("
 	addl %1, %0
-	adcl %4, %0
-	adcl %5, %0
-	adcl $0, %0
-	movl %0, %1
-	shrl $16, %1
-	addw %w1, %w0
+	adcl %2, %0
+	adcl %3, %0
 	adcl $0, %0
-	notl %0
 	"
-	: "=&r" (sum), "=&r" (saddr)
-	: "0" (daddr), "1"(saddr), "r"((ntohs(len)<<16)+proto*256), "r"(sum));
-	return((unsigned short)sum);
-}
-
-/*
- *	Fold a partial checksum without adding pseudo headers
- */
-
-static inline unsigned int csum_fold(unsigned int sum)
-{
-	__asm__("
-		addl %1, %0
-		adcl $0xffff, %0
-		"
-		: "=r" (sum)
-		: "r" (sum << 16), "0" (sum & 0xffff0000)
-	);
-	return (~sum) >> 16;
+	: "=r" (sum)
+	: "g" (daddr), "g"(saddr), "g"((ntohs(len)<<16)+proto*256), "0"(sum));
+	return csum_fold(sum);
 }
- 
 /*
  * this routine is used for miscellaneous IP-like checksums, mainly
  * in icmp.c
  */
 
 static inline unsigned short ip_compute_csum(unsigned char * buff, int len) {
-    unsigned int sum;
-
-    unsigned int scratch;
-    __asm__("
-	movl %0, %1
-	shrl $16, %1
-	addw %w1, %w0
-	adcl $0, %0
-	notl %0
-	"
-	: "=a"(sum), "=r" (scratch)
-	: "0" (csum_partial(buff, len, 0)));
-	return(sum);
+    return csum_fold (csum_partial(buff, len, 0));
 }
 
 #endif
diff -u --recursive --new-file v1.3.97/linux/include/asm-i386/fcntl.h linux/include/asm-i386/fcntl.h
--- v1.3.97/linux/include/asm-i386/fcntl.h	Sun Jun 11 19:01:08 1995
+++ linux/include/asm-i386/fcntl.h	Tue Apr 30 13:09:45 1996
@@ -51,6 +51,7 @@
 #ifdef __KERNEL__
 #define F_POSIX		1
 #define F_FLOCK		2
+#define F_BROKEN	4	/* broken flock() emulation */
 #endif /* __KERNEL__ */
 
 struct flock {
diff -u --recursive --new-file v1.3.97/linux/include/asm-m68k/fcntl.h linux/include/asm-m68k/fcntl.h
--- v1.3.97/linux/include/asm-m68k/fcntl.h	Tue Apr 23 13:57:12 1996
+++ linux/include/asm-m68k/fcntl.h	Tue Apr 30 13:09:45 1996
@@ -51,6 +51,7 @@
 #ifdef __KERNEL__
 #define F_POSIX		1
 #define F_FLOCK		2
+#define F_BROKEN	4	/* broken flock() emulation */
 #endif /* __KERNEL__ */
 
 struct flock {
diff -u --recursive --new-file v1.3.97/linux/include/asm-mips/fcntl.h linux/include/asm-mips/fcntl.h
--- v1.3.97/linux/include/asm-mips/fcntl.h	Wed Dec 13 12:39:45 1995
+++ linux/include/asm-mips/fcntl.h	Tue Apr 30 13:09:45 1996
@@ -52,6 +52,7 @@
 #ifdef __KERNEL__
 #define F_POSIX		1
 #define F_FLOCK		2
+#define F_BROKEN	4	/* broken flock() emulation */
 #endif /* __KERNEL__ */
 
 typedef struct flock {
diff -u --recursive --new-file v1.3.97/linux/include/asm-ppc/fcntl.h linux/include/asm-ppc/fcntl.h
--- v1.3.97/linux/include/asm-ppc/fcntl.h	Sat Nov 25 19:49:06 1995
+++ linux/include/asm-ppc/fcntl.h	Tue Apr 30 13:09:45 1996
@@ -51,6 +51,7 @@
 #ifdef __KERNEL__
 #define F_POSIX		1
 #define F_FLOCK		2
+#define F_BROKEN	4	/* broken flock() emulation */
 #endif /* __KERNEL__ */
 
 struct flock {
diff -u --recursive --new-file v1.3.97/linux/include/asm-sparc/fcntl.h linux/include/asm-sparc/fcntl.h
--- v1.3.97/linux/include/asm-sparc/fcntl.h	Mon Mar  4 08:50:00 1996
+++ linux/include/asm-sparc/fcntl.h	Tue Apr 30 13:09:45 1996
@@ -51,6 +51,7 @@
 #ifdef __KERNEL__
 #define F_POSIX		1
 #define F_FLOCK		2
+#define F_BROKEN	4	/* broken flock() emulation */
 #endif /* __KERNEL__ */
 
 struct flock {
diff -u --recursive --new-file v1.3.97/linux/include/linux/apm_bios.h linux/include/linux/apm_bios.h
--- v1.3.97/linux/include/linux/apm_bios.h	Mon Apr  8 19:01:46 1996
+++ linux/include/linux/apm_bios.h	Fri May  3 16:19:14 1996
@@ -61,6 +61,8 @@
 	int		suser;
 	int		suspends_pending;
 	int		standbys_pending;
+	int		suspends_read;
+	int		standbys_read;
 	int		event_head;
 	int		event_tail;
 	apm_event_t	events[APM_MAX_EVENTS];
@@ -81,6 +83,7 @@
 extern int		apm_register_callback(int (*callback)(apm_event_t));
 extern void		apm_unregister_callback(int (*callback)(apm_event_t));
 
+extern int		apm_set_power_state(ushort state);
 extern int		apm_display_blank(void);
 extern int		apm_display_unblank(void);
 
diff -u --recursive --new-file v1.3.97/linux/include/linux/bios32.h linux/include/linux/bios32.h
--- v1.3.97/linux/include/linux/bios32.h	Tue Nov 14 07:35:31 1995
+++ linux/include/linux/bios32.h	Fri May  3 15:40:09 1996
@@ -9,11 +9,11 @@
  * PCI System Design Guide
  *
  * PCI Special Interest Group
- * M/S HF3-15A
- * 5200 N.E. Elam Young Parkway
- * Hillsboro, Oregon 97124-6497
- * +1 (503) 696-2000 
- * +1 (800) 433-5177
+ * P.O. Box 14070
+ * Portland, OR 97214
+ * U. S. A.
+ * Phone: 800-433-5177 / +1-503-797-4207
+ * Fax: +1-503-234-6762 
  * 
  * Manuals are $25 each or $50 for all three, plus $7 shipping 
  * within the United States, $35 abroad.
diff -u --recursive --new-file v1.3.97/linux/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h
--- v1.3.97/linux/include/linux/ext2_fs.h	Mon Mar 25 10:22:43 1996
+++ linux/include/linux/ext2_fs.h	Sat May  4 10:06:18 1996
@@ -60,7 +60,9 @@
 #define EXT2_ACL_DATA_INO	 4	/* ACL inode */
 #define EXT2_BOOT_LOADER_INO	 5	/* Boot loader inode */
 #define EXT2_UNDEL_DIR_INO	 6	/* Undelete directory inode */
-#define EXT2_FIRST_INO		11	/* First non reserved inode */
+
+/* First non-reserved inode for old ext2 filesystems */
+#define EXT2_GOOD_OLD_FIRST_INO	11
 
 /*
  * The second extended file system magic number
@@ -90,10 +92,17 @@
 #else
 # define EXT2_BLOCK_SIZE_BITS(s)	((s)->s_log_block_size + 10)
 #endif
-#define	EXT2_INODES_PER_BLOCK(s)	(EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_inode))
 #ifdef __KERNEL__
 #define	EXT2_ADDR_PER_BLOCK_BITS(s)	((s)->u.ext2_sb.s_addr_per_block_bits)
-#define	EXT2_INODES_PER_BLOCK_BITS(s)	((s)->u.ext2_sb.s_inodes_per_block_bits)
+#define EXT2_INODE_SIZE(s)		((s)->u.ext2_sb.s_inode_size)
+#define EXT2_FIRST_INO(s)		((s)->u.ext2_sb.s_first_ino)
+#else
+#define EXT2_INODE_SIZE(s)	(((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+				 EXT2_GOOD_OLD_INODE_SIZE : \
+				 (s)->s_inode_size)
+#define EXT2_FIRST_INO(s)	(((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+				 EXT2_GOOD_OLD_FIRST_INO : \
+				 (s)->s_first_ino)
 #endif
 
 /*
@@ -180,7 +189,8 @@
 #define EXT2_IMMUTABLE_FL		0x00000010 /* Immutable file */
 #define EXT2_APPEND_FL			0x00000020 /* writes to file may only append */
 #define EXT2_NODUMP_FL			0x00000040 /* do not dump file */
-
+#define EXT2_RESERVED_FL		0x80000000 /* reserved for ext2 lib */
+	
 /*
  * ioctl commands
  */
@@ -327,14 +337,32 @@
 	__u16	s_magic;		/* Magic signature */
 	__u16	s_state;		/* File system state */
 	__u16	s_errors;		/* Behaviour when detecting errors */
-	__u16	s_pad;
+	__u16	s_minor_rev_level; 	/* minor revision level */
 	__u32	s_lastcheck;		/* time of last check */
 	__u32	s_checkinterval;	/* max. time between checks */
 	__u32	s_creator_os;		/* OS */
 	__u32	s_rev_level;		/* Revision level */
 	__u16	s_def_resuid;		/* Default uid for reserved blocks */
 	__u16	s_def_resgid;		/* Default gid for reserved blocks */
-	__u32	s_reserved[235];	/* Padding to the end of the block */
+	/*
+	 * These fields are for EXT2_DYNAMIC_REV superblocks only.
+	 *
+	 * Note: the difference between the compatible feature set and
+	 * the incompatible feature set is that if there is a bit set
+	 * in the incompatible feature set that the kernel doesn't
+	 * know about, it should refuse to mount the filesystem.
+	 * 
+	 * e2fsck's requirements are more strict; if it doesn't know
+	 * about a feature in either the compatible or incompatible
+	 * feature set, it must abort and not try to meddle with
+	 * things it doesn't understand...
+	 */
+	__u32	s_first_ino; 		/* First non-reserved inode */
+	__u16   s_inode_size; 		/* size of inode structure */
+	__u16	s_block_group_nr; 	/* block group # of this superblock */
+	__u32	s_feature_compat; 	/* compatible feature set */
+	__u32	s_feature_incompat; 	/* incompatible feature set */
+	__u32	s_reserved[231];	/* Padding to the end of the block */
 };
 
 /*
@@ -350,8 +378,12 @@
  * Revision levels
  */
 #define EXT2_GOOD_OLD_REV	0	/* The good old (original) format */
+#define EXT2_DYNAMIC_REV	1 	/* V2 format w/ dynamic inode sizes */
 
 #define EXT2_CURRENT_REV	EXT2_GOOD_OLD_REV
+#define EXT2_MAX_SUPP_REV	EXT2_DYNAMIC_REV
+
+#define EXT2_GOOD_OLD_INODE_SIZE 128
 
 /*
  * Default values for user and/or group using reserved blocks
@@ -380,6 +412,12 @@
 #define EXT2_DIR_ROUND 			(EXT2_DIR_PAD - 1)
 #define EXT2_DIR_REC_LEN(name_len)	(((name_len) + 8 + EXT2_DIR_ROUND) & \
 					 ~EXT2_DIR_ROUND)
+
+/*
+ * Feature set definitions --- none are defined as of now
+ */
+#define EXT2_FEATURE_COMPAT_SUPP	0
+#define EXT2_FEATURE_INCOMPAT_SUPP	0
 
 #ifdef __KERNEL__
 /*
diff -u --recursive --new-file v1.3.97/linux/include/linux/ext2_fs_i.h linux/include/linux/ext2_fs_i.h
--- v1.3.97/linux/include/linux/ext2_fs_i.h	Wed Mar 22 10:33:59 1995
+++ linux/include/linux/ext2_fs_i.h	Sat May  4 10:06:18 1996
@@ -35,6 +35,7 @@
 	__u32	i_next_alloc_goal;
 	__u32	i_prealloc_block;
 	__u32	i_prealloc_count;
+	int	i_new_inode:1;	/* Is a freshly allocated inode */
 };
 
 #endif	/* _LINUX_EXT2_FS_I */
diff -u --recursive --new-file v1.3.97/linux/include/linux/ext2_fs_sb.h linux/include/linux/ext2_fs_sb.h
--- v1.3.97/linux/include/linux/ext2_fs_sb.h	Mon Mar 25 10:22:43 1996
+++ linux/include/linux/ext2_fs_sb.h	Sat May  4 10:06:18 1996
@@ -57,8 +57,9 @@
 	unsigned short s_mount_state;
 	unsigned short s_pad;
 	int s_addr_per_block_bits;
-	int s_inodes_per_block_bits;
 	int s_desc_per_block_bits;
+	int s_inode_size;
+	int s_first_ino;
 };
 
 #endif	/* _LINUX_EXT2_FS_SB */
diff -u --recursive --new-file v1.3.97/linux/include/linux/kernel.h linux/include/linux/kernel.h
--- v1.3.97/linux/include/linux/kernel.h	Mon Mar 11 09:39:34 1996
+++ linux/include/linux/kernel.h	Wed May  1 11:47:47 1996
@@ -57,6 +57,12 @@
 asmlinkage int printk(const char * fmt, ...)
 	__attribute__ ((format (printf, 1, 2)));
 
+#define pr_debug(show,fmt,arg...) \
+	do { if (show) printk(KERN_DEBUG fmt,##arg); } while (0)
+
+#define pr_info(fmt,arg...) \
+	printk(KERN_INFO fmt,##arg)
+
 /*
  * "suser()" checks against the effective user id, while "fsuser()"
  * is used for file permission checking and checks against the fsuid..
diff -u --recursive --new-file v1.3.97/linux/include/linux/mtio.h linux/include/linux/mtio.h
--- v1.3.97/linux/include/linux/mtio.h	Tue Apr 23 13:57:13 1996
+++ linux/include/linux/mtio.h	Fri May  3 10:12:17 1996
@@ -57,6 +57,8 @@
 #define MTLOAD  30	/* execute the SCSI load command */
 #define MTUNLOAD 31	/* execute the SCSI unload command */
 #define MTCOMPRESSION 32/* control compression with SCSI mode page 15 */
+#define MTSETPART 33	/* Change the active tape partition */
+#define MTMKPART  34	/* Format the tape with one or two partitions */
 
 /* structure for MTIOCGET - mag tape get status command */
 
@@ -180,7 +182,6 @@
 #define	MTIOCGETCONFIG	_IOR('m', 4, struct mtconfiginfo) /* get tape config */
 #define	MTIOCSETCONFIG	_IOW('m', 5, struct mtconfiginfo) /* set tape config */
 
-
 /* Generic Mag Tape (device independent) status macros for examining
  * mt_gstat -- HP-UX compatible.
  * There is room for more generic status bits here, but I don't
@@ -235,6 +236,8 @@
 #define MT_ST_DEF_WRITES	0x80
 #define MT_ST_CAN_BSR		0x100
 #define MT_ST_NO_BLKLIMS	0x200
+#define MT_ST_CAN_PARTITIONS    0x400
+#define MT_ST_SCSI2LOGICAL      0x800
 
 /* The mode parameters to be controlled. Parameter chosen with bits 20-28 */
 #define MT_ST_CLEAR_DEFAULT	0xfffff
diff -u --recursive --new-file v1.3.97/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v1.3.97/linux/include/linux/proc_fs.h	Sat Apr 27 15:20:07 1996
+++ linux/include/linux/proc_fs.h	Fri May  3 16:19:04 1996
@@ -42,7 +42,8 @@
 	PROC_SYS,
 	PROC_MTAB,
 	PROC_MD,
-	PROC_RTC
+	PROC_RTC,
+	PROC_LOCKS
 };
 
 enum pid_directory_inos {
diff -u --recursive --new-file v1.3.97/linux/include/linux/random.h linux/include/linux/random.h
--- v1.3.97/linux/include/linux/random.h	Sun Feb 11 12:41:34 1996
+++ linux/include/linux/random.h	Sat May  4 10:02:51 1996
@@ -9,11 +9,26 @@
 
 /* ioctl()'s for the random number generator */
 
+/* Get the entropy count. */
 #define RNDGETENTCNT	0x01080000
+
+/* Add to (or subtract from) the entropy count.  (Superuser only.) */
 #define RNDADDTOENTCNT	0x01080001
+
+/* Get the contents of the entropy pool.  (Superuser only.) */
 #define RNDGETPOOL	0x01080002
+
+/* 
+ * Write bytes into the entropy pool and add to the entropy count.
+ * (Superuser only.)
+ */
 #define RNDADDENTROPY	0x01080003
+
+/* Clear entropy count to 0.  (Superuser only.) */
 #define RNDZAPENTCNT	0x01080004
+
+/* Clear the entropy pool and associated counters.  (Superuser only.) */
+#define RNDCLEARPOOL	0x01080006
 
 struct rand_pool_info {
 	int	entropy_count;
diff -u --recursive --new-file v1.3.97/linux/include/linux/scsi.h linux/include/linux/scsi.h
--- v1.3.97/linux/include/linux/scsi.h	Sun Sep 10 09:37:21 1995
+++ linux/include/linux/scsi.h	Thu Jan  1 02:00:00 1970
@@ -1,198 +0,0 @@
-#ifndef _LINUX_SCSI_H
-#define _LINUX_SCSI_H
-
-/*
- * This header file contains public constants and structures used by
- * the scsi code for linux.
- */
-
-/*
-    $Header: /usr/src/linux/include/linux/RCS/scsi.h,v 1.3 1993/09/24 12:20:33 drew Exp $
-
-    For documentation on the OPCODES, MESSAGES, and SENSE values,
-    please consult the SCSI standard.
-
-*/
-
-/*
- *      SCSI opcodes
- */
-
-#define TEST_UNIT_READY       0x00
-#define REZERO_UNIT           0x01
-#define REQUEST_SENSE         0x03
-#define FORMAT_UNIT           0x04
-#define READ_BLOCK_LIMITS     0x05
-#define REASSIGN_BLOCKS       0x07
-#define READ_6                0x08
-#define WRITE_6               0x0a
-#define SEEK_6                0x0b
-#define READ_REVERSE          0x0f
-#define WRITE_FILEMARKS       0x10
-#define SPACE                 0x11
-#define INQUIRY               0x12
-#define RECOVER_BUFFERED_DATA 0x14
-#define MODE_SELECT           0x15
-#define RESERVE               0x16
-#define RELEASE               0x17
-#define COPY                  0x18
-#define ERASE                 0x19
-#define MODE_SENSE            0x1a
-#define START_STOP            0x1b
-#define RECEIVE_DIAGNOSTIC    0x1c
-#define SEND_DIAGNOSTIC       0x1d
-#define ALLOW_MEDIUM_REMOVAL  0x1e
-
-#define SET_WINDOW            0x24
-#define READ_CAPACITY         0x25
-#define READ_10               0x28
-#define WRITE_10              0x2a
-#define SEEK_10               0x2b
-#define WRITE_VERIFY          0x2e
-#define VERIFY                0x2f
-#define SEARCH_HIGH           0x30
-#define SEARCH_EQUAL          0x31
-#define SEARCH_LOW            0x32
-#define SET_LIMITS            0x33
-#define PRE_FETCH             0x34
-#define READ_POSITION         0x34
-#define SYNCHRONIZE_CACHE     0x35
-#define LOCK_UNLOCK_CACHE     0x36
-#define READ_DEFECT_DATA      0x37
-#define MEDIUM_SCAN           0x38
-#define COMPARE               0x39
-#define COPY_VERIFY           0x3a
-#define WRITE_BUFFER          0x3b
-#define READ_BUFFER           0x3c
-#define UPDATE_BLOCK          0x3d
-#define READ_LONG             0x3e
-#define WRITE_LONG            0x3f
-#define CHANGE_DEFINITION     0x40
-#define WRITE_SAME            0x41
-#define LOG_SELECT            0x4c
-#define LOG_SENSE             0x4d
-#define MODE_SELECT_10        0x55
-#define MODE_SENSE_10         0x5a
-#define READ_12               0xa8
-#define WRITE_12              0xaa
-#define WRITE_VERIFY_12       0xae
-#define SEARCH_HIGH_12        0xb0
-#define SEARCH_EQUAL_12       0xb1
-#define SEARCH_LOW_12         0xb2
-#define SEND_VOLUME_TAG       0xb6
-#define WRITE_LONG_2          0xea
-
-/*
- *  Status codes
- */
-
-#define GOOD                 0x00
-#define CHECK_CONDITION      0x01
-#define CONDITION_GOOD       0x02
-#define BUSY                 0x04
-#define INTERMEDIATE_GOOD    0x08
-#define INTERMEDIATE_C_GOOD  0x0a
-#define RESERVATION_CONFLICT 0x0c
-#define QUEUE_FULL           0x1a
-
-#define STATUS_MASK          0x1e
-
-/*
- *  SENSE KEYS
- */
-
-#define NO_SENSE            0x00
-#define RECOVERED_ERROR     0x01
-#define NOT_READY           0x02
-#define MEDIUM_ERROR        0x03
-#define HARDWARE_ERROR      0x04
-#define ILLEGAL_REQUEST     0x05
-#define UNIT_ATTENTION      0x06
-#define DATA_PROTECT        0x07
-#define BLANK_CHECK         0x08
-#define COPY_ABORTED        0x0a
-#define ABORTED_COMMAND     0x0b
-#define VOLUME_OVERFLOW     0x0d
-#define MISCOMPARE          0x0e
-
-
-/*
- *  DEVICE TYPES
- */
-
-#define TYPE_DISK           0x00
-#define TYPE_TAPE           0x01
-#define TYPE_PROCESSOR      0x03    /* HP scanners use this */
-#define TYPE_WORM           0x04    /* Treated as ROM by our system */
-#define TYPE_ROM            0x05
-#define TYPE_SCANNER        0x06
-#define TYPE_MOD            0x07    /* Magneto-optical disk - 
-				     * - treated as TYPE_DISK */
-#define TYPE_NO_LUN         0x7f
-
-
-/*
- *  MESSAGE CODES
- */
-
-#define COMMAND_COMPLETE    0x00
-#define EXTENDED_MESSAGE    0x01
-#define     EXTENDED_MODIFY_DATA_POINTER    0x00
-#define     EXTENDED_SDTR                   0x01
-#define     EXTENDED_EXTENDED_IDENTIFY      0x02    /* SCSI-I only */
-#define     EXTENDED_WDTR                   0x03
-#define SAVE_POINTERS       0x02
-#define RESTORE_POINTERS    0x03
-#define DISCONNECT          0x04
-#define INITIATOR_ERROR     0x05
-#define ABORT               0x06
-#define MESSAGE_REJECT      0x07
-#define NOP                 0x08
-#define MSG_PARITY_ERROR    0x09
-#define LINKED_CMD_COMPLETE 0x0a
-#define LINKED_FLG_CMD_COMPLETE 0x0b
-#define BUS_DEVICE_RESET    0x0c
-
-#define INITIATE_RECOVERY   0x0f            /* SCSI-II only */
-#define RELEASE_RECOVERY    0x10            /* SCSI-II only */
-
-#define SIMPLE_QUEUE_TAG    0x20
-#define HEAD_OF_QUEUE_TAG   0x21
-#define ORDERED_QUEUE_TAG   0x22
-
-/*
- * Here are some scsi specific ioctl commands which are sometimes useful.
- */
-/* These are a few other constants  only used by scsi  devices */
-
-#define SCSI_IOCTL_GET_IDLUN 0x5382
-
-/* Used to turn on and off tagged queuing for scsi devices */
-
-#define SCSI_IOCTL_TAGGED_ENABLE 0x5383
-#define SCSI_IOCTL_TAGGED_DISABLE 0x5384
-
-/* Used to obtain the host number of a device. */
-#define SCSI_IOCTL_PROBE_HOST 0x5385
-
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4 
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */
-
-#endif
diff -u --recursive --new-file v1.3.97/linux/include/linux/scsicam.h linux/include/linux/scsicam.h
--- v1.3.97/linux/include/linux/scsicam.h	Thu Sep 21 08:59:23 1995
+++ linux/include/linux/scsicam.h	Thu Jan  1 02:00:00 1970
@@ -1,17 +0,0 @@
-/*
- * scsicam.h - SCSI CAM support functions, use for HDIO_GETGEO, etc.
- *
- * Copyright 1993, 1994 Drew Eckhardt
- *      Visionary Computing 
- *      (Unix and Linux consulting and custom programming)
- *      drew@Colorado.EDU
- *	+1 (303) 786-7975
- *
- * For more information, please consult the SCSI-CAM draft.
- */
-
-#ifndef SCSICAM_H
-#define SCSICAM_H
-#include <linux/kdev_t.h>
-extern int scsicam_bios_param (Disk *disk, kdev_t dev, int *ip);
-#endif /* def SCSICAM_H */
diff -u --recursive --new-file v1.3.97/linux/include/linux/timex.h linux/include/linux/timex.h
--- v1.3.97/linux/include/linux/timex.h	Sat Apr 27 15:20:07 1996
+++ linux/include/linux/timex.h	Sat May  4 19:39:22 1996
@@ -222,7 +222,7 @@
 #define TIME_INS	1	/* insert leap second */
 #define TIME_DEL	2	/* delete leap second */
 #define TIME_OOP	3	/* leap second in progress */
-#define TIME_WAIT	4	/* leap second has occured */
+#define TIME_WAIT	4	/* leap second has occurred */
 #define TIME_ERROR	5	/* clock not synchronized */
 #define TIME_BAD	TIME_ERROR /* bw compat */
 
diff -u --recursive --new-file v1.3.97/linux/include/net/route.h linux/include/net/route.h
--- v1.3.97/linux/include/net/route.h	Fri Apr 12 15:52:09 1996
+++ linux/include/net/route.h	Fri May  3 16:24:02 1996
@@ -15,7 +15,6 @@
  *		Alexey Kuznetsov:	Major changes for new routing code.
  *
  *	FIXME:
- *		Modules stuff is broken at the moment.
  *		Make atomic ops more generic and hide them in asm/...
  *
  *		This program is free software; you can redistribute it and/or
@@ -33,7 +32,7 @@
  * 1 - rare events and bugs situations (default)
  * 2 - trace mode.
  */
-#define RT_CACHE_DEBUG		1
+#define RT_CACHE_DEBUG		0
 
 #define RT_HASH_DIVISOR	    	256
 #define RT_CACHE_SIZE_MAX    	256
diff -u --recursive --new-file v1.3.97/linux/include/scsi/scsi.h linux/include/scsi/scsi.h
--- v1.3.97/linux/include/scsi/scsi.h	Thu Jan  1 02:00:00 1970
+++ linux/include/scsi/scsi.h	Thu May  2 07:48:55 1996
@@ -0,0 +1,198 @@
+#ifndef _LINUX_SCSI_H
+#define _LINUX_SCSI_H
+
+/*
+ * This header file contains public constants and structures used by
+ * the scsi code for linux.
+ */
+
+/*
+    $Header: /usr/src/linux/include/linux/RCS/scsi.h,v 1.3 1993/09/24 12:20:33 drew Exp $
+
+    For documentation on the OPCODES, MESSAGES, and SENSE values,
+    please consult the SCSI standard.
+
+*/
+
+/*
+ *      SCSI opcodes
+ */
+
+#define TEST_UNIT_READY       0x00
+#define REZERO_UNIT           0x01
+#define REQUEST_SENSE         0x03
+#define FORMAT_UNIT           0x04
+#define READ_BLOCK_LIMITS     0x05
+#define REASSIGN_BLOCKS       0x07
+#define READ_6                0x08
+#define WRITE_6               0x0a
+#define SEEK_6                0x0b
+#define READ_REVERSE          0x0f
+#define WRITE_FILEMARKS       0x10
+#define SPACE                 0x11
+#define INQUIRY               0x12
+#define RECOVER_BUFFERED_DATA 0x14
+#define MODE_SELECT           0x15
+#define RESERVE               0x16
+#define RELEASE               0x17
+#define COPY                  0x18
+#define ERASE                 0x19
+#define MODE_SENSE            0x1a
+#define START_STOP            0x1b
+#define RECEIVE_DIAGNOSTIC    0x1c
+#define SEND_DIAGNOSTIC       0x1d
+#define ALLOW_MEDIUM_REMOVAL  0x1e
+
+#define SET_WINDOW            0x24
+#define READ_CAPACITY         0x25
+#define READ_10               0x28
+#define WRITE_10              0x2a
+#define SEEK_10               0x2b
+#define WRITE_VERIFY          0x2e
+#define VERIFY                0x2f
+#define SEARCH_HIGH           0x30
+#define SEARCH_EQUAL          0x31
+#define SEARCH_LOW            0x32
+#define SET_LIMITS            0x33
+#define PRE_FETCH             0x34
+#define READ_POSITION         0x34
+#define SYNCHRONIZE_CACHE     0x35
+#define LOCK_UNLOCK_CACHE     0x36
+#define READ_DEFECT_DATA      0x37
+#define MEDIUM_SCAN           0x38
+#define COMPARE               0x39
+#define COPY_VERIFY           0x3a
+#define WRITE_BUFFER          0x3b
+#define READ_BUFFER           0x3c
+#define UPDATE_BLOCK          0x3d
+#define READ_LONG             0x3e
+#define WRITE_LONG            0x3f
+#define CHANGE_DEFINITION     0x40
+#define WRITE_SAME            0x41
+#define LOG_SELECT            0x4c
+#define LOG_SENSE             0x4d
+#define MODE_SELECT_10        0x55
+#define MODE_SENSE_10         0x5a
+#define READ_12               0xa8
+#define WRITE_12              0xaa
+#define WRITE_VERIFY_12       0xae
+#define SEARCH_HIGH_12        0xb0
+#define SEARCH_EQUAL_12       0xb1
+#define SEARCH_LOW_12         0xb2
+#define SEND_VOLUME_TAG       0xb6
+#define WRITE_LONG_2          0xea
+
+/*
+ *  Status codes
+ */
+
+#define GOOD                 0x00
+#define CHECK_CONDITION      0x01
+#define CONDITION_GOOD       0x02
+#define BUSY                 0x04
+#define INTERMEDIATE_GOOD    0x08
+#define INTERMEDIATE_C_GOOD  0x0a
+#define RESERVATION_CONFLICT 0x0c
+#define QUEUE_FULL           0x1a
+
+#define STATUS_MASK          0x1e
+
+/*
+ *  SENSE KEYS
+ */
+
+#define NO_SENSE            0x00
+#define RECOVERED_ERROR     0x01
+#define NOT_READY           0x02
+#define MEDIUM_ERROR        0x03
+#define HARDWARE_ERROR      0x04
+#define ILLEGAL_REQUEST     0x05
+#define UNIT_ATTENTION      0x06
+#define DATA_PROTECT        0x07
+#define BLANK_CHECK         0x08
+#define COPY_ABORTED        0x0a
+#define ABORTED_COMMAND     0x0b
+#define VOLUME_OVERFLOW     0x0d
+#define MISCOMPARE          0x0e
+
+
+/*
+ *  DEVICE TYPES
+ */
+
+#define TYPE_DISK           0x00
+#define TYPE_TAPE           0x01
+#define TYPE_PROCESSOR      0x03    /* HP scanners use this */
+#define TYPE_WORM           0x04    /* Treated as ROM by our system */
+#define TYPE_ROM            0x05
+#define TYPE_SCANNER        0x06
+#define TYPE_MOD            0x07    /* Magneto-optical disk - 
+				     * - treated as TYPE_DISK */
+#define TYPE_NO_LUN         0x7f
+
+
+/*
+ *  MESSAGE CODES
+ */
+
+#define COMMAND_COMPLETE    0x00
+#define EXTENDED_MESSAGE    0x01
+#define     EXTENDED_MODIFY_DATA_POINTER    0x00
+#define     EXTENDED_SDTR                   0x01
+#define     EXTENDED_EXTENDED_IDENTIFY      0x02    /* SCSI-I only */
+#define     EXTENDED_WDTR                   0x03
+#define SAVE_POINTERS       0x02
+#define RESTORE_POINTERS    0x03
+#define DISCONNECT          0x04
+#define INITIATOR_ERROR     0x05
+#define ABORT               0x06
+#define MESSAGE_REJECT      0x07
+#define NOP                 0x08
+#define MSG_PARITY_ERROR    0x09
+#define LINKED_CMD_COMPLETE 0x0a
+#define LINKED_FLG_CMD_COMPLETE 0x0b
+#define BUS_DEVICE_RESET    0x0c
+
+#define INITIATE_RECOVERY   0x0f            /* SCSI-II only */
+#define RELEASE_RECOVERY    0x10            /* SCSI-II only */
+
+#define SIMPLE_QUEUE_TAG    0x20
+#define HEAD_OF_QUEUE_TAG   0x21
+#define ORDERED_QUEUE_TAG   0x22
+
+/*
+ * Here are some scsi specific ioctl commands which are sometimes useful.
+ */
+/* These are a few other constants  only used by scsi  devices */
+
+#define SCSI_IOCTL_GET_IDLUN 0x5382
+
+/* Used to turn on and off tagged queuing for scsi devices */
+
+#define SCSI_IOCTL_TAGGED_ENABLE 0x5383
+#define SCSI_IOCTL_TAGGED_DISABLE 0x5384
+
+/* Used to obtain the host number of a device. */
+#define SCSI_IOCTL_PROBE_HOST 0x5385
+
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4 
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
+
+#endif
diff -u --recursive --new-file v1.3.97/linux/include/scsi/scsi_ioctl.h linux/include/scsi/scsi_ioctl.h
--- v1.3.97/linux/include/scsi/scsi_ioctl.h	Thu Jan  1 02:00:00 1970
+++ linux/include/scsi/scsi_ioctl.h	Thu May  2 07:48:55 1996
@@ -0,0 +1,21 @@
+#ifndef _SCSI_IOCTL_H
+#define _SCSI_IOCTL_H 
+
+#define SCSI_IOCTL_SEND_COMMAND 1
+#define SCSI_IOCTL_TEST_UNIT_READY 2
+#define SCSI_IOCTL_BENCHMARK_COMMAND 3
+#define SCSI_IOCTL_SYNC 4			/* Request synchronous parameters */
+/* The door lock/unlock constants are compatible with Sun constants for
+   the cdrom */
+#define SCSI_IOCTL_DOORLOCK 0x5380		/* lock the eject mechanism */
+#define SCSI_IOCTL_DOORUNLOCK 0x5381		/* unlock the mechanism	  */
+
+#define	SCSI_REMOVAL_PREVENT	1
+#define	SCSI_REMOVAL_ALLOW	0
+
+extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
+extern int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
+
+#endif
+
+
diff -u --recursive --new-file v1.3.97/linux/include/scsi/scsicam.h linux/include/scsi/scsicam.h
--- v1.3.97/linux/include/scsi/scsicam.h	Thu Jan  1 02:00:00 1970
+++ linux/include/scsi/scsicam.h	Thu May  2 07:48:55 1996
@@ -0,0 +1,17 @@
+/*
+ * scsicam.h - SCSI CAM support functions, use for HDIO_GETGEO, etc.
+ *
+ * Copyright 1993, 1994 Drew Eckhardt
+ *      Visionary Computing 
+ *      (Unix and Linux consulting and custom programming)
+ *      drew@Colorado.EDU
+ *	+1 (303) 786-7975
+ *
+ * For more information, please consult the SCSI-CAM draft.
+ */
+
+#ifndef SCSICAM_H
+#define SCSICAM_H
+#include <linux/kdev_t.h>
+extern int scsicam_bios_param (Disk *disk, kdev_t dev, int *ip);
+#endif /* def SCSICAM_H */
diff -u --recursive --new-file v1.3.97/linux/include/scsi/sg.h linux/include/scsi/sg.h
--- v1.3.97/linux/include/scsi/sg.h	Thu Jan  1 02:00:00 1970
+++ linux/include/scsi/sg.h	Thu May  2 07:48:55 1996
@@ -0,0 +1,41 @@
+/*
+   History:
+    Started: Aug 9 by Lawrence Foard (entropy@world.std.com), to allow user 
+     process control of SCSI devices.
+    Development Sponsored by Killy Corp. NY NY
+*/
+
+#ifndef _SCSI_GENERIC_H
+#define _SCSI_GENERIC_H
+
+/* 
+ An SG device is accessed by writing "packets" to it, the replies
+ are then read using the read call. The same header is used for 
+ reply, just ignore reply_len field.
+*/
+
+struct sg_header
+ {
+  int pack_len;    /* length of incoming packet <4096 (including header) */
+  int reply_len;   /* maximum length <4096 of expected reply */
+  int pack_id;     /* id number of packet */
+  int result;      /* 0==ok, otherwise refer to errno codes */
+  unsigned int twelve_byte:1; /* Force 12 byte command length for group 6 & 7 commands  */
+  unsigned int other_flags:31;			/* for future use */
+  unsigned char sense_buffer[16]; /* used only by reads */
+  /* command follows then data for command */
+ };
+
+/* ioctl's */
+#define SG_SET_TIMEOUT 0x2201  /* set timeout *(int *)arg==timeout */
+#define SG_GET_TIMEOUT 0x2202  /* get timeout return timeout */
+
+#define SG_DEFAULT_TIMEOUT (60*HZ) /* 1 minute timeout */
+#define SG_DEFAULT_RETRIES 1
+
+#define SG_MAX_QUEUE 4 /* maximum outstanding request, arbitrary, may be
+			  changed if sufficient DMA buffer room available */
+
+#define SG_BIG_BUFF 32768
+
+#endif
diff -u --recursive --new-file v1.3.97/linux/kernel/sys.c linux/kernel/sys.c
--- v1.3.97/linux/kernel/sys.c	Wed Apr 17 09:06:33 1996
+++ linux/kernel/sys.c	Thu May  2 08:06:31 1996
@@ -4,6 +4,7 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
+#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -22,6 +23,9 @@
 #include <linux/fcntl.h>
 #include <linux/acct.h>
 #include <linux/tty.h>
+#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
+#include <linux/apm_bios.h>
+#endif
 
 #include <asm/segment.h>
 #include <asm/io.h>
@@ -193,6 +197,9 @@
 	else if (flag == 0xCDEF0123) {
 		printk(KERN_EMERG "System halted\n");
 		sys_kill(-1, SIGKILL);
+#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
+		apm_set_power_state(APM_STATE_OFF);
+#endif
 		do_exit(0);
 	} else
 		return -EINVAL;
diff -u --recursive --new-file v1.3.97/linux/mm/filemap.c linux/mm/filemap.c
--- v1.3.97/linux/mm/filemap.c	Mon Apr 29 18:05:21 1996
+++ linux/mm/filemap.c	Sat May  4 19:39:22 1996
@@ -422,7 +422,7 @@
  * - The number of effective pending IO read requests.
  *   ONE seems to be the only reasonable value.
  * - The total memory pool usage for the file access stream.
- *   This maximum memory usage is implicitely 2 IO read chunks:
+ *   This maximum memory usage is implicitly 2 IO read chunks:
  *   2*(MAX_READAHEAD + PAGE_SIZE) = 156K if CONFIG_READA_SMALL is undefined,
  *   64k if defined.
  */
diff -u --recursive --new-file v1.3.97/linux/net/Config.in linux/net/Config.in
--- v1.3.97/linux/net/Config.in	Wed Apr 10 17:02:26 1996
+++ linux/net/Config.in	Tue Apr 30 13:45:06 1996
@@ -20,8 +20,10 @@
   bool 'AX.25 over Ethernet' CONFIG_BPQETHER
   bool 'Amateur Radio NET/ROM' CONFIG_NETROM
 fi
-bool 'Bridging (test)' CONFIG_BRIDGE
-bool 'Kernel/User network link driver(ALPHA)' CONFIG_NETLINK
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  bool 'Bridging (EXPERIMENTAL)' CONFIG_BRIDGE
+fi
+bool 'Kernel/User network link driver' CONFIG_NETLINK
 if [ "$CONFIG_NETLINK" = "y" ]; then
   bool 'Routing messages' CONFIG_RTNETLINK
 fi
diff -u --recursive --new-file v1.3.97/linux/net/ax25/ax25_out.c linux/net/ax25/ax25_out.c
--- v1.3.97/linux/net/ax25/ax25_out.c	Sat Apr 27 15:20:10 1996
+++ linux/net/ax25/ax25_out.c	Tue Apr 30 12:42:37 1996
@@ -405,6 +405,9 @@
 	/* gives better performance on FLEXNET nodes. (Why, Gunter?)	  */
 
 	for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) {
+		if (ax25o == ax25)
+			continue;
+
 		if (ax25o->device != ax25->device)
 			continue;
 
diff -u --recursive --new-file v1.3.97/linux/net/ax25/ax25_route.c linux/net/ax25/ax25_route.c
--- v1.3.97/linux/net/ax25/ax25_route.c	Fri Apr 12 15:52:11 1996
+++ linux/net/ax25/ax25_route.c	Tue Apr 30 12:42:37 1996
@@ -488,7 +488,7 @@
 	
 	/*
 	 *	Bind to the physical interface we heard them on, or the default
-	 *	route if non is found;
+	 *	route if none is found;
 	 */
 	for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
 		if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL)
diff -u --recursive --new-file v1.3.97/linux/net/core/dev.c linux/net/core/dev.c
--- v1.3.97/linux/net/core/dev.c	Sat Apr 27 15:20:10 1996
+++ linux/net/core/dev.c	Tue Apr 30 12:42:37 1996
@@ -549,7 +549,7 @@
 
 /*
  *	When we are called the queue is ready to grab, the interrupts are
- *	on and hardware can interrupt and queue to the receive queue a we
+ *	on and hardware can interrupt and queue to the receive queue as we
  *	run with no problems.
  *	This is run as a bottom half after an interrupt handler that does
  *	mark_bh(NET_BH);
diff -u --recursive --new-file v1.3.97/linux/net/ipv4/Config.in linux/net/ipv4/Config.in
--- v1.3.97/linux/net/ipv4/Config.in	Mon Apr 29 18:05:21 1996
+++ linux/net/ipv4/Config.in	Tue Apr 30 13:46:21 1996
@@ -15,15 +15,17 @@
     bool 'IP: masquerading (ALPHA)' CONFIG_IP_MASQUERADE
   fi
   if [ "$CONFIG_IP_MULTICAST" = "y" ]; then
-    bool 'IP: multicast routing(in progress)' CONFIG_IP_MROUTE
+    bool 'IP: multicast routing (EXPERIMENTAL)' CONFIG_IP_MROUTE
   fi
 fi
 if [ "$CONFIG_NET_ALIAS" = "y" ]; then
 	tristate 'IP: aliasing support' CONFIG_IP_ALIAS
 fi
-#if [ "$CONFIG_KERNELD" = "y" ]; then
-#	bool 'IP: ARP daemon support (experimental)' CONFIG_ARPD
-#fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  if [ "$CONFIG_KERNELD" = "y" ]; then
+    bool 'IP: ARP daemon support (EXPERIMENTAL)' CONFIG_ARPD
+  fi
+fi
 comment '(it is safe to leave these untouched)'
 bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP
 tristate 'IP: Reverse ARP' CONFIG_INET_RARP
diff -u --recursive --new-file v1.3.97/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v1.3.97/linux/net/ipv4/af_inet.c	Fri Apr 12 15:52:11 1996
+++ linux/net/ipv4/af_inet.c	Tue Apr 30 12:42:37 1996
@@ -417,8 +417,11 @@
 	{
 		/* this should never happen. */
 		/* actually it can if an ack has just been sent. */
-		printk("Socket destroy delayed (r=%d w=%d)\n",
-			sk->rmem_alloc, sk->wmem_alloc);
+		/*
+		 *	Make this a NETDEBUG at 2.0pre
+		 */
+		/*NETDEBUG(*/printk("Socket destroy delayed (r=%d w=%d)\n",
+			sk->rmem_alloc, sk->wmem_alloc)/*)*/;
 		sk->destroy = 1;
 		sk->ack_backlog = 0;
 		release_sock(sk);
diff -u --recursive --new-file v1.3.97/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v1.3.97/linux/net/ipv4/ip_sockglue.c	Sat Apr 27 15:20:10 1996
+++ linux/net/ipv4/ip_sockglue.c	Sat May  4 19:39:22 1996
@@ -10,7 +10,7 @@
  * Fixes:
  *		Many		:	Split from ip.c , see ip.c for history.
  *		Martin Mares	:	TOS setting fixed.
- *		Alan Cox	:	Fixed a couple of oopses in Martins 
+ *		Alan Cox	:	Fixed a couple of oopses in Martin's 
  *					TOS tweaks.
  */
 
diff -u --recursive --new-file v1.3.97/linux/scripts/Menuconfig linux/scripts/Menuconfig
--- v1.3.97/linux/scripts/Menuconfig	Tue Apr 23 13:57:14 1996
+++ linux/scripts/Menuconfig	Fri May  3 11:55:12 1996
@@ -222,6 +222,7 @@
                         /^$var[ 	]*\$/d
                         /^#.*/d
 			/^[ 	]*\$/q
+                        s/^  //
                         p
                     }" Documentation/Configure.help)
 
