diff -u --recursive --new-file pre2.0.12/linux/Documentation/00-INDEX linux/Documentation/00-INDEX
--- pre2.0.12/linux/Documentation/00-INDEX	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/00-INDEX	Thu Jun  6 14:57:43 1996
@@ -0,0 +1,69 @@
+This is a brief list of all the files in ./linux/Documentation and what
+they contain. If you add a documentation file, please list it here in 
+alphabetical order as well. Note that subdirectories have their own
+index files too.
+							 Thanks -- Paul.
+
+00-INDEX
+	- this file.
+BUG-HUNTING
+	- brute force method of doing binary search of patches to find bug.
+Changes
+	- list of changes that break older software packages.
+CodingStyle
+	- how the boss likes the C code in the kernel to look.
+Configure.help
+	- text file that is used for help when you run "make config"
+SMP.txt
+	- notes, and "To Fix" list for multi-processor Linux. (see smp.tex)
+cdrom/
+	- directory with information on the CD-ROM drivers that Linux has.
+devices.tex
+	- TeX source listing of all the nodes in /dev/ with major minor #'s
+devices.txt
+	- plain ASCII listing of all the nodes in /dev/ with major minor #'s
+digiboard.txt
+	- info on the Digiboard PC/X{i,e,eve} multiport boards.
+filesystems/
+	- directory with info on the various filesystems that Linux supports.
+ide.txt
+	- important info for users of ATA devices (IDE/EIDE disks and CD-ROMS)
+initrd.txt
+	- how to use the RAM disk as an initial/temporary root filesystem.
+ioctl-number.txt
+	- how to implement and register device/driver ioctl calls.
+isdn/
+	- directory with info on the linux ISDN support, and supported cards.
+java.txt
+	- info on the in-kernel binary support for Java(tm)
+locks.txt
+	- info on file locking implementations, flock() vs. fcntl(), etc.
+magic-number.txt
+	- list of magic numbers used to mark/protect kernel data structures.
+mandatory.txt
+	- info on the linux implementation of Sys V mandatory file locking.
+modules.txt
+	- short guide on how to make kernel parts into loadable modules
+networking/
+	- directory with info on various linux networking aspects.
+nfsroot.txt
+	- short guide on setting up a diskless box with NFS root filesystem
+oops-tracing.txt
+	- how to decode those nasty internal kernel error dump messages.
+ramdisk.txt
+	- short guide on how to set up and use the RAM disk.
+riscom8.txt
+	- notes on using the RISCom/8 multi-port serial driver.
+rtc.txt
+	- notes on how to use the Real Time Clock (aka CMOS clock) driver.
+scsi.txt
+	- short blurb on using SCSI support as a module.
+smp.tex
+	- TeX document describing implementation of Multiprocessor Linux
+svga.txt
+	- short guide on selecting video modes at boot via VGA BIOS.
+unicode.txt
+	- info on the Unicode character/font mapping used in Linux.
+watchdog.txt
+	- how to auto-reboot Linux if it has "fallen and can't get up". ;-)
+
diff -u --recursive --new-file pre2.0.12/linux/Documentation/Changes linux/Documentation/Changes
--- pre2.0.12/linux/Documentation/Changes	Wed Jun  5 10:41:27 1996
+++ linux/Documentation/Changes	Wed Jun  5 17:47:00 1996
@@ -18,7 +18,7 @@
 Taylor.  Check out http://www.cviog.uga.edu/LinuxBleed.html if you
 prefer a HTML-ized shopping list.
 
-Last updated: June 1, 1996.
+Last updated: June 5, 1996.
 Current Author: Chris Ricker (gt1355b@prism.gatech.edu).
 
 Current Releases
@@ -34,7 +34,7 @@
 - Termcap		 2.0.8
 - Procps		 0.99a
 - Gpm			 1.09
-- sysvinit		 2.62
+- SysVinit		 2.62
 - Util-linux		 2.5
 
 Upgrade notes
@@ -55,6 +55,21 @@
 /etc/sysconfig/network-scripts/ifup-lo, changing the line `route add
 -net $(IPADDR)' to `route add -net 127.0.0.0' and you should be fine.
 
+Booting Changes
+===============
+
+   The boot stuff in 1.3.x (for arch/i386) has been enhanced so that it
+now can load bigger kernels (bzImage) and that the loaders now can load
+an initial ramdisk (initrd). For initrd see Documentation/initrd.txt.
+For building bigger kernels use one of the following make targets:
+bzImage, bzlilo, bzdisk (equivalent to make targets zImage, zlilo, and
+zdisk respectively).  If you want or need to use the new features
+you'll need to upgrade your bootloaders.  Lilo can be found at
+ftp://lrcftp.epfl.ch/pub/linux/local/lilo/lilo.19.tar.gz.  LOADLIN is at
+ftp://sunsite.unc.edu/pub/Linux/system/Linux-boot/lodlin16.tgz.  If
+you're using more unusual loaders like SysLinux or etherboot, the
+latest versions are 1.3 and 2.0, respectively.
+
 The Linux C Library
 ===================
 
@@ -154,8 +169,8 @@
 latest stable release is 2.2.0f and is available at
 ftp://sunsite.unc.edu/pub/Linux/system/Network/serial/ppp/ppp-2.2.0f.tar.gz.
 
-Named pipes
-===========
+Named pipes (SysVinit)
+======================
 
    Linux's handling of named pipes changed (it now does it The Right Way
 instead of the SunOS way ;-).  This broke some programs that depended
@@ -164,9 +179,7 @@
 your computer shuts down fine but "INIT: error reading initrequest" or
 words to that effect scroll across your screen hundreds of times.  To
 fix, upgrade to
-ftp://sunsite.unc.edu/pub/Linux/system/Daemons/init/sysvinit-2.62.tar.gz
-ftp://tsx-11.mit.edu/pub/linux/sources/sbin/sysvinit-2.62.tar.gz
-ftp://ftp.cistron.nl/pub/people/miquels/software/sysvinit-2.62.tar.gz.
+ftp://ftp.cistron.nl/pub/people/miquels/debian/sysvinit-2.62.tar.gz.
 
    If you're trying to run NCSA httpd, you have to set pre-spawning of
 daemons to zero, as it incorrectly assumes SunOS behavior.  I recommend
@@ -187,6 +200,17 @@
 changing them to read-write) will fix any program that is broken
 because of this change.
 
+File Locking (Sendmail)
+=======================
+
+   As of pre2.0.6 (aka 1.99.6), mixed-style file locking is no longer
+allowed.  For example, a file cannot be simultaneously locked with
+`flock' (BSD-style) and `lockf' (SYSV-style).  Among the programs this
+has impacted are older sendmails.  If you get a message that sendmail
+cannot lock aliases.dir (or other files), you'll need to upgrade to at
+least 8.7.x.  The latest sendmail is at
+ftp://ftp.cs.berkeley.edu/ucb/src/sendmail/sendmail.8.7.5.tar.gz.
+
 Uugetty
 =======
 
@@ -299,7 +323,7 @@
 ====
 
    If tcsh acts funny, get the source from
-ftp://tesla.ee.cornell.edu/pub/tcsh and add #define SYSMALLOC in
+ftp://anise.ee.cornell.edu/pub/tcsh and add #define SYSMALLOC in
 config_f.h before recompiling tcsh.  Binaries can be found in
 ftp://sunsite.unc.edu/pub/Linux/system/Shells/ and a corrected one will
 probably wind up there eventually.
@@ -351,6 +375,20 @@
 /usr/include/mntent.h.  I've uploaded this to sunsite as
 ftp://sunsite.unc.edu/pub/Linux/system/Admin/quotas-1.51-tar.gz
 
+Process Accounting
+==================
+
+   Process accounting support has also been integrated into the new
+kernels. To use this feature, you'll need to get
+ftp://iguana.hut.fi/pub/linux/Kernel/process_accounting/acct_1.3.73.tar.gz.
+
+Bdflush
+=======
+
+   Bdflush has also been integrated into the new kernels, so those of
+you using it on older systems no longer need to hunt for the patches to
+implement it once you upgrade to 1.3.x.
+
 APM support
 ===========
 
@@ -368,7 +406,9 @@
 
    For a version of Dosemu that works (well, at least as well as DOS
 ever works ;-), get
-ftp://tsx-11.mit.edu/pub/linux/ALPHA/dosemu/Development/dosemu-0.63.1.8.tgz.
+ftp://tsx-11.mit.edu/pub/linux/ALPHA/dosemu/Development/dosemu-0.63.1.19.tgz.
+Be sure to follow the instructions in README.newkernels about patching
+your include files, or it will not compile.
 
 Mtools and Fdutils
 ==================
@@ -500,7 +540,7 @@
 SysVinit utilities
 ==================
 
-ftp://sunsite.unc.edu/pub/Linux/system/Daemons/init/sysvinit-2.60.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/system/Daemons/init/sysvinit-2.62.tar.gz
 
 Util-linux
 ==========
diff -u --recursive --new-file pre2.0.12/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- pre2.0.12/linux/Documentation/Configure.help	Sat Jun  1 20:11:29 1996
+++ linux/Documentation/Configure.help	Wed Jun  5 14:42:26 1996
@@ -1250,7 +1250,7 @@
   whenever you want). If you want to compile it as a module, say M
   here and read Documentation/modules.txt.
 
-Adaptec AHA152X support
+Adaptec AHA152X/2825 support
 CONFIG_SCSI_AHA152X
   This is support for a SCSI host adaptor. It is explained in section
   3.3 of the SCSI-HOWTO, available via ftp (user: anonymous) at
@@ -2991,6 +2991,16 @@
   SMB support as a module ( = code which can be inserted in and
   removed from the running kernel whenever you want), say M here and
   read Documentation/modules.txt. Most people say N, however.
+
+SMB long filename support (EXPERIMENTAL)
+CONFIG_SMB_LONG
+  SMBFS was designed to support long filenames using the LanManager
+  2.0 protocol. I had to find out that the support for long filenames
+  sometimes causes problems, which can even result in kernel OOPSes. I
+  did not yet find out what the problem is, but hopefully I will find
+  this bug eventually. As many people seem to run long filenames with
+  no problems, I leave this support in the kernel as an option. The
+  careful among you should say N here.
 
 NCP filesystem support (to mount NetWare volumes)
 CONFIG_NCP_FS
diff -u --recursive --new-file pre2.0.12/linux/Documentation/cdrom/00-INDEX linux/Documentation/cdrom/00-INDEX
--- pre2.0.12/linux/Documentation/cdrom/00-INDEX	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/cdrom/00-INDEX	Thu Jun  6 14:57:43 1996
@@ -0,0 +1,29 @@
+00-INDEX
+	- this file (info on CD-ROMs and Linux)
+aztcd
+	- info on Aztech/Orchid/Okano/Wearnes/Conrad/CyCDROM driver.
+cdrom-standard.tex
+	- LaTeX document on standardizing the CD-ROM programming interface.
+cdu31a
+	- info on the Sony CDU31A/CDU33A CD-ROM driver.
+cm206
+	- info on the Philips/LMS cm206/cm260 CD-ROM driver.
+gscd
+	- info on the Goldstar R420 CD-ROM driver.
+ide-cd
+	- info on setting up and using ATAPI (aka IDE) CD-ROMs.
+isp16
+	- info on the CD-ROM interface on ISP16, MAD16 or Mozart sound card.
+mcd
+	- info on limitations of standard Mitsumi CD-ROM driver.
+mcdx
+	- info on improved Mitsumi CD-ROM driver.
+optcd
+	- info on the Optics Storage 8000 AT CD-ROM driver
+sbpcd
+	- info on the SoundBlaster Pro CD-ROM interface driver.
+sjcd
+	- info on the SANYO CDR-H94A CD-ROM interface driver.
+sonycd535
+	- info on the Sony CDU-535 (and 531) CD-ROM driver.
+
diff -u --recursive --new-file pre2.0.12/linux/Documentation/cdrom/mcdx linux/Documentation/cdrom/mcdx
--- pre2.0.12/linux/Documentation/cdrom/mcdx	Tue May 21 19:52:31 1996
+++ linux/Documentation/cdrom/mcdx	Wed Jun  5 10:47:05 1996
@@ -1,46 +1,44 @@
-This actually is an `improved' driver for the Mitsumi CD-ROM drives. 
+This is a first attempt to create an `improved' driver for the Mitsumi drives.
+It is able to "live together" with mcd.c, if you have at least two Mitsumi
+drives: each driver can use his own drive.
+
+To allow this "coexistence" as long as mcdx.c is not a superset of mcd.c,
+this driver has to use its own device files. We use MAJOR 20 for it. So,
+you have to do
 
-We are using the major device number 20 for it. So, you have to do
-
-~# mknod /dev/mcdx0 b 20 0
-~# mknod /dev/mcdx1 b 20 1
+ # mknod /dev/mcdx0 b 20 0
+ # mknod /dev/mcdx1 b 20 1
 
 and so on, one entry for each drive to support, once.
 
-If you are using the driver as a module, you can specify the ports and IRQs
-like:
-
- # insmod mcdx.o mcdx=0x300,11
+If you are using the driver as a module, you can specify your ports and IRQs
+like
 
-and so on ("address,IRQ" pairs). When You intend to use more then one
-drive, it's necessary to edit the mcdx.h file found in
-/usr/src/linux/include/linux. Instead of providing the values on the
-command line, You can "hardwire" them all in mcdx.h. The command line
-values take precedence over the values in mcdx.h. 
+ # insmod mcdx.o mcdx=0x300,11,0x304,5
 
-WARNING: BE CAREFUL TO SUPPLY THE CORRECT VALUES OTHERWISE THE WHOLE SYSTEM
-WILL HANG DURING BOOT-UP OR LOADING OF THE DRIVER!!!
+and so on ("address,IRQ" pairs).
+This will override the configuration in mcdx.h.
 
 This driver:
 
-	o	Handles XA and multi session CDs as well as ordinary CDs.
-	o	Supports up to 5 drives (of course, you'll need free 
-		IRQs, i/o ports and slots).
-	o	Uses *much* less kernel memory than the standard mcd driver.
-	o 	Plays audio like the `old' driver.
+    o   handles XA (and hopefully) multi session CDs as well as
+        ordinary CDs;
+    o   supports up to 5 drives (of course, you'll need free 
+        IRQs, i/o ports and slots);
+    o   uses much less kernel memory than the standard mcd driver
+        (no extra driver internal buffers!).
+        o   plays audio (like the `old' driver, I hope)
 
 This version doesn't support yet:
 
-	o	Shared IRQs, due to the fact that otherwise it wouldn't be
-		possible to distinguish the drives issuing the interrupt in the 
-		corresponding handling routine.
+    o   shared IRQs (but it seems to be possible - I've successfully
+                connected two drives to the same irq.  So it's `only' a 
+                problem of the driver.)
 
 This driver never will:
 
-	o	Read digital audio (i.e. copy directly), due to missing
-		hardware features. 
-	o	Do DMA transfers. This is simply far less efficient with such 
-		a slow device. Further, the SoundBlaster interfaces doesn't
-		support it, so it's not worth the effort.
+    o   Read digital audio (i.e. copy directly), due to missing
+        hardware features. 
+
 
-1996/05/20 Marcin Dalecki <dalecki@namu03.gwdg.de>
+heiko@lotte.sax.de
diff -u --recursive --new-file pre2.0.12/linux/Documentation/filesystems/00-INDEX linux/Documentation/filesystems/00-INDEX
--- pre2.0.12/linux/Documentation/filesystems/00-INDEX	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/filesystems/00-INDEX	Thu Jun  6 14:57:43 1996
@@ -0,0 +1,16 @@
+00-INDEX
+	- this file (info on some of the filesystems supported by linux).
+affs.txt
+	- info and mount options for the Amiga Fast File System.
+hpfs.txt
+	- info and mount options for the OS/2 HPFS.
+ncpfs.txt
+	- info on Novell Netware(tm) filesystem using NCP protocol.
+smbfs.txt
+	- info on using filesystems with the SMB protocol (Win 3.11, Win NT)
+sysv-fs.txt
+	- info on the SystemV/Coherent filesystem.
+umsdos.txt
+	- info on the umsdos extensions to the msdos filesystem.
+vfat.txt
+	- info on using the VFAT filesystem used in Win NT and Win 95
diff -u --recursive --new-file pre2.0.12/linux/Documentation/isdn/00-INDEX linux/Documentation/isdn/00-INDEX
--- pre2.0.12/linux/Documentation/isdn/00-INDEX	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/isdn/00-INDEX	Thu Jun  6 14:57:43 1996
@@ -0,0 +1,21 @@
+00-INDEX
+	- this file (info on ISDN implementation for Linux)
+CREDITS
+	- list of the kind folks that brought you this stuff.
+INTERFACE
+	- description of Linklevel and Hardwarelevel ISDN interface.
+README
+	- general info on what you need and what to do for Linux ISDN.
+README.audio
+	- info for running audio over ISDN.
+README.icn
+	- info on the ICN-ISDN-card and its driver.
+README.pcbit
+	- info on the PCBIT-D ISDN adapter and driver.
+README.syncppp
+	- info on running Sync PPP over ISDN.
+README.teles
+	- info on driver for Teles compatible ISDN cards.
+syncPPP.FAQ
+	- frequently asked questions about running PPP over ISDN.
+
diff -u --recursive --new-file pre2.0.12/linux/Documentation/modules.txt linux/Documentation/modules.txt
--- pre2.0.12/linux/Documentation/modules.txt	Sat May 11 10:42:04 1996
+++ linux/Documentation/modules.txt	Thu Jun  6 14:57:43 1996
@@ -6,8 +6,15 @@
 In this kernel you also have a possibility to create modules that are
 less dependent on the kernel version.  This option can be selected
 during "make config", by enabling CONFIG_MODVERSIONS.
-Note: If you enable CONFIG_MODVERSIONS, you will need some utilities
-      from the latest module support package: "modules-1.1.8*.tar.gz"!
+
+Note: You should ensure that the modules-X.Y.Z.tar.gz you are using
+is the most up to date one for this kernel. The "X.Y.Z" will reflect
+the kernel version at the time of the release of the modules package.
+Some older modules packages aren't aware of some of the newer modular
+features that the kernel now supports. (If you are unsure, you can 
+usually find out what the current release of the modules-X.Y.Z.tar.gz
+package is by looking up the URL listed for "Bjorn Ekwall" in the 
+file ./linux/CREDITS)
 
 Anyway, your first step is to compile the kernel, as explained in the
 file README.  It generally goes like:
@@ -38,12 +45,8 @@
 	Most low-level SCSI drivers: (i.e. aha1542, in2000)
 	All SCSI high-level drivers: disk, tape, cdrom, generic.
 
-	Some ethernet drivers:
-		plip, slip, dummy,
-		de600, de620
-		3c501, 3c509
-		eexpress, depca,
-		ewrk3, apricot
+	Most ethernet drivers: (too many to list, please see the file
+				./Documentation/networking/net-modules.txt)
 
 	Most CDROM drivers:
 		aztcd:     Aztech,Orchid,Okano,Wearnes
@@ -80,14 +83,12 @@
 
 This will copy all newly made modules into subdirectories under
 "/lib/modules/kernel_release/", where "kernel_release" is something
-like 1.1.83, or whatever the current kernel version is...
+like 2.0.1, or whatever the current kernel version is...
 
 
 Nifty features:
 
-If you have installed the utilities from "modules-1.1.8*.tar.gz",
-you will have access to two new utilities: "modprobe" and "depmod"
-
+You have access to two utilities: "modprobe" and "depmod".
 Using the modprobe utility, you can load any module like this:
 
 	/sbin/modprobe module
diff -u --recursive --new-file pre2.0.12/linux/Documentation/networking/00-INDEX linux/Documentation/networking/00-INDEX
--- pre2.0.12/linux/Documentation/networking/00-INDEX	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/networking/00-INDEX	Thu Jun  6 14:57:43 1996
@@ -0,0 +1,31 @@
+00-INDEX
+	- this file
+3c505.txt
+	- information on the 3Com EtherLink Plus (3c505) driver.
+Configurable
+	- info on some of the configurable network parameters
+alias.txt
+	- info on using alias network devices 
+arcnet-hardware.txt
+	- tons of info on arcnet, hubs, arcnet card jumper settings, etc.
+arcnet.txt
+	- info on the using the arcnet driver itself.
+ax25.txt
+	- info on using AX.25 and NET/ROM code for Linux
+framerelay.txt
+	- info on using Frame Relay/Data Link Connection Identifier (DLCI).
+ncsa-telnet
+	- notes on how NCSA telnet (DOS) breaks with MTU discovery enabled.
+net-modules.txt
+	- info and "insmod" parameters for all network driver modules.
+ppp.txt
+	- info on what software you should use to run PPP.
+tcp.txt
+	- short blurb on how TCP output takes place.
+tulip.txt
+	- info on using DEC 21040/21041/21140 based PCI ethernet cards.
+vortex.txt
+	- info on using 3Com Vortex (3c590, 3c592, 3c595, 3c597) e'net cards.
+z8530drv.txt
+	- info about Linux driver for Z8530 based HDLC cards for AX.25
+
diff -u --recursive --new-file pre2.0.12/linux/Documentation/networking/Configurable linux/Documentation/networking/Configurable
--- pre2.0.12/linux/Documentation/networking/Configurable	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/networking/Configurable	Thu Jun  6 14:57:43 1996
@@ -0,0 +1,33 @@
+
+There are a few network parameters that can be tuned to better match
+the kernel to your system hardware and intended usage. The defaults
+are usually a good choice for 99% of the people 99% of the time, but
+you should be aware they do exist and can be changed.
+
+The current list of parameters can be found in the file:
+
+	./linux/net/TUNABLE
+
+Some of these are accessible via the sysctl interface, and many more are
+scheduled to be added in this way. For example, some parameters related 
+to Address Resolution Protocol (ARP) are very easily viewed and altered.
+
+	# cat /proc/sys/net/ipv4/arp_timeout
+	6000
+	# echo 7000 > /proc/sys/net/ipv4/arp_timeout
+	# cat /proc/sys/net/ipv4/arp_timeout
+	7000
+
+Others are already accessible via the related user space programs.
+For example, MAX_WINDOW has a default of 32k which is a good choice for
+modern hardware, but if you have a slow (8 bit) ethercard and/or a slow
+machine, then this will be far too big for the card to keep up with fast 
+Tx'ing machines on the same net, resulting in overruns and receive errors.
+A value of about 4k would be more appropriate, which can be set via:
+
+	# route add -net 192.168.3.0 window 4096
+
+The remainder of these can only be presently changed by altering a #define
+in the related header file. This means an edit and recompile cycle.
+
+						Paul Gortmaker 06/96
diff -u --recursive --new-file pre2.0.12/linux/Documentation/smp.tex linux/Documentation/smp.tex
--- pre2.0.12/linux/Documentation/smp.tex	Fri May 17 15:32:10 1996
+++ linux/Documentation/smp.tex	Thu Jun  6 14:57:43 1996
@@ -67,7 +67,7 @@
 many operation. Secondly interrupts may pre-empt a kernel running process, 
 but will always return to that process. A process in kernel mode may 
 disable interrupts on the processor and guarantee such an interruption will 
-not occur. The final guarantee is that an interrupt will not bne pre-empted 
+not occur. The final guarantee is that an interrupt will not be pre-empted 
 by a kernel task. That is interrupts will run to completion or be 
 pre-empted by other interrupts only.
 
diff -u --recursive --new-file pre2.0.12/linux/Documentation/watchdog.txt linux/Documentation/watchdog.txt
--- pre2.0.12/linux/Documentation/watchdog.txt	Mon May 13 23:02:46 1996
+++ linux/Documentation/watchdog.txt	Thu Jun  6 14:57:43 1996
@@ -25,8 +25,9 @@
 
 The third interface logs kernel messages on additional alert events.
 
-At the moment only the software watchdog is available in the standard
-kernel.
+Both software and hardware watchdog drivers are available in the standard
+kernel. If you are using the software watchdog, you probably also want
+to use "panic=60" as a boot argument as well.
 
 Features
 --------
diff -u --recursive --new-file pre2.0.12/linux/Makefile linux/Makefile
--- pre2.0.12/linux/Makefile	Wed Jun  5 10:41:27 1996
+++ linux/Makefile	Wed Jun  5 10:41:13 1996
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 99
-SUBLEVEL = 12
+SUBLEVEL = 13
 
 ARCH = i386
 
diff -u --recursive --new-file pre2.0.12/linux/README linux/README
--- pre2.0.12/linux/README	Sat Jun  1 20:11:29 1996
+++ linux/README	Thu Jun  6 14:57:43 1996
@@ -35,8 +35,9 @@
    system: there are much better sources available.
 
  - There are various readme's in the kernel Documentation/ subdirectory:
-   these are mainly used for kernel developers and some very kernel-specific
-   installation notes for some drivers for example.
+   these typically contain kernel-specific installation notes for some 
+   drivers for example. See ./Documentation/00-INDEX for a list of what
+   is contained in each file.
 
 INSTALLING the kernel:
 
@@ -105,7 +106,7 @@
 	- having unnecessary drivers will make the kernel bigger, and can
 	  under some circumstances lead to problems: probing for a
 	  nonexistent controller card may confuse your other controllers
-	- compiling the kernel with "-m486" for a number of 486-specific
+	- compiling the kernel with "Processor type" set higher than 386
 	  will result in a kernel that does NOT work on a 386.  The
 	  kernel will detect this on bootup, and give up.
 	- A kernel with math-emulation compiled in will still use the
@@ -183,7 +184,10 @@
 
 IF SOMETHING GOES WRONG:
 
- - if you have problems that seem to be due to kernel bugs, please mail
+ - if you have problems that seem to be due to kernel bugs, please check
+   the file MAINTAINERS to see if there is a particualr person associated
+   with the part of the kernel that you are having trouble with. If there
+   isn't anyone listed there, then the second best thing is to mail
    them to me (Linus.Torvalds@Helsinki.FI), and possibly to any other
    relevant mailing-list or to the newsgroup.  The mailing-lists are
    useful especially for SCSI and NETworking problems, as I can't test
@@ -210,7 +214,8 @@
    incomprehensible to you, but it does contain information that may
    help debugging the problem.  The text above the dump is also
    important: it tells something about why the kernel dumped code (in
-   the above example it's due to a bad kernel pointer)
+   the above example it's due to a bad kernel pointer). More information
+   on making sense of the dump is in Documentation/oops-tracing.txt
 
  - You can use the "ksymoops" program to make sense of the dump.  Find
    the C++ sources under the scripts/ directory to avoid having to do
diff -u --recursive --new-file pre2.0.12/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- pre2.0.12/linux/arch/i386/defconfig	Mon Jun  3 16:46:53 1996
+++ linux/arch/i386/defconfig	Wed Jun  5 14:51:03 1996
@@ -37,7 +37,7 @@
 CONFIG_BLK_DEV_IDE=y
 
 #
-# Please see drivers/block/README.ide for help/info on IDE drives
+# Please see Documentation/ide.txt for help/info on IDE drives
 #
 # CONFIG_BLK_DEV_HD_IDE is not set
 CONFIG_BLK_DEV_IDECD=y
diff -u --recursive --new-file pre2.0.12/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- pre2.0.12/linux/arch/i386/kernel/irq.c	Tue May  7 16:22:17 1996
+++ linux/arch/i386/kernel/irq.c	Thu Jun  6 13:44:42 1996
@@ -343,6 +343,7 @@
 asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
 {
 	struct irqaction * action = *(irq + irq_action);
+	int do_random = 0;
 
 #ifdef __SMP__
 	if(smp_threads_ready && active_kernel_processor!=smp_processor_id())
@@ -354,11 +355,12 @@
 	int_count[smp_processor_id()][irq]++;
 #endif
 	while (action) {
-		if (action->flags & SA_SAMPLE_RANDOM)
-			add_interrupt_randomness(irq);
+		do_random |= action->flags;
 		action->handler(irq, action->dev_id, regs);
 		action = action->next;
 	}
+	if (do_random & SA_SAMPLE_RANDOM)
+		add_interrupt_randomness(irq);
 }
 
 /*
@@ -369,6 +371,8 @@
 asmlinkage void do_fast_IRQ(int irq)
 {
 	struct irqaction * action = *(irq + irq_action);
+	int do_random = 0;
+	
 #ifdef __SMP__
 	/* IRQ 13 is allowed - that's a flush tlb */
 	if(smp_threads_ready && active_kernel_processor!=smp_processor_id() && irq!=13)
@@ -380,9 +384,12 @@
 	int_count[smp_processor_id()][irq]++;
 #endif
 	while (action) {
+		do_random |= action->flags;
 		action->handler(irq, action->dev_id, NULL);
 		action = action->next;
 	}
+	if (do_random & SA_SAMPLE_RANDOM)
+		add_interrupt_randomness(irq);
 }
 
 int setup_x86_irq(int irq, struct irqaction * new)
diff -u --recursive --new-file pre2.0.12/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- pre2.0.12/linux/drivers/block/Config.in	Fri May 17 15:32:12 1996
+++ linux/drivers/block/Config.in	Wed Jun  5 12:52:58 1996
@@ -6,7 +6,7 @@
 
 tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD
 bool 'Enhanced IDE/MFM/RLL disk/cdrom/tape support' CONFIG_BLK_DEV_IDE
-comment 'Please see drivers/block/README.ide for help/info on IDE drives'
+comment 'Please see Documentation/ide.txt for help/info on IDE drives'
 if [ "$CONFIG_BLK_DEV_IDE" = "n" ]; then
   bool 'Old harddisk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY
 else
@@ -25,7 +25,9 @@
     bool '      ALI M1439/M1445 support' CONFIG_BLK_DEV_ALI14XX
     bool '      DTC-2278        support' CONFIG_BLK_DEV_DTC2278
     bool '      Holtek HT6560B  support' CONFIG_BLK_DEV_HT6560B
-    bool '      PROMISE DC4030  support (ALPHA)' CONFIG_BLK_DEV_PROMISE
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      bool '      PROMISE DC4030  support (EXPERIMENTAL)' CONFIG_BLK_DEV_PROMISE
+    fi
     bool '      QDI QD6580      support' CONFIG_BLK_DEV_QD6580
     bool '      UMC 8672        support' CONFIG_BLK_DEV_UMC8672
   fi
diff -u --recursive --new-file pre2.0.12/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- pre2.0.12/linux/drivers/block/ide.c	Tue May 28 08:09:56 1996
+++ linux/drivers/block/ide.c	Thu Jun  6 13:42:15 1996
@@ -3114,7 +3114,7 @@
 	 * Allocate the irq, if not already obtained for another hwif
 	 */
 	if (!match || match->irq != hwif->irq) {
-		if (request_irq(hwif->irq, ide_intr, SA_INTERRUPT|SA_SAMPLE_RANDOM, hwif->name, hwgroup)) {
+		if (request_irq(hwif->irq, ide_intr, SA_INTERRUPT, hwif->name, hwgroup)) {
 			if (!match)
 				kfree(hwgroup);
 			restore_flags(flags);
diff -u --recursive --new-file pre2.0.12/linux/drivers/block/xd.c linux/drivers/block/xd.c
--- pre2.0.12/linux/drivers/block/xd.c	Tue May  7 16:22:22 1996
+++ linux/drivers/block/xd.c	Wed Jun  5 10:25:29 1996
@@ -22,7 +22,7 @@
  *
  */
 
-
+#include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
diff -u --recursive --new-file pre2.0.12/linux/drivers/cdrom/mcdx.c linux/drivers/cdrom/mcdx.c
--- pre2.0.12/linux/drivers/cdrom/mcdx.c	Tue May 28 08:09:56 1996
+++ linux/drivers/cdrom/mcdx.c	Wed Jun  5 10:49:23 1996
@@ -1,13 +1,20 @@
 /*
  * The Mitsumi CDROM interface
+ * Copyright (C) 1995 1996 Heiko Schlittermann <heiko@lotte.sax.de>
+ * VERSION: 2.14(hs)
  *
- * (H) Hackright 1996 by Marcin Dalecki <dalecki@namu03.gwdg.de>
- * 
- * Based on previous work (as of version 1.9) done by:
- * Copyright (C) 1995 Heiko Schlittermann <heiko@lotte.sax.de>
+ * ... anyway, I'm back again, thanks to Marcin, he adopted 
+ * large portions of my code (at least the parts containing
+ * my main thoughts ...)
+ *
+ ****************** H E L P *********************************
+ * If you ever plan to update your CD ROM drive and perhaps
+ * want to sell or simply give away your Mitsumi FX-001[DS] 
+ * -- Please --
+ * mail me (heiko@lotte.sax.de).  When my last drive goes 
+ * ballistic no more driver support will be available from me!
+ *************************************************************
  *
- * VERSION: 2.5
- * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
@@ -31,28 +38,19 @@
  *  Jon Tombs, Bjorn Ekwall (module support)
  *  Daniel v. Mosnenck (he sent me the Technical and Programming Reference)
  *  Gerd Knorr (he lent me his PhotoCD)
- *  Nils Faerber and Roger E. Wolff (extensively tested the LU portion)
- *  Andreas Kies (testing the mysterious hangups)
+ *  Nils Faerber and Roger E. Wolff (extensivly tested the LU portion)
+ *  Andreas Kies (testing the mysterious hang up's)
+ *  Heiko Eissfeld (VERIFY_READ/WRITE)
+ *  Marcin Dalecki (improved performance, shortened code)
  *  ... somebody forgotten?
- * 
- * 2.1  1996/04/29 Marcin Dalecki <dalecki@namu03.gwdg.de>
- *      Far too many bugfixes/changes to mention them all separately.
- * 2.2  1996/05/06 Marcin Dalecki <dalecki@namu03.gwdg.de>
- *      Mostly fixes to some silly bugs in the previous release :-).
- *      (Hi Michael Thimm! Thank's for lending me Your's double speed drive.)
- * 2.3  1996/05/15 Marcin Dalecki <dalecki@namu03.gwdg.de>
- *      Fixed stereo support. 
- * 2.5  1996/05/19 Marcin Dalecki <dalecki@namu03.gwdg.de>
- *      Overall performance increased by a factor of 1.25 :-).
- *      I hope Heiko doesn't mind the Hackright change, but there isn't much of
- *      code left from his version 1.9 anymore. 
- *      Start speedup for Work(Man|Bone).
- *
- * NOTE:
- *      There will be probably a 3.0 adhering to the new generic non ATAPI
- *      CDROM interface in the unforeseen future.
+ *  
  */
-#define VERSION "2.5"
+
+
+#if RCS
+static const char *mcdx_c_version
+		= "$Id: mcdx.c,v 1.12 1996/06/05 01:38:38 heiko Exp $";
+#endif
 
 #include <linux/version.h>
 #include <linux/module.h>
@@ -71,151 +69,214 @@
 #define MAJOR_NR MITSUMI_X_CDROM_MAJOR
 #include <linux/blk.h>
 
-/* 
- * for compatible parameter passing with "insmod" 
- */
-#define	mcdx_drive_map mcdx
+/* for compatible parameter passing with "insmod" */ 
+#define	mcdx_drive_map mcdx    
 #include <linux/mcdx.h>
 
-#define REQUEST_SIZE	400
+#ifndef HZ 
+#error HZ not defined
+#endif
 
-enum drivemodes {
-	TOC, DATA, RAW, COOKED
-};
+#define xwarn(fmt, args...) printk(KERN_WARNING MCDX " " fmt, ## args)
 
-#define	MODE0 		0x00
-#define MODE1 		0x01
-#define MODE2 		0x02
+#if !MCDX_QUIET
+#define xinfo(fmt, args...) printk(KERN_INFO MCDX " " fmt, ## args)
+#else
+#define xinfo(fmt, args...) { ; }
+#endif
 
-#define DOOR_LOCK 	0x01
-#define DOOR_UNLOCK 	0x00
+#if MCDX_DEBUG
+#define xtrace(lvl, fmt, args...) \
+		{ if (lvl > 0) \
+			{ printk(KERN_DEBUG MCDX ":: " fmt, ## args); } }
+#define xdebug(fmt, args...) printk(KERN_DEBUG MCDX ":: " fmg, ## args)
+#else
+#define xtrace(lvl, fmt, args...) { ; }
+#define xdebug(fmt, args...) { ; }
+#endif
 
-/*
- * Structs used to gather info reported by the drive.
- */
+/* CONSTANTS *******************************************************/
 
+/* Following are the number of sectors we _request_ from the drive
+   every time an access outside the already requested range is done.
+   The _direct_ size is the number of sectors we're allowed to skip
+   directly (perfoming a read instead of requesting the new sector
+   needed */
+const int REQUEST_SIZE = 800;	/* should be less then 255 * 4 */
+const int DIRECT_SIZE = 400;	/* should be less then REQUEST_SIZE */
+
+const unsigned long ACLOSE_INHIBIT = 800;  /* 1/100 s of autoclose inhibit */
+
+enum drivemodes { TOC, DATA, RAW, COOKED };
+enum datamodes { MODE0, MODE1, MODE2 };
+enum resetmodes { SOFT, HARD };
+
+const int SINGLE = 0x01;		/* single speed drive (FX001S, LU) */
+const int DOUBLE = 0x02;		/* double speed drive (FX001D, ..? */
+const int DOOR   = 0x04;		/* door locking capability */
+const int MULTI  = 0x08;		/* multi session capability */
+
+const unsigned char READ1X = 0xc0;
+const unsigned char READ2X = 0xc1;
+
+
+/* DECLARATIONS ****************************************************/ 
 struct s_subqcode {
-	u_char adr:4;
-	u_char ctrl:4;
-	u_char tno;
-	u_char index;
+	unsigned char control;
+	unsigned char tno;
+	unsigned char index;
 	struct cdrom_msf0 tt;
-	u_char dummy;		/* padding for matching the returned struct */
 	struct cdrom_msf0 dt;
 };
 
+struct s_diskinfo {
+	unsigned int n_first;
+	unsigned int n_last;
+	struct cdrom_msf0 msf_leadout;
+	struct cdrom_msf0 msf_first;
+};
+	
 struct s_multi {
 	unsigned char multi;
 	struct cdrom_msf0 msf_last;
 };
 
-struct s_play {
-	struct cdrom_msf0 start;
-	struct cdrom_msf0 stop;
+struct s_version {
+	unsigned char code;
+	unsigned char ver;
 };
 
-/* 
- * Per drive/controller stuff.
- */
-struct s_drive_stuff {
-	struct wait_queue *busyq;
-
-	/* flags */
-	u_char used:1;		/* locks on open, we allow only
-				   exclusive usage of the drive */
-	u_char introk:1;	/* status of last irq operation */
-	u_char busy:1;		/* drive performs an operation */
-	u_char eject_sw:1;	/* 1 - eject on last close (default 0) */
-	u_char autoclose:1;	/* 1 - close the door on open (default 1) */
-	u_char xxx:1;		/* set if changed, reset while open */
-	u_char xa:1;		/* 1 if xa disk */
-	u_char audio:1;		/* 1 if audio disk */
-	u_char eom:1;		/* end of media reached during read request */
-
-	/* drives capabilities */
-	u_char door:1;		/* can close/lock tray */
-	u_char multi_cap:1;	/* multi-session capable */
-	u_char double_speed:1;	/* double speed drive */
+/* Per drive/controller stuff **************************************/
 
+struct s_drive_stuff {
+	/* waitquenes */
+    struct wait_queue *busyq;
+    struct wait_queue *lockq;
+    struct wait_queue *sleepq;
+
+ 	/* flags */
+    volatile int introk;	/* status of last irq operation */
+    volatile int busy;		/* drive performs an operation */
+    volatile int lock;		/* exclusive usage */
+    int eject_sw;           /* 1 - eject on last close (default 0) */
+    int autoclose;          /* 1 - close the door on open (default 1) */
+    
 	/* cd infos */
-	u_int first;
-	u_int last;
-	struct cdrom_msf0 msf_leadout;
+	struct s_diskinfo di;
 	struct s_multi multi;
-
-	struct s_subqcode *toc;	/* first entry of the toc array */
-	struct s_play resume;	/* where to resume after pause */
-
-	int audiostatus;
+	struct s_subqcode* toc;	/* first enty of the toc array */
+	struct s_subqcode start;
+    struct s_subqcode stop;
+	int xa;					/* 1 if xa disk */
+	int audio;				/* 1 if audio disk */
+	int audiostatus;			
 
 	/* `buffer' control */
-	u_char valid:1;
-	int pending;
-	int border;		/* the last sector in sequence we will read,
-				   without reissuing a read command */
-
-	u_int base;		/* base for all registers of the drive */
-	int irq;		/* irq used by this drive */
-	int lastsector;		/* last accessible block */
+    volatile int valid;			/* pending, ..., values are valid */
+    volatile int pending;		/* next sector to be read */
+    volatile int low_border;	/* first sector not to be skipped direct */
+    volatile int high_border; /* first sector `out of area' */
+#ifdef AK2
+    volatile int int_err;
+#endif /* AK2 */
+
+	/* adds and odds */
+	void* wreg_data;	/* w data */
+	void* wreg_reset;	/* w hardware reset */
+	void* wreg_hcon;	/* w hardware conf */
+	void* wreg_chn;		/* w channel */
+	void* rreg_data;	/* r data */
+	void* rreg_status;	/* r status */
+
+    int irq;			/* irq used by this drive */
+    int minor;			/* minor number of this drive */
+    int present;	    /* drive present and its capabilities */
+    unsigned char readcmd;	/* read cmd depends on single/double speed */
+    unsigned char playcmd;  /* play should always be single speed */
+    unsigned int xxx;      /* set if changed, reset while open */
+    unsigned int yyy;      /* set if changed, reset by media_changed */
+    unsigned long ejected;  /* time we called the eject function */
+    int users;				/* keeps track of open/close */
+    int lastsector;			/* last block accessible */
+    int status;				/* last operation's error / status */
+	int readerrs;			/* # of blocks read w/o error */
 };
 
-/*
- * Macros for accessing interface registers
- */
-#define DATA_REG	(stuffp->base)
-#define RESET_REG	(stuffp->base+1)
-#define STAT_REG	(stuffp->base+1)
-#define CHAN_REG	(stuffp->base+3)
 
-/*      
- * Access to elements of the mcdx_drive_map members 
- */
-#define PORT 	0
-#define IRQ 	1
+/* Prototypes ******************************************************/ 
 
-/* 
- * declared in blk.h 
- */
+/*	The following prototypes are already declared elsewhere.  They are
+ 	repeated here to show what's going on.  And to sense, if they're
+	changed elsewhere. */
+
+/* declared in blk.h */
 int mcdx_init(void);
 void do_mcdx_request(void);
+int check_mcdx_media_change(kdev_t);
 
-/* 
- * already declared in init/main 
- */
+/* already declared in init/main */
 void mcdx_setup(char *, int *);
 
-/*      
- * Indirect exported functions. These functions are exported by their
- * addresses, such as mcdx_open and mcdx_close in the 
- * structure fops. 
- */
-
-/* 
- * ???  exported by the mcdx_sigaction struct 
- */
-static void mcdx_intr(int, void *, struct pt_regs *);
+/*	Indirect exported functions. These functions are exported by their
+	addresses, such as mcdx_open and mcdx_close in the 
+	structure fops. */
+
+/* ???  exported by the mcdx_sigaction struct */
+static void mcdx_intr(int, void *, struct pt_regs*);
+
+/* exported by file_ops */
+static int mcdx_open(struct inode*, struct file*);
+static void mcdx_close(struct inode*, struct file*);
+static int mcdx_ioctl(struct inode*, struct file*, unsigned int, unsigned long);
+
+/* misc internal support functions */
+static void log2msf(unsigned int, struct cdrom_msf0*);
+static unsigned int msf2log(const struct cdrom_msf0*);
+static unsigned int uint2bcd(unsigned int);
+static unsigned int bcd2uint(unsigned char);
+static char *port(int*);
+static int irq(int*);
+static void mcdx_delay(struct s_drive_stuff*, long jifs);
+static int mcdx_transfer(struct s_drive_stuff*, char* buf, int sector, int nr_sectors);
+static int mcdx_xfer(struct s_drive_stuff*, char* buf, int sector, int nr_sectors);
+
+static int mcdx_config(struct s_drive_stuff*, int);
+static int mcdx_closedoor(struct s_drive_stuff*, int);
+static int mcdx_requestversion(struct s_drive_stuff*, struct s_version*, int);
+static int mcdx_lockdoor(struct s_drive_stuff*, int, int);
+static int mcdx_stop(struct s_drive_stuff*, int);
+static int mcdx_hold(struct s_drive_stuff*, int);
+static int mcdx_reset(struct s_drive_stuff*, enum resetmodes, int);
+static int mcdx_eject(struct s_drive_stuff*, int);
+static int mcdx_setdrivemode(struct s_drive_stuff*, enum drivemodes, int);
+static int mcdx_setdatamode(struct s_drive_stuff*, enum datamodes, int);
+static int mcdx_requestsubqcode(struct s_drive_stuff*, struct s_subqcode*, int);
+static int mcdx_requestmultidiskinfo(struct s_drive_stuff*, struct s_multi*, int);
+static int mcdx_requesttocdata(struct s_drive_stuff*, struct s_diskinfo*, int);
+static int mcdx_getstatus(struct s_drive_stuff*, int);
+static int mcdx_getval(struct s_drive_stuff*, int to, int delay, char*);
+static int mcdx_talk(struct s_drive_stuff*, 
+		const unsigned char* cmd, size_t, 
+        void *buffer, size_t size, 
+        unsigned int timeout, int);
+static int mcdx_readtoc(struct s_drive_stuff*);
+static int mcdx_playtrk(struct s_drive_stuff*, const struct cdrom_ti*);
+static int mcdx_playmsf(struct s_drive_stuff*, const struct cdrom_msf*);
+static int mcdx_setattentuator(struct s_drive_stuff*, struct cdrom_volctrl*, int);
 
-/* 
- * exported by file_ops 
- */
-static int mcdx_open(struct inode *, struct file *);
-static void mcdx_close(struct inode *, struct file *);
-static int mcdx_ioctl(struct inode *, struct file *,
-		      unsigned int, unsigned long);
-static int mcdx_media_change(kdev_t);
+/* static variables ************************************************/
 
-static int mcdx_blksize_size[MCDX_NDRIVES];
+static int mcdx_blocksizes[MCDX_NDRIVES];
 static int mcdx_drive_map[][2] = MCDX_DRIVEMAP;
-static struct s_drive_stuff *mcdx_stuffp[MCDX_NDRIVES];
-static struct s_drive_stuff *mcdx_irq_map[16] =
-{0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0};
+static struct s_drive_stuff* mcdx_stuffp[MCDX_NDRIVES];
+static struct s_drive_stuff* mcdx_irq_map[16] =
+		{0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0};
 
-static struct file_operations mcdx_fops =
-{
+static struct file_operations mcdx_fops = {
 	NULL,			/* lseek - use kernel default */
 	block_read,		/* read - general block-dev read */
-	block_write,		/* write - general block-dev write */
+	block_write,	/* write - general block-dev write */
 	NULL,			/* no readdir */
 	NULL,			/* no select */
 	mcdx_ioctl,		/* ioctl() */
@@ -223,1118 +284,1647 @@
 	mcdx_open,		/* open() */
 	mcdx_close,		/* close() */
 	NULL,			/* fsync */
-	NULL,			/* fasync */
-	mcdx_media_change,	/* media_change */
-	NULL			/* revalidate */
+	NULL,                     /* fasync */
+	check_mcdx_media_change,  /* media_change */
+	NULL                      /* revalidate */
 };
 
-/*     
- * Misc number converters 
- */
+/* KERNEL INTERFACE FUNCTIONS **************************************/ 
 
-static unsigned int bcd2uint(unsigned int c)
-{
-	return (c >> 4) * 10 + (c & 0x0f);
-}
+static int 
+mcdx_ioctl(
+	struct inode* ip, struct file* fp, 
+	unsigned int cmd, unsigned long arg)
+{ 
+	struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
 
-static unsigned int uint2bcd(unsigned int ival)
-{
-	return ((ival / 10) << 4) | (ival % 10);
-}
+	if (!stuffp->present) return -ENXIO;
+	if (!ip) return -EINVAL;
 
-static unsigned int msf2log(const struct cdrom_msf0 *pmsf)
-{
-	return bcd2uint(pmsf->frame)
-	    + bcd2uint(pmsf->second) * 75
-	    + bcd2uint(pmsf->minute) * 4500
-	    - CD_BLOCK_OFFSET;
-}
+	switch (cmd) {
+		case CDROMSTART: {
+			xtrace(IOCTL, "ioctl() START\n");
+			return 0;
+		}
 
-/*
- * Low level hardware related functions.
- */
+		case CDROMSTOP: {
+			xtrace(IOCTL, "ioctl() STOP\n");
+            stuffp->audiostatus = CDROM_AUDIO_INVALID;
+			if (-1 == mcdx_stop(stuffp, 1))
+				return -EIO;
+			return 0;
+		}
 
-/*
- * Return drives status in case of success, -1 otherwise.
- *
- * First we try to get the status information quickly.
- * Then we sleep repeatedly for about 10 usecs, before we finally reach the 
- * timeout. For this reason this command must be called with the drive being 
- * locked!
- */
-static int get_status(struct s_drive_stuff *stuffp,
-		      unsigned long timeout)
-{
-	unsigned long bang = jiffies + 2;
-	timeout += jiffies;
+		case CDROMPLAYTRKIND: {
+			int ans;
+			struct cdrom_ti ti;
+
+			xtrace(IOCTL, "ioctl() PLAYTRKIND\n");
+			if ((ans = verify_area(VERIFY_READ, (void*) arg, sizeof(ti))))
+				return ans;
+			memcpy_fromfs(&ti, (void*) arg, sizeof(ti));
+			if ((ti.cdti_trk0 < stuffp->di.n_first)
+					|| (ti.cdti_trk0 > stuffp->di.n_last)
+					|| (ti.cdti_trk1 < stuffp->di.n_first))
+				return -EINVAL;
+			if (ti.cdti_trk1 > stuffp->di.n_last) ti.cdti_trk1 = stuffp->di.n_last;
+            xtrace(PLAYTRK, "ioctl() track %d to %d\n", ti.cdti_trk0, ti.cdti_trk1);
+
+            return mcdx_playtrk(stuffp, &ti);
+        }
+
+        case CDROMPLAYMSF: {
+            int ans;
+            struct cdrom_msf msf;
+
+            xtrace(IOCTL, "ioctl() PLAYMSF\n");
+
+            if ((stuffp->audiostatus == CDROM_AUDIO_PLAY)
+                && (-1 == mcdx_hold(stuffp, 1))) return -EIO;
+
+            if ((ans = verify_area(
+                    VERIFY_READ, (void*) arg, sizeof(struct cdrom_msf)))) 
+                return ans;
+
+            memcpy_fromfs(&msf, (void*) arg, sizeof msf);
+
+            msf.cdmsf_min0 = uint2bcd(msf.cdmsf_min0);
+            msf.cdmsf_sec0 = uint2bcd(msf.cdmsf_sec0);
+            msf.cdmsf_frame0 = uint2bcd(msf.cdmsf_frame0);
+
+            msf.cdmsf_min1 = uint2bcd(msf.cdmsf_min1);
+            msf.cdmsf_sec1 = uint2bcd(msf.cdmsf_sec1);
+            msf.cdmsf_frame1 = uint2bcd(msf.cdmsf_frame1);
+
+            return mcdx_playmsf(stuffp, &msf);
+        }
+
+        case CDROMRESUME: {
+            xtrace(IOCTL, "ioctl() RESUME\n");
+            return mcdx_playtrk(stuffp, NULL);
+        }
+
+		case CDROMREADTOCENTRY: {
+			struct cdrom_tocentry entry;
+			struct s_subqcode *tp = NULL;
+			int ans;
+
+			xtrace(IOCTL, "ioctl() READTOCENTRY\n");
+
+            if (-1 == mcdx_readtoc(stuffp)) return -1;
+
+			if ((ans = verify_area(VERIFY_WRITE, (void *) arg, sizeof(entry)))) return ans;
+			memcpy_fromfs(&entry, (void *) arg, sizeof(entry));
+
+			if (entry.cdte_track == CDROM_LEADOUT) 
+				tp = &stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1];
+			else if (entry.cdte_track > stuffp->di.n_last 
+					|| entry.cdte_track < stuffp->di.n_first) return -EINVAL;
+			else tp = &stuffp->toc[entry.cdte_track - stuffp->di.n_first];
+
+			if (NULL == tp) xwarn("FATAL.\n"); 
+
+			entry.cdte_adr = tp->control;
+			entry.cdte_ctrl = tp->control >> 4;
+
+			if (entry.cdte_format == CDROM_MSF) {
+				entry.cdte_addr.msf.minute = bcd2uint(tp->dt.minute);
+				entry.cdte_addr.msf.second = bcd2uint(tp->dt.second);
+				entry.cdte_addr.msf.frame = bcd2uint(tp->dt.frame);
+			} else if (entry.cdte_format == CDROM_LBA)
+				entry.cdte_addr.lba = msf2log(&tp->dt);
+			else return -EINVAL;
 
-	do {
-		if (!(inb(STAT_REG) & MCDX_RBIT_STEN)) {
-			return (inb(DATA_REG) & 0xff);
+			memcpy_tofs((void*) arg, &entry, sizeof(entry));
+
+			return 0;
 		}
-	} while (jiffies < bang);
 
-	while (inb(STAT_REG) & MCDX_RBIT_STEN) {
-		if (jiffies > timeout)
-			return -1;
-		current->state = TASK_INTERRUPTIBLE;
-		current->timeout = jiffies + HZ / 2;
-		schedule();
-	}
-	return (inb(DATA_REG) & 0xff);
-}
+		case CDROMSUBCHNL: {
+			int ans;
+			struct cdrom_subchnl sub;
+			struct s_subqcode q;
+
+			xtrace(IOCTL, "ioctl() SUBCHNL\n");
+
+			if ((ans = verify_area(VERIFY_WRITE,
+                    (void*) arg, sizeof(sub)))) return ans;
+
+			memcpy_fromfs(&sub, (void*) arg, sizeof(sub));
+
+			if (-1 == mcdx_requestsubqcode(stuffp, &q, 2)) return -EIO;
+
+            xtrace(SUBCHNL, "audiostatus: %x\n", stuffp->audiostatus);
+			sub.cdsc_audiostatus = stuffp->audiostatus;
+			sub.cdsc_adr = q.control;
+			sub.cdsc_ctrl = q.control >> 4;
+			sub.cdsc_trk = bcd2uint(q.tno);
+			sub.cdsc_ind = bcd2uint(q.index);
+
+            xtrace(SUBCHNL, "trk %d, ind %d\n", 
+                    sub.cdsc_trk, sub.cdsc_ind);
+
+			if (sub.cdsc_format == CDROM_LBA) {
+				sub.cdsc_absaddr.lba = msf2log(&q.dt);
+				sub.cdsc_reladdr.lba = msf2log(&q.tt);
+                xtrace(SUBCHNL, "lba: abs %d, rel %d\n",
+                    sub.cdsc_absaddr.lba,
+                    sub.cdsc_reladdr.lba);
+			} else if (sub.cdsc_format == CDROM_MSF) {
+				sub.cdsc_absaddr.msf.minute = bcd2uint(q.dt.minute);
+				sub.cdsc_absaddr.msf.second = bcd2uint(q.dt.second);
+				sub.cdsc_absaddr.msf.frame = bcd2uint(q.dt.frame);
+				sub.cdsc_reladdr.msf.minute = bcd2uint(q.tt.minute);
+				sub.cdsc_reladdr.msf.second = bcd2uint(q.tt.second);
+				sub.cdsc_reladdr.msf.frame = bcd2uint(q.tt.frame);
+                xtrace(SUBCHNL,
+                        "msf: abs %02d:%02d:%02d, rel %02d:%02d:%02d\n",
+                        sub.cdsc_absaddr.msf.minute,
+                        sub.cdsc_absaddr.msf.second,
+                        sub.cdsc_absaddr.msf.frame,
+                        sub.cdsc_reladdr.msf.minute,
+                        sub.cdsc_reladdr.msf.second,
+                        sub.cdsc_reladdr.msf.frame);
+			} else return -EINVAL;
 
-static void release_toc(struct s_drive_stuff *stuffp)
-{
-	if (stuffp->toc) {
-		kfree(stuffp->toc);
-		stuffp->toc = 0;
-	}
-}
+			memcpy_tofs((void*) arg, &sub, sizeof(sub));
 
-/* Send a command to the drive, wait for the result.
- * returns -1 on timeout, drive status otherwise.
- * If buffer is not zero, the result (length size) is stored there.
- * If buffer is zero the size should be the number of bytes to read
- * from the drive.  These bytes are discarded.
- */
-static int talk(struct s_drive_stuff *stuffp,
-		const unsigned char command,
-		void *pars, size_t parslen,
-		void *buffer, size_t size,
-		unsigned int timeout)
-{
-	int st;
+			return 0;
+		}
 
-	stuffp->valid = 0;
-	outb(command, DATA_REG);
-	if (parslen)
-		outsb(DATA_REG, pars, parslen);
+		case CDROMREADTOCHDR: {
+			struct cdrom_tochdr toc;
+			int ans;
+
+			xtrace(IOCTL, "ioctl() READTOCHDR\n");
+			if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof toc)))
+				return ans;
+			toc.cdth_trk0 = stuffp->di.n_first;
+			toc.cdth_trk1 = stuffp->di.n_last;
+			memcpy_tofs((void*) arg, &toc, sizeof toc);
+			xtrace(TOCHDR, "ioctl() track0 = %d, track1 = %d\n",
+					stuffp->di.n_first, stuffp->di.n_last);
+			return 0;
+		}
 
-	if (-1 == (st = get_status(stuffp, timeout))) {
-		goto end_talk;
-	}
-	if (st & MCDX_RBIT_CMDERR) {
-		printk(KERN_ERR MCDX ": error in command 0x%2x\n", command);
-		st = -1;
-		goto end_talk;
-	}
-	/* audio status? */
-	if (stuffp->audiostatus == CDROM_AUDIO_INVALID) {
-		stuffp->audiostatus =
-		    (st & MCDX_RBIT_AUDIOBS) ?
-		    CDROM_AUDIO_PLAY : CDROM_AUDIO_NO_STATUS;
-	} else if (stuffp->audiostatus == CDROM_AUDIO_PLAY
-		   && !(st & MCDX_RBIT_AUDIOBS)) {
-		stuffp->audiostatus = CDROM_AUDIO_COMPLETED;
-	}
-	/* media change? */
-	if (st & MCDX_RBIT_CHANGED)
-		stuffp->xxx = 1;
-	/* now actually get the data */
-	while (size--) {
-		if (-1 == (st = get_status(stuffp, timeout))) {
-			break;
+		case CDROMPAUSE: {
+			xtrace(IOCTL, "ioctl() PAUSE\n");
+			if (stuffp->audiostatus != CDROM_AUDIO_PLAY) return -EINVAL;
+			if (-1 == mcdx_stop(stuffp, 1)) return -EIO;
+            stuffp->audiostatus = CDROM_AUDIO_PAUSED;
+			if (-1 == mcdx_requestsubqcode(stuffp, &stuffp->start, 1))
+				return -EIO;
+			return 0;
 		}
-		*((char *) buffer) = st;
-		buffer++;
-	}
-	/* The goto's make GCC generate better code.
-	 */
-      end_talk:
-	return st;
-}
 
-static int issue_command(struct s_drive_stuff *stuffp,
-			 unsigned char command,
-			 unsigned int timeout)
-{
-	return talk(stuffp, command, 0, 0, NULL, 0, timeout);
-}
+		case CDROMMULTISESSION: {
+			int ans;
+			struct cdrom_multisession ms;
+			xtrace(IOCTL, "ioctl() MULTISESSION\n");
+			if (0 != (ans = verify_area(VERIFY_WRITE, (void*) arg, 
+					sizeof(struct cdrom_multisession))))
+				return ans;
+				
+			memcpy_fromfs(&ms, (void*) arg, sizeof(struct cdrom_multisession));
+			if (ms.addr_format == CDROM_MSF) {
+				ms.addr.msf.minute = bcd2uint(stuffp->multi.msf_last.minute);
+				ms.addr.msf.second = bcd2uint(stuffp->multi.msf_last.second);
+				ms.addr.msf.frame = bcd2uint(stuffp->multi.msf_last.frame);
+			} else if (ms.addr_format == CDROM_LBA)
+				ms.addr.lba = msf2log(&stuffp->multi.msf_last);
+			else
+				return -EINVAL;
+			ms.xa_flag = !!stuffp->multi.multi;
+
+			memcpy_tofs((void*) arg, &ms, sizeof(struct cdrom_multisession));
+			if (ms.addr_format == CDROM_MSF) {
+				xtrace(MS, 
+						"ioctl() (%d, %02x:%02x.%02x [%02x:%02x.%02x])\n",
+						ms.xa_flag, 
+						ms.addr.msf.minute,
+						ms.addr.msf.second,
+						ms.addr.msf.frame,
+						stuffp->multi.msf_last.minute,
+						stuffp->multi.msf_last.second,
+						stuffp->multi.msf_last.frame);
+			} else {
+			    xtrace(MS, 
+					"ioctl() (%d, 0x%08x [%02x:%02x.%02x])\n",
+					ms.xa_flag,
+					ms.addr.lba,
+					stuffp->multi.msf_last.minute,
+					stuffp->multi.msf_last.second,
+					stuffp->multi.msf_last.frame);
+			  }
+			return 0;
+		}
 
-static inline int set_command(struct s_drive_stuff *stuffp,
-			      const unsigned char command,
-			      void *pars, size_t parlen,
-			      unsigned int timeout)
-{
-	return talk(stuffp, command, pars, parlen, NULL, 0, timeout);
-}
+		case CDROMEJECT: {
+			xtrace(IOCTL, "ioctl() EJECT\n");
+			if (stuffp->users > 1) return -EBUSY;
+			if (-1 == mcdx_eject(stuffp, 1)) return -EIO;
+			return 0;
+		}
 
-static inline int get_command(struct s_drive_stuff *stuffp,
-			      const unsigned char command,
-			      void *buffer, size_t size,
-			      unsigned int timeout)
-{
-	return talk(stuffp, command, NULL, 0, buffer, size, timeout);
-}
+        case CDROMEJECT_SW: {
+            stuffp->eject_sw = arg;
+            return 0;
+        }
+
+        case CDROMVOLCTRL: {
+            int ans;
+            struct cdrom_volctrl volctrl;
+
+            xtrace(IOCTL, "ioctl() VOLCTRL\n");
+            if ((ans = verify_area(
+                    VERIFY_READ, 
+                    (void*) arg,
+                    sizeof(volctrl))))
+                return ans;
+
+            memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
+#if 0		/* not tested! */
+			/* adjust for the weirdness of workman (md) */
+			/* can't test it (hs) */
+			volctrl.channel2 = volctrl.channel1;
+			volctrl.channel1 = volctrl.channel3 = 0x00;
+#endif
+            return mcdx_setattentuator(stuffp, &volctrl, 2);
+        }
 
-static int request_subq_code(struct s_drive_stuff *stuffp,
-			     struct s_subqcode *sub)
-{
-	return get_command(stuffp, MCDX_CMD_GET_SUBQ_CODE,
-			   sub, sizeof(struct s_subqcode), 2 * HZ);
+		default:
+			xwarn("ioctl(): unknown request 0x%04x\n", cmd);
+	    	return -EINVAL;
+	}
 }
 
-static int request_toc_data(struct s_drive_stuff *stuffp)
+void do_mcdx_request()
 {
-	char buf[8];
-	int ans;
+    int dev;
+    struct s_drive_stuff *stuffp;
 
-	ans = get_command(stuffp, MCDX_CMD_GET_TOC, buf, sizeof(buf), 2 * HZ);
-	if (ans == -1) {
-		stuffp->first = 0;
-		stuffp->last = 0;
-	} else {
-		stuffp->first = bcd2uint(buf[0]);
-		stuffp->last = bcd2uint(buf[1]);
-		memcpy(&(stuffp->msf_leadout), buf + 2, 3);
+  again:
+
+	if (CURRENT == NULL) {
+		xtrace(REQUEST, "end_request(0): CURRENT == NULL\n");
+		return;
 	}
-	return ans;
-}
 
-static int set_drive_mode(struct s_drive_stuff *stuffp, enum drivemodes mode)
-{
-	char value;
-	if (-1 == get_command(stuffp, MCDX_CMD_GET_DRIVE_MODE,
-			      &value, 1, 5 * HZ))
-		return -1;
-	switch (mode) {
-	case TOC:
-		value |= 0x04;
-		break;
-	case DATA:
-		value &= ~0x04;
-		break;
-	case RAW:
-		value |= 0x40;
-		break;
-	case COOKED:
-		value &= ~0x40;
-		break;
-	default:
-		break;
-	}
-	return set_command(stuffp, MCDX_CMD_SET_DRIVE_MODE, &value, 1, 5 * HZ);
-}
-
-static int config_drive(struct s_drive_stuff *stuffp)
-{
-	unsigned char buf[2];
-	buf[0] = 0x10;		/* irq enable */
-	buf[1] = 0x05;		/* pre, err irq enable */
+	if (CURRENT->rq_status == RQ_INACTIVE) {
+		xtrace(REQUEST, "end_request(0): rq_status == RQ_INACTIVE\n");
+		return;
+	}
 
-	if (-1 == set_command(stuffp, MCDX_CMD_CONFIG, buf,
-			      sizeof(buf), 1 * HZ))
-		return -1;
+    INIT_REQUEST;
 
-	buf[0] = 0x02;		/* dma select */
-	buf[1] = 0x00;		/* no dma */
+    dev = MINOR(CURRENT->rq_dev);
+    stuffp = mcdx_stuffp[dev];
 
-	return set_command(stuffp, MCDX_CMD_CONFIG, buf, sizeof(buf), 1 * HZ);
-}
+	if ((dev < 0) 
+		|| (dev >= MCDX_NDRIVES) 
+		|| !stuffp 
+		|| (!stuffp->present)) {
+		xwarn("do_request(): bad device: %s\n", 
+				kdevname(CURRENT->rq_dev));
+		xtrace(REQUEST, "end_request(0): bad device\n");
+		end_request(0); return;
+    }
+
+	if (stuffp->audio) {
+		xwarn("do_request() attempt to read from audio cd\n");
+		xtrace(REQUEST, "end_request(0): read from audio\n");
+		end_request(0); return;
+	}
+
+	xtrace(REQUEST, "do_request() (%lu + %lu)\n",
+			CURRENT->sector, CURRENT->nr_sectors);
+
+    switch (CURRENT->cmd) {
+      case WRITE:
+	  xwarn("do_request(): attempt to write to cd!!\n");
+	  xtrace(REQUEST, "end_request(0): write\n");
+	  end_request(0); return;
+
+      case READ:
+	  stuffp->status = 0;
+	  while (CURRENT->nr_sectors) {
+	      int i;
+
+	      i = mcdx_transfer(stuffp,
+				  CURRENT->buffer,
+				  CURRENT->sector,
+				  CURRENT->nr_sectors);
+
+		  if (i == -1) {
+			  end_request(0);
+			  goto again;
+		  }
+		  CURRENT->sector += i;
+	 	  CURRENT->nr_sectors -= i;
+		  CURRENT->buffer += (i * 512);
+	  }
+	  end_request(1);
+	  goto again;
+
+	  xtrace(REQUEST, "end_request(1)\n");
+	  end_request(1);
+	  break;
+
+      default:
+	  panic(MCDX "do_request: unknown command.\n");
+	  break;
+    }
+
+    goto again;
+}
+
+static int 
+mcdx_open(struct inode *ip, struct file *fp)
+/*  actions done on open:
+ *  1)  get the drives status 
+ *  2)  set the stuffp.readcmd if a CD is in.
+ *  (return no error if no CD is found, since ioctl() 
+ *  needs an opened device */
+{
+    struct s_drive_stuff *stuffp;
+
+	xtrace(OPENCLOSE, "open()\n");
+
+    stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
+    if (!stuffp->present) return -ENXIO;
+
+	/* Make the modules looking used ... (thanx bjorn).
+	 * But we shouldn't forget to decrement the module counter
+	 * on error return */
+	MOD_INC_USE_COUNT;
 
-/*  
- * Read the toc entries from the CD.
- * Return: -1 on failure, else 0 
- */
-int read_toc(struct s_drive_stuff *stuffp)
-{
-	int trk;
-	int i;
+#if 0
+	/* We don't allow multiple users of a drive.  In case of data CDs
+     * they'll be used by mounting, which ensures anyway exclusive
+	 * usage. In case of audio CDs it's meaningless to try playing to
+	 * different tracks at once! (md)
+	 * - Hey, what about cat /dev/cdrom? Why shouldn't it called by
+	 * more then one process at any time?  (hs) */
+	if (stuffp->users) {
+		MOD_DEC_USE_COUNT;
+		return -EBUSY;
+	}
+#endif
 
-	if (stuffp->toc)
-		return 0;
-	if (-1 == issue_command(stuffp, MCDX_CMD_HOLD, 2 * HZ))
-		return -1;
-	if (-1 == set_drive_mode(stuffp, TOC))
+    /* this is only done to test if the drive talks with us */
+    if (-1 == mcdx_getstatus(stuffp, 1)) {
+		MOD_DEC_USE_COUNT;
 		return -EIO;
+	}
 
-	/* All seems to be OK so far ... malloc. When this fails all bets
-	 * are off anyway, so we don't check for it.
-	 */
-	stuffp->toc = kmalloc(sizeof(struct s_subqcode) *
-			 (stuffp->last - stuffp->first + 1), GFP_KERNEL);
-	/* now read actually the index tracks */
-	for (trk = 0; trk < stuffp->last - stuffp->first + 1; trk++)
-		stuffp->toc[trk].index = 0;
-
-	for (i = 300; i; --i) {	/* why 300? */
-		struct s_subqcode q;
-		unsigned int idx;
-
-		if (-1 == request_subq_code(stuffp, &q)) {
-			set_drive_mode(stuffp, DATA);
+	/* close the door,
+	 * This should be explained ...
+	 * - If the door is open and its last close is too recent the 
+	 *   autoclose wouldn't probably be what we want.
+	 * - If we didn't try to close the door yet, close it and go on.
+	 * - If we autoclosed the door and couldn't succeed in find a valid
+	 *   CD we shouln't try autoclose any longer (until a valid CD is
+	 *   in.) */
+
+	if (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_DOOR) {
+        if (jiffies - stuffp->ejected < ACLOSE_INHIBIT) {
+			MOD_DEC_USE_COUNT;
 			return -EIO;
 		}
-		idx = bcd2uint(q.index);
-
-		if (idx > 0 && idx <= stuffp->last && q.tno == 0
-		    && stuffp->toc[idx - stuffp->first].index == 0) {
-			stuffp->toc[idx - stuffp->first] = q;
-			trk--;
-		}
-		if (trk == 0)
-			break;
-	}
-	i = stuffp->last - stuffp->first + 1;
-	memset(&stuffp->toc[i], 0, sizeof(stuffp->toc[0]));
-	stuffp->toc[i].dt = stuffp->msf_leadout;
+        if (stuffp->autoclose) mcdx_closedoor(stuffp, 1);
+		else {
+			MOD_DEC_USE_COUNT;
+			return -EIO;
+		}
+    }
 
-	/* unset toc mode */
-	if (-1 == set_drive_mode(stuffp, DATA))
-		return -EIO;
+	/* if the media changed we will have to do a little more */
+	if (stuffp->xxx) {
 
-	return 0;
-}
+		xtrace(OPENCLOSE, "open() media changed\n");
+        /* but wait - the time of media change will be set at the 
+         * very last of this block - it seems, some of the following
+         * talk() will detect a media change ... (I think, config()
+         * is the reason. */
 
-/*
- * Return 0 on success, error value -1 otherwise.
- */
-static int play_track(struct s_drive_stuff *stuffp, const struct cdrom_ti *ti)
-{
-	struct s_play times;
+        stuffp->audiostatus = CDROM_AUDIO_INVALID;
+		stuffp->readcmd = 0;
 
-	if (ti) {
-		if (-1 == read_toc(stuffp)) {
-			stuffp->audiostatus = CDROM_AUDIO_ERROR;
+		/* get the multisession information */
+		xtrace(OPENCLOSE, "open() Request multisession info\n");
+		if (-1 == mcdx_requestmultidiskinfo(
+				stuffp, &stuffp->multi, 6)) {
+			xinfo("No multidiskinfo\n");
+			stuffp->autoclose = 0;
+			/*
+			MOD_DEC_USE_COUNT;
+			stuffp->xxx = 0;
 			return -EIO;
-		}
-		times.start = stuffp->toc[ti->cdti_trk0 - stuffp->first].dt;
-		times.stop = stuffp->resume.stop =
-		    stuffp->toc[ti->cdti_trk1 - stuffp->first + 1].dt;
-	} else {
-		times = stuffp->resume;
-	}
-	if (-1 == set_command(stuffp, MCDX_CMD_PLAY,
-			      &times, sizeof(times), 5 * HZ)) {
-		printk(KERN_WARNING MCDX ": play track timeout\n");
-		stuffp->audiostatus = CDROM_AUDIO_ERROR;
-		return -EIO;
-	}
-	stuffp->audiostatus = CDROM_AUDIO_PLAY;
+			*/
+		} else {
+			/* we succeeded, so on next open(2) we could try auto close
+			 * again */
+			stuffp->autoclose = 1;
+		
+#if !MCDX_QUIET
+			if (stuffp->multi.multi > 2)
+				xinfo("open() unknown multisession value (%d)\n", 
+						stuffp->multi.multi);
+#endif
 
-	return 0;
-}
+			/* multisession ? */
+			if (!stuffp->multi.multi)
+				stuffp->multi.msf_last.second = 2;
+
+			xtrace(OPENCLOSE, "open() MS: %d, last @ %02x:%02x.%02x\n",
+					stuffp->multi.multi,
+					stuffp->multi.msf_last.minute,
+					stuffp->multi.msf_last.second,
+					stuffp->multi.msf_last.frame);
 
-static int lock_door(struct s_drive_stuff *stuffp, u_char lock)
-{
-	if (stuffp->door)
-		return set_command(stuffp, MCDX_CMD_LOCK_DOOR,
-				   &lock, sizeof(lock), 5 * HZ);
-	return 0;
-}
+		{ ; } /* got multisession information */
 
-/* 
- * KERNEL INTERFACE FUNCTIONS
- */
+		/* request the disks table of contents (aka diskinfo) */
+		if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) {
 
-static int mcdx_ioctl(struct inode *ip, struct file *fp,
-		      unsigned int command, unsigned long arg)
-{
-	int ans;
-	struct cdrom_ti ti;
-	struct cdrom_msf msf;
-	struct cdrom_tocentry entry;
-	struct s_subqcode *tp = NULL;
-	struct cdrom_subchnl sub;
-	struct s_subqcode q;
-	struct cdrom_tochdr toc;
-	struct cdrom_multisession ms;
-	struct cdrom_volctrl volctrl;
-	struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
+			stuffp->lastsector = -1;
 
-	MCDX_TRACE_IOCTL(("mcdx_ioctl():\n"));
+		} else {
 
-	if (!stuffp)
-		return -ENXIO;
-	if (!ip)
-		return -EINVAL;
-
-	switch (command) {
-	case CDROMSTART:	/* spin up the drive */
-		MCDX_TRACE_IOCTL(("CDROMSTART\n"));
-		/* Don't think we can do this.  Even if we could,
-		 * I think the drive times out and stops after a while
-		 * anyway.  For now, ignore it.
-		 */
-		return 0;
+			stuffp->lastsector = (CD_FRAMESIZE / 512) 
+					* msf2log(&stuffp->di.msf_leadout) - 1;
 
-	case CDROMSTOP:
-		MCDX_TRACE_IOCTL(("CDROMSTOP\n"));
-		stuffp->audiostatus = CDROM_AUDIO_INVALID;
-		if (-1 == issue_command(stuffp, MCDX_CMD_STOP, 2 * HZ))
+			xtrace(OPENCLOSE, "open() start %d (%02x:%02x.%02x) %d\n",
+					stuffp->di.n_first,
+					stuffp->di.msf_first.minute,
+					stuffp->di.msf_first.second,
+					stuffp->di.msf_first.frame,
+					msf2log(&stuffp->di.msf_first));
+			xtrace(OPENCLOSE, "open() last %d (%02x:%02x.%02x) %d\n",
+					stuffp->di.n_last,
+					stuffp->di.msf_leadout.minute,
+					stuffp->di.msf_leadout.second,
+					stuffp->di.msf_leadout.frame,
+					msf2log(&stuffp->di.msf_leadout));
+		}
+
+		if (stuffp->toc) {
+			xtrace(MALLOC, "open() free old toc @ %p\n", stuffp->toc);
+			kfree(stuffp->toc);
+		
+			stuffp->toc = NULL;
+		}
+
+		xtrace(OPENCLOSE, "open() init irq generation\n");
+		if (-1 == mcdx_config(stuffp, 1)) {
+			MOD_DEC_USE_COUNT;
 			return -EIO;
-		return 0;
-
-	case CDROMPLAYTRKIND:
-		MCDX_TRACE_IOCTL(("CDROMPLAYTRKIND\n"));
-
-		if ((ans = verify_area(VERIFY_READ, (void *) arg, sizeof(ti))))
-			return ans;
-		memcpy_fromfs(&ti, (void *) arg, sizeof(ti));
-		if (ti.cdti_trk0 < stuffp->first
-		    || ti.cdti_trk0 > stuffp->last
-		    || ti.cdti_trk1 < stuffp->first)
-			return -EINVAL;
-		if (ti.cdti_trk1 > stuffp->last)
-			ti.cdti_trk1 = stuffp->last;
-		return play_track(stuffp, &ti);
-
-	case CDROMPLAYMSF:
-		MCDX_TRACE_IOCTL(("CDROMPLAYMSF "));
-
-		if ((ans = verify_area(VERIFY_READ, (void *) arg,
-				       sizeof(struct cdrom_msf))))
-			 return ans;
-		memcpy_fromfs(&msf, (void *) arg, sizeof msf);
-		msf.cdmsf_min0 = uint2bcd(msf.cdmsf_min0);
-		msf.cdmsf_sec0 = uint2bcd(msf.cdmsf_sec0);
-		msf.cdmsf_frame0 = uint2bcd(msf.cdmsf_frame0);
-		stuffp->resume.stop.minute =
-		    msf.cdmsf_min1 = uint2bcd(msf.cdmsf_min1);
-		stuffp->resume.stop.second =
-		    msf.cdmsf_sec1 = uint2bcd(msf.cdmsf_sec1);
-		stuffp->resume.stop.frame =
-		    msf.cdmsf_frame1 = uint2bcd(msf.cdmsf_frame1);
-		if (-1 == set_command(stuffp, MCDX_CMD_PLAY,
-				      &msf, sizeof(msf), 3 * HZ)) {
-			return -1;
 		}
-		stuffp->audiostatus = CDROM_AUDIO_PLAY;
-		return 0;
 
-	case CDROMPAUSE:
-		MCDX_TRACE_IOCTL(("CDROMPAUSE\n"));
+#if FALLBACK
+		/* Set the read speed */
+		xwarn("AAA %x AAA\n", stuffp->readcmd);
+		if (stuffp->readerrs) stuffp->readcmd = READ1X;
+		else stuffp->readcmd = 
+				stuffp->present | SINGLE ? READ1X : READ2X;
+		xwarn("XXX %x XXX\n", stuffp->readcmd);
+#else
+		stuffp->readcmd = stuffp->present | SINGLE ? READ1X : READ2X;
+#endif
 
-		if (stuffp->audiostatus != CDROM_AUDIO_PLAY)
-			return -EINVAL;
+		/* try to get the first sector, iff any ... */
+		if (stuffp->lastsector >= 0) {
+			char buf[512];
+			int ans;
+			int tries;
 
-		if (-1 == issue_command(stuffp, MCDX_CMD_STOP, 2 * HZ))
-			return -EIO;
-		if (-1 == request_subq_code(stuffp, &q)) {
-			stuffp->audiostatus = CDROM_AUDIO_NO_STATUS;
-			return 0;
-		}
-		stuffp->resume.start = q.dt;
-		stuffp->audiostatus = CDROM_AUDIO_PAUSED;
-		return 0;
+			stuffp->xa = 0;
+			stuffp->audio = 0;
 
-	case CDROMRESUME:
-		MCDX_TRACE_IOCTL(("CDROMRESUME\n"));
+			for (tries = 6; tries; tries--) {
 
-		if (stuffp->audiostatus != CDROM_AUDIO_PAUSED)
-			return -EINVAL;
-		return play_track(stuffp, NULL);
-	case CDROMREADTOCENTRY:
-		MCDX_TRACE_IOCTL(("CDROMREADTOCENTRY\n"));
+				stuffp->introk = 1;
 
-		if (-1 == read_toc(stuffp))
-			return -1;
-		if ((ans = verify_area(VERIFY_READ, (void *) arg, sizeof(entry))))
-			return ans;
-		memcpy_fromfs(&entry, (void *) arg, sizeof(entry));
-
-		if (entry.cdte_track == CDROM_LEADOUT)
-			tp = &stuffp->toc[stuffp->last - stuffp->first + 1];
-		else if (entry.cdte_track > stuffp->last
-			 || entry.cdte_track < stuffp->first)
-			return -EINVAL;
-		else
-			tp = &stuffp->toc[entry.cdte_track - stuffp->first];
-
-		entry.cdte_adr = tp->adr;
-		entry.cdte_ctrl = tp->ctrl;
-
-		if (entry.cdte_format == CDROM_MSF) {
-			entry.cdte_addr.msf.minute = bcd2uint(tp->dt.minute);
-			entry.cdte_addr.msf.second = bcd2uint(tp->dt.second);
-			entry.cdte_addr.msf.frame = bcd2uint(tp->dt.frame);
-		} else if (entry.cdte_format == CDROM_LBA)
-			entry.cdte_addr.lba = msf2log(&tp->dt);
-		else
-			return -EINVAL;
-
-		if ((ans = verify_area(VERIFY_WRITE, (void *) arg, sizeof(entry))))
-			return ans;
-		memcpy_tofs((void *) arg, &entry, sizeof(entry));
+				xtrace(OPENCLOSE, "open() try as %s\n",
+					stuffp->xa ? "XA" : "normal");
 
-		return 0;
+				/* set data mode */
+				if (-1 == (ans = mcdx_setdatamode(stuffp, 
+						stuffp->xa ? MODE2 : MODE1, 1))) {
+					/* MOD_DEC_USE_COUNT, return -EIO; */
+					stuffp->xa = 0;
+					break;
+				}
 
-	case CDROMSUBCHNL:
-		MCDX_TRACE_IOCTL(("CDROMSUBCHNL\n"));
+				if ((stuffp->audio = e_audio(ans))) break; 
 
-		if ((ans = verify_area(VERIFY_READ,
-				       (void *) arg, sizeof(sub))))
-			return ans;
+				while (0 == (ans = mcdx_transfer(stuffp, buf, 0, 1))) 
+					;
 
-		memcpy_fromfs(&sub, (void *) arg, sizeof(sub));
+				if (ans == 1) break;
+				stuffp->xa = !stuffp->xa; 
+			}
+			/* if (!tries) MOD_DEC_USE_COUNT, return -EIO; */
+		}
 
-		if (-1 == request_subq_code(stuffp, &q))
+		/* xa disks will be read in raw mode, others not */
+		if (-1 == mcdx_setdrivemode(stuffp, 
+				stuffp->xa ? RAW : COOKED, 1)) {
+			MOD_DEC_USE_COUNT;
 			return -EIO;
+		}
 
-		sub.cdsc_audiostatus = stuffp->audiostatus;
-		sub.cdsc_adr = q.adr;
-		sub.cdsc_ctrl = q.ctrl;
-		sub.cdsc_trk = bcd2uint(q.tno);
-		sub.cdsc_ind = bcd2uint(q.index);
-
-		if (sub.cdsc_format == CDROM_LBA) {
-			sub.cdsc_absaddr.lba = msf2log(&q.dt);
-			sub.cdsc_reladdr.lba = msf2log(&q.tt);
-		} else if (sub.cdsc_format == CDROM_MSF) {
-			sub.cdsc_absaddr.msf.minute = bcd2uint(q.dt.minute);
-			sub.cdsc_absaddr.msf.second = bcd2uint(q.dt.second);
-			sub.cdsc_absaddr.msf.frame = bcd2uint(q.dt.frame);
-			sub.cdsc_reladdr.msf.minute = bcd2uint(q.tt.minute);
-			sub.cdsc_reladdr.msf.second = bcd2uint(q.tt.second);
-			sub.cdsc_reladdr.msf.frame = bcd2uint(q.tt.frame);
-		} else
-			return -EINVAL;
-
-		if ((ans = verify_area(VERIFY_WRITE, (void *) arg, sizeof(sub))))
-			return ans;
-		memcpy_tofs((void *) arg, &sub, sizeof(sub));
+		if (stuffp->audio) {
+			xinfo("open() audio disk found\n");
+		} else if (stuffp->lastsector >= 0) {
+			xinfo("open() %s%s disk found\n",
+					stuffp->xa ? "XA / " : "",
+					stuffp->multi.multi ? "Multi Session" : "Single Session");
+		} 
 
-		return 0;
+        /* stuffp->xxx = 0; */
+	}
+
+    /* lock the door if not already done */
+    if (0 == stuffp->users && (-1 == mcdx_lockdoor(stuffp, 1, 1)))  {
+		MOD_DEC_USE_COUNT;
+        return -EIO;
+	}
+	} 
 
-	case CDROMREADTOCHDR:
-		MCDX_TRACE_IOCTL(("CDROMREADTOCHDR\n"));
+	stuffp->xxx = 0;
+    stuffp->users++;
+    return 0;
 
-		if ((ans = verify_area(VERIFY_WRITE, (void *) arg, sizeof toc)))
-			return ans;
-		/*
-		 * Make sure, we really read it!
-		 */
-		release_toc(stuffp);
-		if (-1 == request_toc_data(stuffp))
-			return -EIO;
+}
 
-		toc.cdth_trk0 = stuffp->first;
-		toc.cdth_trk1 = stuffp->last;
-		memcpy_tofs((void *) arg, &toc, sizeof toc);
-		return 0;
+static void 
+mcdx_close(struct inode *ip, struct file *fp)
+{
+    struct s_drive_stuff *stuffp;
 
-	case CDROMMULTISESSION:
-		MCDX_TRACE_IOCTL(("CDROMMULTISESSION\n"));
+    xtrace(OPENCLOSE, "close()\n");
 
-		if ((ans = verify_area(VERIFY_READ, (void *) arg,
-				     sizeof(struct cdrom_multisession))))
-			 return ans;
-
-		memcpy_fromfs(&ms,
-			(void *) arg, sizeof(struct cdrom_multisession));
-		if (ms.addr_format == CDROM_MSF) {
-			ms.addr.msf.minute =
-			    bcd2uint(stuffp->multi.msf_last.minute);
-			ms.addr.msf.second =
-			    bcd2uint(stuffp->multi.msf_last.second);
-			ms.addr.msf.frame =
-			    bcd2uint(stuffp->multi.msf_last.frame);
-		} else if (ms.addr_format == CDROM_LBA)
-			ms.addr.lba = msf2log(&stuffp->multi.msf_last);
-		else
-			return -EINVAL;
-		ms.xa_flag = !!stuffp->multi.multi;
-
-		if ((ans = verify_area(VERIFY_WRITE, (void *) arg,
-				     sizeof(struct cdrom_multisession))))
-			 return ans;
+    stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
 
-		memcpy_tofs((void *) arg,
-			    &ms, sizeof(struct cdrom_multisession));
-		return 0;
+    if (0 == --stuffp->users) {
+		sync_dev(ip->i_rdev);	/* needed for r/o device? */
 
-	case CDROMEJECT:
-		MCDX_TRACE_IOCTL(("CDROMEJECT\n"));
-		if (stuffp->door) {
-			if (-1 == issue_command(stuffp, MCDX_CMD_EJECT, 5 * HZ))
-				return -EIO;
-		}
-		/*
-		 * Force rereading of toc next time the disk gets accessed!
-		 */
-		release_toc(stuffp);
+		/* invalidate_inodes(ip->i_rdev); */
+		invalidate_buffers(ip->i_rdev);
 
-		return 0;
 
-	case CDROMEJECT_SW:
-		MCDX_TRACE_IOCTL(("CDROMEJECT_SW\n"));
+#if !MCDX_QUIET
+		if (-1 == mcdx_lockdoor(stuffp, 0, 3))
+				xinfo("close() Cannot unlock the door\n");
+#else
+		mcdx_lockdoor(stuffp, 0, 3);
+#endif
 
-		stuffp->eject_sw = !!arg;
-		return 0;
+        /* eject if wished */
+        if (stuffp->eject_sw) mcdx_eject(stuffp, 1);
 
-	case CDROMVOLCTRL:
-		MCDX_TRACE_IOCTL(("CDROMVOLCTRL\n"));
+    }
 
-		if ((ans = verify_area(VERIFY_READ,
-				       (void *) arg,
-				       sizeof(volctrl))))
-			return ans;
-
-		memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
-		/* Adjust for the weirdness of workman. */
-		volctrl.channel2 = volctrl.channel1;
-		volctrl.channel1 = volctrl.channel3 = 0x00;
-		return talk(stuffp, MCDX_CMD_SET_ATTENATOR,
-			    &volctrl, sizeof(volctrl),
-			    &volctrl, sizeof(volctrl), 2 * HZ);
-
-	default:
-		printk(KERN_WARNING MCDX
-		       ": unknown ioctl request 0x%04x\n", command);
-		return -EINVAL;
-	}
+    MOD_DEC_USE_COUNT;
+    return;
 }
 
-/*   
- * This does actually the transfer from the drive.
- * Return:      -1 on timeout or other error
- */
-static int transfer_data(struct s_drive_stuff *stuffp,
-			 char *p, int sector, int nr_sectors)
+int check_mcdx_media_change(kdev_t full_dev)
+/*	Return: 1 if media changed since last call to this function
+			0 otherwise */
 {
-	int off;
-	int done = 0;
-
-	if (!stuffp->valid || sector < stuffp->pending
-	    || sector > stuffp->border) {
-		unsigned char cmd[6];
-
-		stuffp->valid = 1;
-		stuffp->pending = sector & ~3;
-
-		/* do some sanity checks */
-		if (stuffp->pending > stuffp->lastsector) {
-			printk(KERN_ERR MCDX
-			": sector %d transfer from nirvana requested.\n",
-			       stuffp->pending);
-			stuffp->eom = 1;
-			stuffp->valid = 0;
-			return -1;
-		}
-		if ((stuffp->border = stuffp->pending + REQUEST_SIZE)
-		    > stuffp->lastsector)
-			stuffp->border = stuffp->lastsector;
-		{
-			unsigned int l = (stuffp->pending / 4)
-			+ CD_BLOCK_OFFSET;
+    struct s_drive_stuff *stuffp; 
 
-			cmd[0] = uint2bcd(l / 4500), l %= 4500;		/* minute */
-			cmd[1] = uint2bcd(l / 75);	/* second */
-			cmd[2] = uint2bcd(l % 75);	/* frame */
-		}
+    xinfo("check_mcdx_media_change called for device %s\n",
+	  kdevname(full_dev));
 
-		stuffp->busy = 1;
-		/*
-		 * FIXME: What about the ominous frame length?!
-		 */
-		cmd[5] = cmd[4] = cmd[3] = ~0;
-
-		outb(stuffp->double_speed ? MCDX_CMD_PLAY_2X : MCDX_CMD_PLAY,
-		     DATA_REG);
-		outsb(DATA_REG, cmd, 6);
-	}
-	off = sector + nr_sectors;
-	if (stuffp->border < off)
-		off = stuffp->border;
-	do {
-		/* wait for the drive become idle, but first
-		 * check for possible occurred errors --- the drive
-		 * seems to report them asynchronously
-		 */
-		current->timeout = jiffies + 5 * HZ;
-		while (stuffp->introk && stuffp->busy
-		       && current->timeout) {
-			interruptible_sleep_on(&stuffp->busyq);
-		}
+	stuffp = mcdx_stuffp[MINOR(full_dev)];
+	mcdx_getstatus(stuffp, 1);
 
-		/* test for possible errors */
-		if (current->timeout == 0 || !stuffp->introk) {
-			if (current->timeout == 0) {
-				printk(KERN_ERR MCDX ": transfer timeout.\n");
-			}
-			/*
-			 * We don't report about !stuffp->introk, since this is
-			 * already done in the interrupt routine.
-			 */
-			stuffp->busy = 0;
-			stuffp->valid = 0;
-			stuffp->introk = 1;
-			return -1;
-		}
-		/* test if it's the first sector of a block,
-		 * there we have to skip some bytes as we read raw data 
-		 */
-		if (stuffp->xa && (0 == (stuffp->pending & 3))) {
-			insb(DATA_REG, p,
-			   CD_FRAMESIZE_RAW - CD_XA_TAIL - CD_FRAMESIZE);
-		}
-		/* now actually read the data */
-		insb(DATA_REG, p, 512);
+	if (stuffp->yyy == 0) return 0;
 
-		/* test if it's the last sector of a block,
-		 * if so, we have to expect an interrupt and to skip 
-		 * some data too 
-		 */
-		if ((stuffp->busy = (3 == (stuffp->pending & 3)))
-		    && stuffp->xa) {
-			int i;
-			for (i = 0; i < CD_XA_TAIL; ++i)
-				inb(DATA_REG);
-		}
-		if (stuffp->pending == sector) {
-			p += 512;
-			done++;
-			sector++;
-		}
-	} while (++(stuffp->pending) < off);
+	stuffp->yyy = 0;
+	return 1;
+}
 
-	return done;
+void mcdx_setup(char *str, int *pi)
+{
+	if (pi[0] > 0) mcdx_drive_map[0][0] = pi[1];
+	if (pi[0] > 1) mcdx_drive_map[0][1] = pi[2];
 }
 
-void do_mcdx_request()
+/* DIRTY PART ******************************************************/ 
+
+static void mcdx_delay(struct s_drive_stuff *stuff, long jifs)
+/* This routine is used for sleeping.
+ * A jifs value <0 means NO sleeping,
+ *              =0 means minimal sleeping (let the kernel
+ *                 run for other processes)
+ *              >0 means at least sleep for that amount.
+ *	May be we could use a simple count loop w/ jumps to itself, but 
+ *	I wanna make this independend of cpu speed. [1 jiffie is 1/HZ] sec */
+{
+    unsigned long tout = jiffies + jifs;
+    if (jifs < 0) return;
+
+	/* If loaded during kernel boot no *_sleep_on is
+	 * allowed! */
+    if (current->pid == 0) {        
+		while (jiffies < tout) {
+            current->timeout = jiffies;
+            schedule();
+        }
+    } else {  
+        current->timeout = tout;
+		xtrace(SLEEP, "*** delay: sleepq\n");
+		interruptible_sleep_on(&stuff->sleepq);
+		xtrace(SLEEP, "delay awoken\n");
+		if (current->signal & ~current->blocked) {
+			xtrace(SLEEP, "got signal\n");
+		}
+	} 
+}
+
+static void 
+mcdx_intr(int irq, void *dev_id, struct pt_regs* regs)
 {
-	int dev;
-	struct s_drive_stuff *stuffp;
+    struct s_drive_stuff *stuffp;
+	unsigned char b;
 
-      again:
+    stuffp = mcdx_irq_map[irq];
 
-	if ((CURRENT == NULL) || (CURRENT->rq_status == RQ_INACTIVE)) {
+    if (stuffp == NULL ) {
+		xwarn("mcdx: no device for intr %d\n", irq);
 		return;
-	}
-	stuffp = mcdx_stuffp[MINOR(CURRENT->rq_dev)];
+    }
 
-	INIT_REQUEST;
-	dev = MINOR(CURRENT->rq_dev);
-	if (dev < 0 || dev >= MCDX_NDRIVES || !stuffp || stuffp->audio) {
-		end_request(0);
-		goto again;
-	}
-	switch (CURRENT->cmd) {
-	case WRITE:
-		end_request(0);
-		break;
-
-	case READ:
-		stuffp->eom = 0;	/* clear end of media flag */
-		while (CURRENT->nr_sectors) {
-			int i;
+#ifdef AK2
+	if ( !stuffp->busy && stuffp->pending )
+		stuffp->int_err = 1;
+
+#endif /* AK2 */
+	/* get the interrupt status */
+	b = inb((unsigned int) stuffp->rreg_status);
+	stuffp->introk = ~b & MCDX_RBIT_DTEN;
 
-			i = transfer_data(stuffp, CURRENT->buffer,
-				   CURRENT->sector, CURRENT->nr_sectors);
-			if (i == -1) {
-				if (stuffp->eom) {
-					CURRENT->sector += CURRENT->nr_sectors;
-					CURRENT->nr_sectors = 0;
-				} else
-					break;	/* FIXME: drop down speed ??? */
-				end_request(0);
-				goto again;
-			}
-			CURRENT->sector += i;
-			CURRENT->nr_sectors -= i;
-			CURRENT->buffer += (i * 512);
-		}
-		end_request(1);
-		break;
+	/* NOTE: We only should get interrupts if the data we 
+	 * requested are ready to transfer.
+	 * But the drive seems to generate ``asynchronous'' interrupts
+	 * on several error conditions too.  (Despite the err int enable
+	 * setting during initialisation) */
 
-	default:
-		panic(MCDX "do_request: unknown command.\n");
-		break;
-	}
+	/* if not ok, read the next byte as the drives status */
+	if (!stuffp->introk) {
+		xtrace(IRQ, "intr() irq %d hw status 0x%02x\n", irq, b);
+		if (~b & MCDX_RBIT_STEN) {
+			xinfo(  "intr() irq %d    status 0x%02x\n", 
+					irq, inb((unsigned int) stuffp->rreg_data));
+		} else {
+			xinfo(  "intr() irq %d ambigous hw status\n", irq);
+		}
+	} else {
+		xtrace(IRQ, "irq() irq %d ok, status %02x\n", irq, b);
+    }
 
-	goto again;
+    stuffp->busy = 0;
+    wake_up_interruptible(&stuffp->busyq);
 }
 
-/*  
- * actions done on open:
- * 1)   get the drives status 
- * 2)   handle disk changes
+
+static int 
+mcdx_talk (
+		struct s_drive_stuff *stuffp, 
+		const unsigned char *cmd, size_t cmdlen,
+		void *buffer, size_t size, 
+		unsigned int timeout, int tries)
+/* Send a command to the drive, wait for the result.
+ * returns -1 on timeout, drive status otherwise
+ * If buffer is not zero, the result (length size) is stored there.
+ * If buffer is zero the size should be the number of bytes to read
+ * from the drive.  These bytes are discarded.
  */
-static int mcdx_open(struct inode *ip, struct file *fp)
 {
-	struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
-	int st = 0;
-	unsigned long bang;
+	int st;
+    char c;
+    int discard;
 
-	MCDX_TRACE(("mcdx_open()\n"));
+	/* Somebody wants the data read? */
+    if ((discard = (buffer == NULL))) buffer = &c;
 
-	if (!stuffp)
-		return -ENXIO;
+    while (stuffp->lock) {
+		xtrace(SLEEP, "*** talk: lockq\n");
+		interruptible_sleep_on(&stuffp->lockq); 
+		xtrace(SLEEP, "talk: awoken\n");
+	}
+
+    stuffp->lock = 1;
+
+	/* An operation other then reading data destroys the
+     * data already requested and remembered in stuffp->request, ... */
+    stuffp->valid = 0;	
+
+#if MCDX_DEBUG & TALK
+	{ 
+		unsigned char i;
+		xtrace(TALK, "talk() %d / %d tries, res.size %d, command 0x%02x", 
+				tries, timeout, size, (unsigned char) cmd[0]);
+		for (i = 1; i < cmdlen; i++) xtrace(TALK, " 0x%02x", cmd[i]);
+		xtrace(TALK, "\n");
+	}
+#endif
 
-	/* We don't allow multiple users of a drive. In case of data CD's they
-	 * will be only used by mounting, which ensures anyway exclusive usage.
-	 * In case of sound CD's it's anyway meaningless to try playing two
-	 * different tracks at once! This saves us A LOT of trouble.
-	 */
-	if (stuffp->used)
-		return -EBUSY;
-
-	/* close the door, if necessary (get the door information
-	 * from the hardware status register). 
-	 * If we can't read the CD after an autoclose
-	 * no further auto-closes will be tried 
-	 */
-	if (inb(STAT_REG) & MCDX_RBIT_DOOR) {
-		if (stuffp->autoclose && (stuffp->door))
-			issue_command(stuffp, MCDX_CMD_CLOSE_DOOR, 5 * HZ);
-		else
-			return -EIO;
-	}
-	/*
-	 * Check if a disk is in.
-	 */
-	bang = jiffies + 5 * HZ;
-	while (jiffies < bang) {
-		st = issue_command(stuffp, MCDX_CMD_GET_STATUS, 1 * HZ);
-		if (st != -1 && (st & MCDX_RBIT_DISKSET))
-			break;
-		current->state = TASK_INTERRUPTIBLE;
-		current->timeout = jiffies + 1 * HZ;
-		schedule();
-	}
-	if (st == -1 || (st & MCDX_RBIT_DISKSET) == 0) {
-		printk(KERN_INFO MCDX ": no disk in drive\n");
-		return -EIO;
-	}
-	/* if the media changed we will have to do a little more 
-	 * FIXME: after removing of the mcdx_requestmultisession() it is showed
-	 * that the logics of this may be broken.
-	 */
-	if (stuffp->xxx) {
-		/* but wait - the time of media change will be set at the 
-		 * very last of this block.
-		 */
-
-		stuffp->audiostatus = CDROM_AUDIO_INVALID;
-		stuffp->autoclose = 1;
-
-		/* get the multisession information */
+    /*  give up if all tries are done (bad) or if the status
+     *  st != -1 (good) */
+	for (st = -1; st == -1 && tries; tries--) {
+
+		char *bp = (char*) buffer;
+		size_t sz = size;
+
+		outsb((unsigned int) stuffp->wreg_data, cmd, cmdlen);
+        xtrace(TALK, "talk() command sent\n");
+
+        /* get the status byte */
+        if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) {
+            xinfo("talk() %02x timed out (status), %d tr%s left\n", 
+                    cmd[0], tries - 1, tries == 2 ? "y" : "ies");
+                continue; 
+        }
+        st = *bp;
+        sz--;
+        if (!discard) bp++;
+
+        xtrace(TALK, "talk() got status 0x%02x\n", st);
+
+        /* command error? */
+        if (e_cmderr(st)) {
+            xwarn("command error cmd = %02x %s \n", 
+                    cmd[0], cmdlen > 1 ? "..." : "");
+            st = -1;
+            continue;
+		}
+
+        /* audio status? */
+        if (stuffp->audiostatus == CDROM_AUDIO_INVALID)
+            stuffp->audiostatus = 
+                    e_audiobusy(st) ? CDROM_AUDIO_PLAY : CDROM_AUDIO_NO_STATUS;
+        else if (stuffp->audiostatus == CDROM_AUDIO_PLAY 
+                && e_audiobusy(st) == 0)
+            stuffp->audiostatus = CDROM_AUDIO_COMPLETED;
+
+        /* media change? */
+        if (e_changed(st)) {
+            xinfo("talk() media changed\n");
+            stuffp->xxx = stuffp->yyy = 1;
+        }
+
+        /* now actually get the data */
+        while (sz--) {
+            if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) {
+                xinfo("talk() %02x timed out (data), %d tr%s left\n", 
+                        cmd[0], tries - 1, tries == 2 ? "y" : "ies");
+                st = -1; break;
+            }
+            if (!discard) bp++;
+            xtrace(TALK, "talk() got 0x%02x\n", *(bp - 1));
+        }
+    }
 
-		if (stuffp->multi_cap) {
-			int i = 6;	/* number of retries */
+#if !MCDX_QUIET
+    if (!tries && st == -1) xinfo("talk() giving up\n");
+#endif
 
-			while (i--)
-				if (-1 != get_command(stuffp, MCDX_CMD_GET_MDISK_INFO,
-						      &stuffp->multi,
-						  sizeof(struct s_multi),
-						      2 * HZ))
-					 break;
-
-			if (!i) {
-				stuffp->autoclose = 0;	/* don't try it again on next open */
-				if (stuffp->door)
-					issue_command(stuffp, MCDX_CMD_EJECT, 5 * HZ);
-				return -EIO;
-			}
-		} else
-			stuffp->multi.multi = 0;
+    stuffp->lock = 0;
+    wake_up_interruptible(&stuffp->lockq);
 
-		if (!stuffp->multi.multi)
-			stuffp->multi.msf_last.second = 2;
-		release_toc(stuffp);	/* force rereading */
-		if (-1 == request_toc_data(stuffp))
-			stuffp->lastsector = -1;
-		else {
-			stuffp->lastsector = (CD_FRAMESIZE / 512)
-			    * msf2log(&stuffp->msf_leadout) - 1;
-		}
-		if (-1 == config_drive(stuffp))
-			return -EIO;
+	xtrace(TALK, "talk() done with 0x%02x\n", st);
+    return st;
+}
 
-		/* try to get the first sector, iff any ... */
-		if (stuffp->lastsector >= 0) {
-			int tries;
+/* MODULE STUFF ***********************************************************/
+#ifdef MODULE
 
-			stuffp->xa = 0;
-			stuffp->audio = 0;
-			for (tries = 6; tries; tries--) {
-				char buf[512];
-				int st;
-				unsigned char c;
-				stuffp->introk = 1;
+int init_module(void)
+{
+	int i;
+	int drives = 0;
 
-				/* set data mode */
-				c = stuffp->xa ? MODE2 : MODE1;
-				st = set_command(stuffp,
-						 MCDX_CMD_SET_DATA_MODE,
-						 &c, sizeof(c), 5 * HZ);
-				if (-1 == st) {
-					stuffp->xa = 0;
-					continue;
-				} else if (st & MCDX_RBIT_AUDIOTR) {
-					stuffp->audio = 1;
-					break;
-				}
-				while (0 == (st = transfer_data(stuffp, buf,
-								0, 1)));
-				if (st == 1)
-					break;
-				stuffp->xa = !stuffp->xa;
-			}
-			/* if (!tries) return -EIO; */
+	mcdx_init();
+	for (i = 0; i < MCDX_NDRIVES; i++)  {
+		if (mcdx_stuffp[i]) {
+		xtrace(INIT, "init_module() drive %d stuff @ %p\n",
+				i, mcdx_stuffp[i]);
+			drives++;
 		}
-		/* xa disks will be read in raw mode, others not */
-		if (-1 == set_drive_mode(stuffp, stuffp->xa ? RAW : COOKED))
-			return -EIO;
-		stuffp->xxx = 0;
 	}
-	/* lock the door if not already done */
-	if (!stuffp->used && (-1 == lock_door(stuffp, DOOR_LOCK)))
+
+    if (!drives) 
 		return -EIO;
 
-	stuffp->used = 1;
-	MOD_INC_USE_COUNT;
-	return 0;
+    register_symtab(0);
+    return 0;
 }
 
-static void mcdx_close(struct inode *ip, struct file *fp)
+void cleanup_module(void)
 {
-	struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(ip->i_rdev)];
-
-	MCDX_TRACE(("mcdx_close()\n"));
-
-	sync_dev(ip->i_rdev);	/* needed for r/o device? */
+    int i;
 
-	/* invalidate_inodes(ip->i_rdev); */
-	invalidate_buffers(ip->i_rdev);
-	lock_door(stuffp, DOOR_UNLOCK);
+	xinfo("cleanup_module called\n");
+	
+    for (i = 0; i < MCDX_NDRIVES; i++) {
+		struct s_drive_stuff *stuffp;
+		stuffp = mcdx_stuffp[i];
+		if (!stuffp) continue;
+		release_region((unsigned long) stuffp->wreg_data, MCDX_IO_SIZE);
+		free_irq(stuffp->irq, NULL);
+		if (stuffp->toc) {
+			xtrace(MALLOC, "cleanup_module() free toc @ %p\n", stuffp->toc);
+			kfree(stuffp->toc);
+		}
+		xtrace(MALLOC, "cleanup_module() free stuffp @ %p\n", stuffp);
+		mcdx_stuffp[i] = NULL;
+		kfree(stuffp);
+    }
 
-	/* eject if wished and possible */
-	if (stuffp->eject_sw && (stuffp->door)) {
-		issue_command(stuffp, MCDX_CMD_EJECT, 5 * HZ);
-	}
-	stuffp->used = 0;
-	MOD_DEC_USE_COUNT;
+    if (unregister_blkdev(MAJOR_NR, DEVICE_NAME) != 0) {
+        xwarn("cleanup() unregister_blkdev() failed\n");
+    } 
+#if !MCDX_QUIET
+	else xinfo("cleanup() succeeded\n");
+#endif
 }
 
-/*      
- * Return: 1 if media changed since last call to this function, 0 otherwise.
- */
-static int mcdx_media_change(kdev_t full_dev)
-{
-	struct s_drive_stuff *stuffp;
+#endif MODULE
 
-	MCDX_TRACE(("mcdx_media_change()\n"));
+/* Support functions ************************************************/
 
-	/*
-	 * FIXME: probably this is unneeded or should be simplified!
-	 */
-	issue_command(stuffp = mcdx_stuffp[MINOR(full_dev)],
-		      MCDX_CMD_GET_STATUS, 5 * HZ);
-
-	return stuffp->xxx;
-}
-
-/* Interrupt handler routine.
- * This function is called, when during transfer the end of a physical 2048
- * byte block is reached.
- */
-static void mcdx_intr(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct s_drive_stuff *stuffp;
-	u_char b;
-
-	if (!(stuffp = mcdx_irq_map[irq])) {
-		return;		/* huh? */
-	}
-	/* NOTE: We only should get interrupts if data were requested.
-	 * But the drive seems to generate ``asynchronous'' interrupts
-	 * on several error conditions too.  (Despite the err int enable
-	 * setting during initialization) 
-	 */
-	b = inb(STAT_REG);
-	if (!(b & MCDX_RBIT_DTEN))
-		stuffp->introk = 1;
-	else {
-		stuffp->introk = 0;
-		if (!(b & MCDX_RBIT_STEN)) {
-			b = inb(DATA_REG);
-			if (stuffp->used)
-				printk(KERN_DEBUG MCDX
-				       ": irq %d status 0x%02x\n", irq, b);
-		} else
-			MCDX_TRACE(("irq %d ambiguous hw status\n", irq));
-	}
-	stuffp->busy = 0;
-	wake_up_interruptible(&stuffp->busyq);
-}
-
-/*
- * FIXME:
- * This seems to hang badly, when the driver is loaded with inappropriate
- * port/irq settings!
- */
 int mcdx_init(void)
 {
 	int drive;
 
 #ifdef MODULE
-	printk(KERN_INFO "Mitsumi driver V" VERSION " for %s\n",
-	       kernel_version);
+	xwarn("Version 2.14(hs) for %s\n", kernel_version);
 #else
-	printk(KERN_INFO "Mitsumi driver V" VERSION "\n");
+	xwarn("Version 2.14(hs) \n");
 #endif
-	for (drive = 0; drive < MCDX_NDRIVES; drive++) {
-		struct {
-			u_char code;
-			u_char version;
-		} firmware;
-		int i;
-		struct s_drive_stuff *stuffp;
-		int size;
 
-		mcdx_blksize_size[drive] = 0;
-		mcdx_stuffp[drive] = 0;
+	xwarn("$Id: mcdx.c,v 1.12 1996/06/05 01:38:38 heiko Exp $\n");
 
-		size = sizeof(*stuffp);
+	/* zero the pointer array */
+	for (drive = 0; drive < MCDX_NDRIVES; drive++)
+		mcdx_stuffp[drive] = NULL;
 
-		if (!(stuffp = kmalloc(size, GFP_KERNEL)))
-			break;
+	/* do the initialisation */
+	for (drive = 0; drive < MCDX_NDRIVES; drive++) { 
+		struct s_version version;
+		struct s_drive_stuff* stuffp;
+        int size;
+
+		mcdx_blocksizes[drive] = 0;
+
+        size = sizeof(*stuffp);
+		
+		xtrace(INIT, "init() try drive %d\n", drive);
+
+        xtrace(INIT, "kmalloc space for stuffpt's\n");
+		xtrace(MALLOC, "init() malloc %d bytes\n", size);
+		if (!(stuffp = kmalloc(size, GFP_KERNEL))) {
+			xwarn("init() malloc failed\n");
+			break; 
+		}
+
+		xtrace(INIT, "init() got %d bytes for drive stuff @ %p\n", sizeof(*stuffp), stuffp);
 
 		/* set default values */
 		memset(stuffp, 0, sizeof(*stuffp));
-		stuffp->autoclose = 1;	/* close the door on open(2) */
+        stuffp->autoclose = 1;      /* close the door on open(2) */
 
-		stuffp->base = mcdx_drive_map[drive][PORT];
-		stuffp->irq = mcdx_drive_map[drive][IRQ];
+		stuffp->present = 0;		/* this should be 0 already */
+		stuffp->toc = NULL;			/* this should be NULL already */
+
+		/* setup our irq and i/o addresses */
+		stuffp->irq = irq(mcdx_drive_map[drive]);
+		stuffp->wreg_data = stuffp->rreg_data = port(mcdx_drive_map[drive]);
+		stuffp->wreg_reset = stuffp->rreg_status = stuffp->wreg_data + 1;
+		stuffp->wreg_hcon = stuffp->wreg_reset + 1;
+		stuffp->wreg_chn = stuffp->wreg_hcon + 1;
 
 		/* check if i/o addresses are available */
-		if (check_region(stuffp->base, MCDX_IO_SIZE)) {
-			printk(KERN_WARNING
-			       "Init failed. I/O ports (0x%3x..0x%3x) "
-			       "already in use.\n",
-			  stuffp->base, stuffp->base + MCDX_IO_SIZE - 1);
-			kfree(stuffp);
-			continue;	/* next drive */
+		if (0 != check_region((unsigned int) stuffp->wreg_data, MCDX_IO_SIZE)) {
+            xwarn("0x%3p,%d: "
+                    "Init failed. I/O ports (0x%3p..0x%3p) already in use.\n",
+                    stuffp->wreg_data, stuffp->irq,
+                    stuffp->wreg_data, 
+                    stuffp->wreg_data + MCDX_IO_SIZE - 1);
+			xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp);
+            kfree(stuffp);
+			xtrace(INIT, "init() continue at next drive\n");
+			continue; /* next drive */
 		}
-		/*
-		 * Hardware reset.
-		 */
-		outb(0, CHAN_REG);	/* no dma, no irq -> hardware */
-		outb(0, RESET_REG);	/* hw reset */
-
-		i = 10;		/* number of retries */
-		while (-1 == get_command(stuffp, MCDX_CMD_GET_FIRMWARE,
-				    &firmware, sizeof(firmware), 2 * HZ))
-			--i;
-		if (!i) {
+
+		xtrace(INIT, "init() i/o port is available at 0x%3p\n", stuffp->wreg_data);
+
+		xtrace(INIT, "init() hardware reset\n");
+		mcdx_reset(stuffp, HARD, 1);
+
+		xtrace(INIT, "init() get version\n");
+		if (-1 == mcdx_requestversion(stuffp, &version, 4)) {
 			/* failed, next drive */
-			printk(KERN_WARNING
-			"%s=0x%3x,%d: Init failed. Can't get version.\n",
-			       MCDX, stuffp->base, stuffp->irq);
-			kfree(stuffp);
+            xwarn("%s=0x%3p,%d: Init failed. Can't get version.\n",
+                    MCDX,
+                    stuffp->wreg_data, stuffp->irq);
+			xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp);
+            kfree(stuffp);
+			xtrace(INIT, "init() continue at next drive\n");
 			continue;
 		}
-		switch (firmware.code) {
-		case 'D':
-			stuffp->double_speed = stuffp->door =
-			    stuffp->multi_cap = 1;
-			break;
-		case 'F':
-			stuffp->door = stuffp->multi_cap = 1;
-			break;
-		case 'M':
-			break;
-		default:
+
+		switch (version.code) {
+		case 'D': 
+                stuffp->readcmd = READ2X; 
+                stuffp->present = DOUBLE | DOOR | MULTI; 
+                break;
+		case 'F': 
+                stuffp->readcmd = READ1X; 
+                stuffp->present = SINGLE | DOOR | MULTI;
+                break;
+		case 'M': 
+                stuffp->readcmd = READ1X;
+                stuffp->present = SINGLE;
+                break;
+		default: 
+                stuffp->present = 0; break;
+		}
+
+        stuffp->playcmd = READ1X;
+
+
+		if (!stuffp->present) {
+            xwarn("%s=0x%3p,%d: Init failed. No Mitsumi CD-ROM?.\n",
+                    MCDX, stuffp->wreg_data, stuffp->irq);
 			kfree(stuffp);
+			continue; /* next drive */
 		}
 
-		if (!stuffp)
-			continue;	/* next drive */
-
-		if (register_blkdev(MAJOR_NR, DEVICE_NAME, &mcdx_fops)) {
+		xtrace(INIT, "init() register blkdev\n");
+		if (register_blkdev(MAJOR_NR, DEVICE_NAME, &mcdx_fops) != 0) {
+            xwarn("%s=0x%3p,%d: Init failed. Can't get major %d.\n",
+                    MCDX,
+                    stuffp->wreg_data, stuffp->irq, MAJOR_NR);
 			kfree(stuffp);
-			continue;	/* next drive */
+			continue; /* next drive */
 		}
-		/*
-		 * CD-ROM's are an example of non 1024 devices
-		 */
-		mcdx_blksize_size[drive] = 1024;
+
 		blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
 		read_ahead[MAJOR_NR] = READ_AHEAD;
-		blksize_size[MAJOR_NR] = mcdx_blksize_size;
 
+		blksize_size[MAJOR_NR] = mcdx_blocksizes;
+
+		xtrace(INIT, "init() subscribe irq and i/o\n");
 		mcdx_irq_map[stuffp->irq] = stuffp;
-		if (request_irq(stuffp->irq, mcdx_intr,
-				SA_INTERRUPT, DEVICE_NAME, NULL)) {
+		if (request_irq(stuffp->irq, mcdx_intr, SA_INTERRUPT, DEVICE_NAME, NULL)) {
+            xwarn("%s=0x%3p,%d: Init failed. Can't get irq (%d).\n",
+                    MCDX,
+                    stuffp->wreg_data, stuffp->irq, stuffp->irq);
 			stuffp->irq = 0;
 			kfree(stuffp);
-			continue;	/* next drive */
+			continue;
+		}
+		request_region((unsigned int) stuffp->wreg_data, 
+                MCDX_IO_SIZE, 
+                DEVICE_NAME); 
+
+		xtrace(INIT, "init() get garbage\n");
+		{
+			int i;
+			mcdx_delay(stuffp, HZ/2);
+			for (i = 100; i; i--) (void) inb((unsigned int) stuffp->rreg_status);
 		}
-		request_region(stuffp->base, MCDX_IO_SIZE, DEVICE_NAME);
 
-		/* get junk after some delay.
-		 */
-		current->state = TASK_INTERRUPTIBLE;
-		current->timeout = jiffies + HZ / 2;
-		schedule();
-		for (i = 100; i; i--)
-			(void) inb(STAT_REG);
 
 #if WE_KNOW_WHY
-		outb(0x50, CHAN_REG);	/* irq 11 -> channel register */
+			outb(0x50, (unsigned int) stuffp->wreg_chn);	/* irq 11 -> channel register */
 #endif
 
-		config_drive(stuffp);
+		xtrace(INIT, "init() set non dma but irq mode\n");
+		mcdx_config(stuffp, 1);
+
+		stuffp->minor = drive;
 
-		printk(KERN_INFO MCDX
-		       "%d: at 0x%3x, irq %d, type: %c, firmware: %x\n",
-		       drive, stuffp->base, stuffp->irq,
-		       firmware.code, firmware.version);
+		xwarn("(%s) installed at 0x%3p, irq %d."
+			   " (Firmware version %c %x)\n",
+			   DEVICE_NAME,
+			   stuffp->wreg_data, stuffp->irq, version.code,
+               version.ver);
 		mcdx_stuffp[drive] = stuffp;
+		xtrace(INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp);
 	}
 
 	return 0;
 }
 
-#ifdef MODULE
+static int 
+mcdx_transfer(struct s_drive_stuff *stuffp,
+		char *p, int sector, int nr_sectors)
+/*	This seems to do the actually transfer.  But it does more.  It
+	keeps track of errors ocurred and will (if possible) fall back
+	to single speed on error. 
+	Return:	-1 on timeout or other error
+			else status byte (as in stuff->st) */
+{
+	int ans;
 
-int init_module(void)
+	ans = mcdx_xfer(stuffp, p, sector, nr_sectors);
+	return ans;
+#if FALLBACK
+	if (-1 == ans) stuffp->readerrs++;
+	else return ans;
+
+	if (stuffp->readerrs && stuffp->readcmd == READ1X) {
+		xwarn("XXX Alrady reading 1x -- no chance\n");
+		return -1;
+	}
+
+	xwarn("XXX Fallback to 1x\n");
+
+	stuffp->readcmd = READ1X;
+	return mcdx_transfer(stuffp, p, sector, nr_sectors);
+#endif
+	
+}
+
+
+static int mcdx_xfer(struct s_drive_stuff *stuffp,
+		char *p, int sector, int nr_sectors)
+/*	This does actually the transfer from the drive.
+	Return:	-1 on timeout or other error
+			else status byte (as in stuff->st) */
 {
-	int i;
+    int border;
+    int done = 0;
+	
+	if (stuffp->audio) {
+			xwarn("Attempt to read from audio CD.\n");
+			return -1;
+	}
 
-	mcdx_init();
-	for (i = 0; i < MCDX_NDRIVES; i++) {
-		if (mcdx_stuffp[i]) {
-			register_symtab(0);
-			return 0;
+	if (!stuffp->readcmd) {
+			xinfo("Can't transfer from missing disk.\n");
+			return -1;
+	}
+
+    while (stuffp->lock) {
+		interruptible_sleep_on(&stuffp->lockq);
+	}
+
+    if (stuffp->valid
+			&& (sector >= stuffp->pending)
+			&& (sector < stuffp->low_border)) {
+
+		/* All (or at least a part of the sectors requested) seems
+         * to be already requested, so we don't need to bother the
+		 * drive with new requests ...
+		 * Wait for the drive become idle, but first
+		 * check for possible occured errors --- the drive
+		 * seems to report them asynchronously */
+
+
+	border = stuffp->high_border < (border = sector + nr_sectors)
+			? stuffp->high_border : border;
+
+	stuffp->lock = current->pid;
+
+	do {
+
+	    current->timeout = jiffies + 5 * HZ;
+	    while (stuffp->busy) {
+
+			interruptible_sleep_on(&stuffp->busyq);
+
+			if (!stuffp->introk) { xtrace(XFER, "error via interrupt\n"); }
+			else if (current->timeout == 0) { xtrace(XFER, "timeout\n"); } 
+			else if (current->signal & ~current->blocked) {
+				xtrace(XFER, "signal\n"); 
+			} else continue;
+
+			stuffp->lock = 0;
+			stuffp->busy = 0;
+			stuffp->valid = 0;
+
+			wake_up_interruptible(&stuffp->lockq);
+			xtrace(XFER, "transfer() done (-1)\n");
+			return -1;
+	    }
+
+ 		/* check if we need to set the busy flag (as we
+		 * expect an interrupt */
+		stuffp->busy = (3 == (stuffp->pending & 3));
+
+		/* Test if it's the first sector of a block,
+		 * there we have to skip some bytes as we read raw data */
+		if (stuffp->xa && (0 == (stuffp->pending & 3))) {
+			const int HEAD = CD_FRAMESIZE_RAW - CD_XA_TAIL - CD_FRAMESIZE;
+			insb((unsigned int) stuffp->rreg_data, p, HEAD);
+		}
+
+		/* now actually read the data */ 
+	    insb((unsigned int) stuffp->rreg_data, p, 512); 
+
+		/* test if it's the last sector of a block,
+		 * if so, we have to handle XA special */
+		if ((3 == (stuffp->pending & 3)) && stuffp->xa) {
+			char dummy[CD_XA_TAIL];
+			insb((unsigned int) stuffp->rreg_data, &dummy[0], CD_XA_TAIL);
+		}
+
+	    if (stuffp->pending == sector) {
+			p += 512;
+			done++;
+			sector++;
+	    }
+	} while (++(stuffp->pending) < border);
+
+	stuffp->lock = 0;
+	wake_up_interruptible(&stuffp->lockq);
+
+    } else {
+
+		/* The requested sector(s) is/are out of the 
+		 * already requested range, so we have to bother the drive
+		 * with a new request. */
+
+		static unsigned char cmd[] = {
+			0,
+			0, 0, 0,
+			0, 0, 0
+		};
+
+		cmd[0] = stuffp->readcmd;
+
+		/* The numbers held in ->pending, ..., should be valid */
+		stuffp->valid = 1;
+		stuffp->pending = sector & ~3;
+
+		/* do some sanity checks */
+		if (stuffp->pending > stuffp->lastsector) {
+			xwarn("transfer() sector %d from nirvana requested.\n",
+				stuffp->pending);
+			stuffp->status = MCDX_ST_EOM;
+			stuffp->valid = 0;
+			xtrace(XFER, "transfer() done (-1)\n");
+			return -1;
 		}
+
+		if ((stuffp->low_border = stuffp->pending + DIRECT_SIZE)
+				> stuffp->lastsector + 1) {
+			xtrace(XFER, "cut low_border\n");
+			stuffp->low_border = stuffp->lastsector + 1;
+		}
+		if ((stuffp->high_border = stuffp->pending + REQUEST_SIZE)
+				> stuffp->lastsector + 1) {
+			xtrace(XFER,  "cut high_border\n");
+			stuffp->high_border = stuffp->lastsector + 1;
+		}
+
+		{	/* Convert the sector to be requested to MSF format */
+			struct cdrom_msf0 pending;
+			log2msf(stuffp->pending / 4, &pending);
+			cmd[1] = pending.minute;
+			cmd[2] = pending.second;
+			cmd[3] = pending.frame;
+		}
+
+		cmd[6] = (unsigned char) ((stuffp->high_border - stuffp->pending) / 4);
+		xtrace(XFER, "[%2d]\n", cmd[6]);
+
+		stuffp->busy = 1;
+		/* Now really issue the request command */
+		outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd);
+
+    }
+#ifdef AK2
+	if ( stuffp->int_err ) {
+		stuffp->valid = 0;
+		stuffp->int_err = 0;
+		return -1;
 	}
-	return -EIO;
+#endif /* AK2 */
+
+    stuffp->low_border = (stuffp->low_border += done) < stuffp->high_border
+	    ? stuffp->low_border : stuffp->high_border;
+
+    return done;
 }
 
-void cleanup_module(void)
+
+/*	Access to elements of the mcdx_drive_map members */
+
+static char* port(int *ip) { return (char*) ip[0]; }
+static int irq(int *ip) { return ip[1]; }
+
+/*	Misc number converters */
+
+static unsigned int bcd2uint(unsigned char c)
+{ return (c >> 4) * 10 + (c & 0x0f); }
+
+static unsigned int uint2bcd(unsigned int ival)
+{ return ((ival / 10) << 4) | (ival % 10); }
+
+static void log2msf(unsigned int l, struct cdrom_msf0* pmsf)
 {
-	int i;
+    l += CD_BLOCK_OFFSET;
+    pmsf->minute = uint2bcd(l / 4500), l %= 4500;
+    pmsf->second = uint2bcd(l / 75);
+    pmsf->frame = uint2bcd(l % 75);
+}
 
-	unregister_blkdev(MAJOR_NR, DEVICE_NAME);
+static unsigned int msf2log(const struct cdrom_msf0* pmsf)
+{
+    return bcd2uint(pmsf->frame)
+    + bcd2uint(pmsf->second) * 75
+    + bcd2uint(pmsf->minute) * 4500
+    - CD_BLOCK_OFFSET;
+}
+	
+int mcdx_readtoc(struct s_drive_stuff* stuffp)
+/*  Read the toc entries from the CD,
+ *  Return: -1 on failure, else 0 */
+{
 
-	for (i = 0; i < MCDX_NDRIVES; i++) {
-		struct s_drive_stuff *stuffp;
-		stuffp = mcdx_stuffp[i];
-		if (!stuffp)
-			continue;
-		release_region(stuffp->base, MCDX_IO_SIZE);
-		free_irq(stuffp->irq, NULL);
-		release_toc(stuffp);
-		mcdx_stuffp[i] = NULL;
-		kfree(stuffp);
+	if (stuffp->toc) {
+		xtrace(READTOC, "ioctl() toc already read\n");
+		return 0;
+	}
+
+	xtrace(READTOC, "ioctl() readtoc for %d tracks\n",
+			stuffp->di.n_last - stuffp->di.n_first + 1);
+
+    if (-1 == mcdx_hold(stuffp, 1)) return -1;
+
+	xtrace(READTOC, "ioctl() tocmode\n");
+	if (-1 == mcdx_setdrivemode(stuffp, TOC, 1)) return -EIO;
+
+	/* all seems to be ok so far ... malloc */
+	{
+		int size;
+		size = sizeof(struct s_subqcode) * (stuffp->di.n_last - stuffp->di.n_first + 2);
+
+		xtrace(MALLOC, "ioctl() malloc %d bytes\n", size);
+		stuffp->toc = kmalloc(size, GFP_KERNEL);
+		if (!stuffp->toc) {
+			xwarn("Cannot malloc %d bytes for toc\n", size);
+			mcdx_setdrivemode(stuffp, DATA, 1);
+			return -EIO;
+		}
 	}
-	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+
+	/* now read actually the index */
+	{
+		int trk;
+		int retries;
+
+		for (trk = 0; 
+				trk < (stuffp->di.n_last - stuffp->di.n_first + 1); 
+				trk++)
+			stuffp->toc[trk].index = 0;
+
+		for (retries = 300; retries; retries--) { /* why 300? */
+			struct s_subqcode q;
+			unsigned int idx;
+		
+			if (-1 == mcdx_requestsubqcode(stuffp, &q, 1)) {
+				mcdx_setdrivemode(stuffp, DATA, 1);
+				return -EIO;
+			}
+
+			idx = bcd2uint(q.index);
+
+			if ((idx > 0) 
+					&& (idx <= stuffp->di.n_last) 
+					&& (q.tno == 0)
+					&& (stuffp->toc[idx - stuffp->di.n_first].index == 0)) {
+				stuffp->toc[idx - stuffp->di.n_first] = q;
+				xtrace(READTOC, "ioctl() toc idx %d (trk %d)\n", idx, trk);
+				trk--;
+			}
+			if (trk == 0) break;
+		}
+		memset(&stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1], 
+				0, sizeof(stuffp->toc[0]));
+		stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1].dt
+				= stuffp->di.msf_leadout;
+	}
+
+	/* unset toc mode */
+	xtrace(READTOC, "ioctl() undo toc mode\n");
+	if (-1 == mcdx_setdrivemode(stuffp, DATA, 2))
+		return -EIO;
+
+#if MCDX_DEBUG && READTOC
+	{ int trk;
+	for (trk = 0; 
+			trk < (stuffp->di.n_last - stuffp->di.n_first + 2); 
+			trk++)
+		xtrace(READTOC, "ioctl() %d readtoc %02x %02x %02x"
+				"  %02x:%02x.%02x  %02x:%02x.%02x\n",
+				trk + stuffp->di.n_first,
+				stuffp->toc[trk].control, stuffp->toc[trk].tno, stuffp->toc[trk].index,
+				stuffp->toc[trk].tt.minute, stuffp->toc[trk].tt.second, stuffp->toc[trk].tt.frame,
+				stuffp->toc[trk].dt.minute, stuffp->toc[trk].dt.second, stuffp->toc[trk].dt.frame);
+	}
+#endif
+
+	return 0;
 }
 
-#else				/* MODULE */
+static int
+mcdx_playmsf(struct s_drive_stuff* stuffp, const struct cdrom_msf* msf)
+{
+    unsigned char cmd[7] = {
+        0, 0, 0, 0, 0, 0, 0
+    };
 
-/*
- * This function is used by the kernel in init/main.c to provide semantics
- * for the corresponding kernel options. It's unused otherwise.
- */
-void mcdx_setup(char *str, int *pi)
+	if (!stuffp->readcmd) {
+		xinfo("Can't play from missing disk.\n");
+		return -1;
+	}
+
+    cmd[0] = stuffp->playcmd;
+    
+    cmd[1] = msf->cdmsf_min0;
+    cmd[2] = msf->cdmsf_sec0;
+    cmd[3] = msf->cdmsf_frame0;
+    cmd[4] = msf->cdmsf_min1;
+    cmd[5] = msf->cdmsf_sec1;
+    cmd[6] = msf->cdmsf_frame1;
+
+    xtrace(PLAYMSF, "ioctl(): play %x "
+            "%02x:%02x:%02x -- %02x:%02x:%02x\n",
+            cmd[0], cmd[1], cmd[2], cmd[3],
+            cmd[4], cmd[5], cmd[6]); 
+
+    outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd);
+
+    if (-1 == mcdx_getval(stuffp, 3 * HZ, 0, NULL)) {
+        xwarn("playmsf() timeout\n"); 
+        return -1;
+    }
+
+    stuffp->audiostatus = CDROM_AUDIO_PLAY;
+    return 0;
+}
+
+static int 
+mcdx_playtrk(struct s_drive_stuff* stuffp, const struct cdrom_ti* ti)
+{
+    struct s_subqcode* p;
+    struct cdrom_msf msf;
+
+    if (-1 == mcdx_readtoc(stuffp)) return -1;
+
+    if (ti) p = &stuffp->toc[ti->cdti_trk0 - stuffp->di.n_first];
+    else p = &stuffp->start;
+
+    msf.cdmsf_min0 = p->dt.minute;
+    msf.cdmsf_sec0 = p->dt.second;
+    msf.cdmsf_frame0 = p->dt.frame;
+
+    if (ti) {
+        p = &stuffp->toc[ti->cdti_trk1 - stuffp->di.n_first + 1];
+        stuffp->stop = *p;
+    } else p = &stuffp->stop;
+
+    msf.cdmsf_min1 = p->dt.minute;
+    msf.cdmsf_sec1 = p->dt.second;
+    msf.cdmsf_frame1 = p->dt.frame;
+
+    return mcdx_playmsf(stuffp, &msf);
+}
+
+
+/* Drive functions ************************************************/
+
+static int 
+mcdx_closedoor(struct s_drive_stuff *stuffp, int tries)
 {
-	if (pi[0] > 0)
-		mcdx_drive_map[0][0] = pi[1];
-	if (pi[0] > 1)
-		mcdx_drive_map[0][1] = pi[2];
+	if (stuffp->present & DOOR)
+		return mcdx_talk(stuffp, "\xf8", 1, NULL, 1, 5 * HZ, tries);
+	else
+		return 0;
+}
+
+static int 
+mcdx_stop(struct s_drive_stuff *stuffp, int tries)
+{ return mcdx_talk(stuffp, "\xf0", 1, NULL, 1, 2 * HZ, tries); }
+
+static int
+mcdx_hold(struct s_drive_stuff *stuffp, int tries)
+{ return mcdx_talk(stuffp, "\x70", 1, NULL, 1, 2 * HZ, tries); }
+
+static int
+mcdx_eject(struct s_drive_stuff *stuffp, int tries)
+{
+	if (stuffp->present & DOOR) {
+        stuffp->ejected = jiffies;
+		return mcdx_talk(stuffp, "\xf6", 1, NULL, 1, 5 * HZ, tries);
+    } else return 0;
+}
+
+static int
+mcdx_requestsubqcode(struct s_drive_stuff *stuffp, 
+        struct s_subqcode *sub, 
+        int tries)
+{
+	char buf[11];
+	int ans;
+
+	if (-1 == (ans = mcdx_talk(
+            stuffp, "\x20", 1, buf, sizeof(buf),
+            2 * HZ, tries))) 
+        return -1;
+	sub->control = buf[1];
+	sub->tno = buf[2];
+	sub->index = buf[3];
+	sub->tt.minute = buf[4];
+	sub->tt.second = buf[5];
+	sub->tt.frame = buf[6];
+	sub->dt.minute = buf[8];
+	sub->dt.second = buf[9];
+	sub->dt.frame = buf[10];
+
+	return ans;
+}
+
+static int
+mcdx_requestmultidiskinfo(struct s_drive_stuff *stuffp, struct s_multi *multi, int tries)
+{
+	char buf[5];
+	int ans;
+
+    if (stuffp->present & MULTI) {
+		ans = mcdx_talk(stuffp, "\x11", 1, buf, sizeof(buf), 2 * HZ, tries);
+		multi->multi = buf[1];
+        multi->msf_last.minute = buf[2];
+        multi->msf_last.second = buf[3];
+        multi->msf_last.frame = buf[4];
+        return ans;
+    } else {
+        multi->multi = 0;
+        return 0;
+    }
+}
+
+static int 
+mcdx_requesttocdata(struct s_drive_stuff *stuffp, struct s_diskinfo *info, int tries)
+{
+	char buf[9];
+	int ans;
+	ans = mcdx_talk(stuffp, "\x10", 1, buf, sizeof(buf), 2 * HZ, tries);
+	if (ans == -1) {
+		info->n_first = 0;
+		info->n_last = 0;
+	} else {
+		info->n_first = bcd2uint(buf[1]);
+		info->n_last = bcd2uint(buf[2]);
+		info->msf_leadout.minute = buf[3];
+		info->msf_leadout.second = buf[4];
+		info->msf_leadout.frame = buf[5];
+		info->msf_first.minute = buf[6];
+		info->msf_first.second = buf[7];
+		info->msf_first.frame = buf[8];
+	}
+	return ans;
+}
+
+static int
+mcdx_setdrivemode(struct s_drive_stuff *stuffp, enum drivemodes mode, int tries)
+{
+	char cmd[2];
+	int ans;
+
+	xtrace(HW, "setdrivemode() %d\n", mode);
+
+	if (-1 == (ans = mcdx_talk(stuffp, "\xc2", 1, cmd, sizeof(cmd), 5 * HZ, tries)))
+		return -1;
+
+	switch (mode) {
+	  case TOC: cmd[1] |= 0x04; break;
+	  case DATA: cmd[1] &= ~0x04; break;
+	  case RAW: cmd[1] |= 0x40; break;
+	  case COOKED: cmd[1] &= ~0x40; break;
+	  default: break;
+	}
+	cmd[0] = 0x50;
+	return mcdx_talk(stuffp, cmd, 2, NULL, 1, 5 * HZ, tries);
+}
+
+static int
+mcdx_setdatamode(struct s_drive_stuff *stuffp, enum datamodes mode, int tries)
+{
+	unsigned char cmd[2] = { 0xa0 };
+	xtrace(HW, "setdatamode() %d\n", mode);
+	switch (mode) {
+	  case MODE0: cmd[1] = 0x00; break;
+	  case MODE1: cmd[1] = 0x01; break;
+	  case MODE2: cmd[1] = 0x02; break;
+	  default: return -EINVAL;
+	}
+	return mcdx_talk(stuffp, cmd, 2, NULL, 1, 5 * HZ, tries);
+}
+
+static int
+mcdx_config(struct s_drive_stuff *stuffp, int tries)
+{
+	char cmd[4];
+
+	xtrace(HW, "config()\n");
+
+	cmd[0] = 0x90;
+
+	cmd[1] = 0x10;		/* irq enable */
+	cmd[2] = 0x05;		/* pre, err irq enable */
+
+	if (-1 == mcdx_talk(stuffp, cmd, 3, NULL, 1, 1 * HZ, tries))
+		return -1;
+
+	cmd[1] = 0x02;		/* dma select */
+	cmd[2] = 0x00;		/* no dma */
+
+	return mcdx_talk(stuffp, cmd, 3, NULL, 1, 1 * HZ, tries);
+}
+
+static int
+mcdx_requestversion(struct s_drive_stuff *stuffp, struct s_version *ver, int tries)
+{
+	char buf[3];
+	int ans;
+
+	if (-1 == (ans = mcdx_talk(stuffp, "\xdc", 
+			1, buf, sizeof(buf), 2 * HZ, tries)))
+		return ans;
+
+	ver->code = buf[1];
+	ver->ver = buf[2];
+
+	return ans;
 }
-#endif				/* MODULE */
+
+static int
+mcdx_reset(struct s_drive_stuff *stuffp, enum resetmodes mode, int tries)
+{ 
+	if (mode == HARD) {
+		outb(0, (unsigned int) stuffp->wreg_chn);		/* no dma, no irq -> hardware */
+		outb(0, (unsigned int) stuffp->wreg_reset);		/* hw reset */
+		return 0;
+	} else return mcdx_talk(stuffp, "\x60", 1, NULL, 1, 5 * HZ, tries);
+}
+
+static int
+mcdx_lockdoor(struct s_drive_stuff *stuffp, int lock, int tries)
+{
+	char cmd[2] = { 0xfe };
+    if (stuffp->present & DOOR) {
+        cmd[1] = lock ? 0x01 : 0x00;
+        return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 1, 5 * HZ, tries);
+    } else return 0;
+}
+
+static int
+mcdx_getstatus(struct s_drive_stuff *stuffp, int tries)
+{ return mcdx_talk(stuffp, "\x40", 1, NULL, 1, 5 * HZ, tries); }
+
+static int
+mcdx_getval(struct s_drive_stuff *stuffp, int to, int delay, char* buf)
+{
+    unsigned long timeout = to + jiffies;
+    char c;
+
+    if (!buf) buf = &c;
+
+    while (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_STEN) {
+        if (jiffies > timeout) return -1;
+        mcdx_delay(stuffp, delay);
+    }
+
+    *buf = (unsigned char) inb((unsigned int) stuffp->rreg_data) & 0xff;
+
+    return 0;
+}
+
+static int
+mcdx_setattentuator(
+        struct s_drive_stuff* stuffp, 
+        struct cdrom_volctrl* vol, 
+        int tries)
+{
+    char cmd[5];
+    cmd[0] = 0xae;
+    cmd[1] = vol->channel0;
+    cmd[2] = 0;
+    cmd[3] = vol->channel1;
+    cmd[4] = 0;
+
+    return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 5, 200, tries);
+}
+
+/* ex:set ts=4 sw=4 ai si: */
diff -u --recursive --new-file pre2.0.12/linux/drivers/cdrom/sbpcd.c linux/drivers/cdrom/sbpcd.c
--- pre2.0.12/linux/drivers/cdrom/sbpcd.c	Mon May 13 23:02:48 1996
+++ linux/drivers/cdrom/sbpcd.c	Wed Jun  5 09:01:32 1996
@@ -13,7 +13,7 @@
  *             labelled E2550UA or MK4015 or 2800F).
  */
 
-#define VERSION "v4.3 Eberhard Moenkeberg <emoenke@gwdg.de>"
+#define VERSION "v4.4 Eberhard Moenkeberg <emoenke@gwdg.de>"
 
 /*   Copyright (C) 1993, 1994, 1995  Eberhard Moenkeberg <emoenke@gwdg.de>
  *
@@ -278,6 +278,11 @@
  *       reading the ToC; still trying to solve it.
  *       Removed some redundant verify_area calls (yes, Heiko Eissfeldt
  *       is visiting all the Linux CDROM drivers ;-).
+ *       
+ *  4.4  Adapted one idea from tiensivu@pilot.msu.edu's "stripping-down"
+ *       experiments: "KLOGD_PAUSE".
+ *       Inhibited "play audio" attempts with data CDs. Provisions for a
+ *       "data-safe" handling of "mixed" (data plus audio) Cds.
  *
  *
  *  TODO
@@ -689,6 +694,9 @@
 	u_char TocEnt_number;
 	u_char TocEnt_format; /* em */
 	u_int TocEnt_address;
+#if SAFE_MIXED
+	char has_data;
+#endif SAFE_MIXED
 	u_char ored_ctl_adr; /* to detect if CDROM contains data tracks */
 	
 	struct {
@@ -751,7 +759,9 @@
 	vsprintf(&buf[18], fmt, args);
 	va_end(args);
 	printk(buf);
-	sbp_sleep(55); /* else messages get lost */
+#if KLOGD_PAUSE
+	sbp_sleep(KLOGD_PAUSE); /* else messages get lost */
+#endif KLOGD_PAUSE
 	return;
 }
 /*==========================================================================*/
@@ -1966,7 +1976,7 @@
 		else if (fam2_drive)
 		{
 			drvcmd[0]=CMD2_PLAY_MSF;
-			flags_cmd_out = f_putcmd | f_ResponseStatus;
+			flags_cmd_out = f_putcmd | f_ResponseStatus | f_obey_p_check;
 		}
 		else if (famT_drive)
 		{
@@ -3916,6 +3926,9 @@
 		
 	case CDROMPLAYMSF:
 		msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n");
+#if SAFE_MIXED
+		if (D_S[d].has_data>1) return (-EBUSY);
+#endif SAFE_MIXED
 		if (D_S[d].audio_state==audio_playing)
 		{
 			i=cc_Pause_Resume(1);
@@ -3938,15 +3951,21 @@
 		msg(DBG_IOX,"ioctl: CDROMPLAYMSF %08X %08X\n",
 		    D_S[d].pos_audio_start,D_S[d].pos_audio_end);
 		i=cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end);
-		msg(DBG_IOC,"ioctl: cc_PlayAudio returns %d\n",i);
-#if 0
-		if (i<0) return (-EIO);
-#endif 0
+		if (i<0)
+		{
+			msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i);
+			DriveReset();
+			D_S[d].audio_state=0;
+			return (-EIO);
+		}
 		D_S[d].audio_state=audio_playing;
 		return (0);
 		
 	case CDROMPLAYTRKIND: /* Play a track.  This currently ignores index. */
 		msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n");
+#if SAFE_MIXED
+		if (D_S[d].has_data>1) return (-EBUSY);
+#endif SAFE_MIXED
 		if (D_S[d].audio_state==audio_playing)
 		{
 			msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n");
@@ -3969,9 +3988,13 @@
 		D_S[d].pos_audio_start=D_S[d].TocBuffer[ti.cdti_trk0].address;
 		D_S[d].pos_audio_end=D_S[d].TocBuffer[ti.cdti_trk1+1].address;
 		i=cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end);
-#if 0
-		if (i<0) return (-EIO);
-#endif 0
+		if (i<0)
+		{
+			msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i);
+			DriveReset();
+			D_S[d].audio_state=0;
+			return (-EIO);
+		}
 		D_S[d].audio_state=audio_playing;
 		return (0);
 		
@@ -4015,6 +4038,9 @@
 		
 	case CDROMSTOP:      /* Spin down the drive */
 		msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n");
+#if SAFE_MIXED
+		if (D_S[d].has_data>1) return (-EBUSY);
+#endif SAFE_MIXED
 		i=cc_Pause_Resume(1);
 		D_S[d].audio_state=0;
 		return (i);
@@ -4119,6 +4145,9 @@
 		
 	case CDROMREADMODE1:
 		msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n");
+#if SAFE_MIXED
+		if (D_S[d].has_data>1) return (-EBUSY);
+#endif SAFE_MIXED
 		cc_ModeSelect(CD_FRAMESIZE);
 		cc_ModeSense();
 		D_S[d].mode=READ_M1;
@@ -4126,6 +4155,9 @@
 		
 	case CDROMREADMODE2: /* not usable at the moment */
 		msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n");
+#if SAFE_MIXED
+		if (D_S[d].has_data>1) return (-EBUSY);
+#endif SAFE_MIXED
 		cc_ModeSelect(CD_FRAMESIZE_RAW1);
 		cc_ModeSense();
 		D_S[d].mode=READ_M2;
@@ -4165,6 +4197,9 @@
 		if (famL_drive) return (-EINVAL);
 		if (famV_drive) return (-EINVAL);
 		if (famT_drive) return (-EINVAL);
+#if SAFE_MIXED
+		if (D_S[d].has_data>1) return (-EBUSY);
+#endif SAFE_MIXED
 		if (D_S[d].aud_buf==NULL) return (-EINVAL);
 		i=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_read_audio));
 		if (i) return (i);
@@ -4488,6 +4523,9 @@
 		sbp_sleep(0);
 		if (sbp_data() != 0)
 		{
+#if SAFE_MIXED
+			D_S[d].has_data=2; /* is really a data disk */
+#endif SAFE_MIXED
 			end_request(1);
 			goto request_loop;
 		}
@@ -4959,7 +4997,15 @@
 		i=DiskInfo();
 		if (famT_drive)	msg(DBG_TEA,"sbpcd_open: after i=DiskInfo();.\n");
 		if ((D_S[d].ored_ctl_adr&0x40)==0)
+		{		
 			msg(DBG_INF,"CD contains no data tracks.\n");
+#if SAFE_MIXED
+			D_S[d].has_data=0;
+#endif SAFE_MIXED
+		}
+#if SAFE_MIXED
+		else if (D_S[d].has_data<1) D_S[d].has_data=1;
+#endif SAFE_MIXED
 	}
 	if (!st_spinning) cc_SpinUp();
 	return (0);
@@ -4979,11 +5025,6 @@
 		return;
 	}
 	switch_drive(i);
-	
-	D_S[d].sbp_first_frame=D_S[d].sbp_last_frame=-1;
-	sync_dev(ip->i_rdev);                   /* nonsense if read only device? */
-	invalidate_buffers(ip->i_rdev);
-	
 	/*
 	 * try to keep an "open" counter here and unlock the door if 1->0.
 	 */
@@ -4994,11 +5035,17 @@
 	{
 		if (--D_S[d].open_count<=0) 
 		{
+			D_S[d].sbp_first_frame=D_S[d].sbp_last_frame=-1;
+			sync_dev(ip->i_rdev); /* nonsense if read only device? */
+			invalidate_buffers(ip->i_rdev);
 			i=UnLockDoor();
 			if (D_S[d].audio_state!=audio_playing)
 				if (D_S[d].f_eject) cc_SpinDown();
 			D_S[d].diskstate_flags &= ~cd_size_bit;
 			D_S[d].open_count=0; 
+#if SAFE_MIXED
+			D_S[d].has_data=0;
+#endif SAFE_MIXED
 		}
 	}
 }
@@ -5327,6 +5374,9 @@
 	{
 		if (D_S[j].drv_id==-1) continue;
 		switch_drive(j);
+#if SAFE_MIXED
+		D_S[j].has_data=0;
+#endif SAFE_MIXED
 		/*
 		 * allocate memory for the frame buffers
 		 */
diff -u --recursive --new-file pre2.0.12/linux/drivers/char/ChangeLog linux/drivers/char/ChangeLog
--- pre2.0.12/linux/drivers/char/ChangeLog	Sat May 11 10:42:05 1996
+++ linux/drivers/char/ChangeLog	Thu Jun  6 13:42:15 1996
@@ -1,3 +1,52 @@
+Wed Jun  5 18:52:04 1996  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* tty_io.c (do_tty_hangup): 
+	* pty.c (pty_close): When closing a pty, make sure packet mode is
+	 	cleared.
+
+Sun May 26 09:33:52 1996  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* vesa_blank.c (set_vesa_blanking): Add missing verify_area() call.
+
+	* selection.c (set_selection): Add missing verify_area() call.
+
+	* tty_io.c (tty_ioctl): Add missing verify_area() calls.
+
+	* serial.c (rs_ioctl): Add missing verify_area() calls.
+		(rs_init): Allow initialization of serial driver
+		configuration from a module.
+
+	* random.c (extract_entropy): Add missing verify_area call.
+		Don't limit number of characters returned to
+		32,768. Extract entropy is now no longer a inlined
+		function.
+
+		(random_read): Check return value in case extract_entropy
+		returns an error.
+
+		(secure_tcp_sequence_number): New function which returns a
+		secure TCP sequence number.  This is needed to prevent some
+		nasty TCP hijacking attacks.
+	
+		(init_std_data): Initialize using gettimeofday() instead of
+		struct timveal xtime.
+
+		(fast_add_entropy_word, add_entropy_word): Rename the
+		inline function add_entropy_word() to
+		fast_add_entropy_word().  Make add_entropy_word() be the
+		non-inlined function which is used in non-timing critical
+		places, in order to save space.
+
+		(initialize_benchmark, begin_benchmark, end_benchmark): New
+		functions defined when RANDOM_BENCHMARK is defined.  They
+		allow us to benchmark the speed of the
+		add_timer_randomness() call.
+
+		(int_ln, rotate_left): Add two new inline functions with
+		i386 optimized asm instructions.  This speeds up the
+		critical add_entropy_word() and add_timer_randomness()
+		functions, which are called from interrupt handlers.
+
 Tue May  7 22:51:11 1996    <tytso@rsts-11.mit.edu>
 
 	* random.c (add_timer_randomness): Limit the amount randomness
@@ -14,7 +63,6 @@
 		ioctl values used by the /dev/random driver.  Allow the
 		old ioctl values to be used for backwards compatibility
 		(for a limited amount of time).
-
 
 Wed Apr 24 14:02:04 1996  Theodore Ts'o  <tytso@rsts-11.mit.edu>
 
diff -u --recursive --new-file pre2.0.12/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- pre2.0.12/linux/drivers/char/Config.in	Sat Jun  1 20:11:30 1996
+++ linux/drivers/char/Config.in	Wed Jun  5 08:45:21 1996
@@ -16,7 +16,7 @@
 tristate 'Parallel printer support' CONFIG_PRINTER
 
 
-bool 'Bus Mouse Support' CONFIG_MOUSE
+bool 'Mouse Support (not serial mice)' CONFIG_MOUSE
 if [ "$CONFIG_MOUSE" = "y" ]; then
 	tristate 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE
 	tristate 'Logitech busmouse support' CONFIG_BUSMOUSE
diff -u --recursive --new-file pre2.0.12/linux/drivers/char/baycom.c linux/drivers/char/baycom.c
--- pre2.0.12/linux/drivers/char/baycom.c	Fri May 17 15:32:14 1996
+++ linux/drivers/char/baycom.c	Thu Jun  6 14:23:55 1996
@@ -66,6 +66,11 @@
  *                  Various resource allocation cleanups
  *   0.2  12.05.96  Changed major to allocated 51. Integrated into kernel
  *                  source tree
+ *   0.3  04.06.96  Major bug fixed (forgot to wake up after write) which
+ *                  interestingly manifested only with kernel ax25
+ *                  (the slip line discipline)
+ *                  introduced bottom half and tq_baycom
+ *                  HDLC processing now done with interrupts on
  */
 
 /*****************************************************************************/
@@ -89,7 +94,8 @@
 #include <linux/ioport.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
-
+#include <linux/interrupt.h>
+#include <linux/tqueue.h>
 #include <linux/baycom.h>
 
 /* --------------------------------------------------------------------- */
@@ -106,13 +112,12 @@
  * circuitry is usually slow.
  */
 
-#define BUFLEN_RX 16384
-#define BUFLEN_TX 16384
+#define BUFLEN_RX 8192
+#define BUFLEN_TX 8192
 
 #define NR_PORTS 4
 
 #define KISS_VERBOSE
-#undef HDLC_LOOPBACK
 
 #define BAYCOM_MAGIC 0x3105bac0
 
@@ -184,10 +189,11 @@
 
 struct hdlc_state_rx {
 	int rx_state;	/* 0 = sync hunt, != 0 receiving */
-	unsigned char lastbit;
 	unsigned int bitstream;
-	unsigned int assembly;
-	
+	unsigned int bitbuf;
+	int numbits;
+	unsigned int shreg1, shreg2;
+
 	int len;
 	unsigned char *bp;
 	unsigned char buffer[BAYCOM_MAXFLEN+2];	   /* make room for CRC */
@@ -205,6 +211,10 @@
 	unsigned int current_byte;
 	unsigned char ptt;
 
+	unsigned int bitbuf;
+	int numbits;
+	unsigned int shreg1, shreg2;
+
 	int len;
 	unsigned char *bp;
 	unsigned char buffer[BAYCOM_MAXFLEN+2];		/* make room for CRC */
@@ -226,12 +236,13 @@
 	unsigned int dcd_shreg;
 	unsigned long descram;
 	unsigned long scram;
-	unsigned int tx_bits;
+	unsigned char last_rxbit;
 };
 
 struct modem_state {
 	unsigned char dcd;
 	short arb_divider;
+	unsigned char flags;
 	struct modem_state_ser12 ser12;
 	struct modem_state_par96 par96;
 };
@@ -288,6 +299,10 @@
 	int opened;
 	struct tty_struct *tty;
 
+#ifdef BAYCOM_USE_BH
+	struct tq_struct tq_receiver, tq_transmitter, tq_arbitrate;
+#endif /* BAYCOM_USE_BH */
+
 	struct packet_buffer rx_buf;
 	struct packet_buffer tx_buf;
 
@@ -316,6 +331,10 @@
 
 struct baycom_state baycom_state[NR_PORTS];
 
+#ifdef BAYCOM_USE_BH
+DECLARE_TASK_QUEUE(tq_baycom);
+#endif /* BAYCOM_USE_BH */
+
 /* --------------------------------------------------------------------- */
 
 /*
@@ -520,16 +539,29 @@
 #ifdef BAYCOM_DEBUG
 static inline void add_bitbuffer(struct bit_buffer * buf, unsigned int bit)
 {
+	unsigned char new;
+
 	if (!buf) return;
-	buf->shreg <<= 1;
+	new = buf->shreg & 1;
+	buf->shreg >>= 1;
 	if (bit)
-		buf->shreg |= 1;
-	if (buf->shreg & 0x100) {
+		buf->shreg |= 0x80;
+	if (new) {
 		buf->buffer[buf->wr] = buf->shreg;
 		buf->wr = (buf->wr+1) % sizeof(buf->buffer);
-		buf->shreg = 1;
+		buf->shreg = 0x80;
 	}
 }
+
+static inline void add_bitbuffer_word(struct bit_buffer * buf, 
+				      unsigned int bits)
+{
+	buf->buffer[buf->wr] = bits & 0xff;
+	buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+	buf->buffer[buf->wr] = (bits >> 8) & 0xff;
+	buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+
+}
 #endif /* BAYCOM_DEBUG */
 
 /* ---------------------------------------------------------------------- */
@@ -553,67 +585,98 @@
  * one bit per call
  */
 
-static void hdlc_rx_bit(struct baycom_state *bc, unsigned int bit)
+static inline int hdlc_rx_add_bytes(struct baycom_state *bc, 
+				    unsigned int bits, int num)
 {
+	int added = 0;
+	while (bc->hdlc_rx.rx_state && num >= 8) {
+		if (bc->hdlc_rx.len >= sizeof(bc->hdlc_rx.buffer)) {
+			bc->hdlc_rx.rx_state = 0;
+			return 0;
+		}
+		*bc->hdlc_rx.bp++ = bits >> (32-num);
+		bc->hdlc_rx.len++;
+		num -= 8;
+		added += 8;
+	}
+	return added;
+}
+
+static inline void hdlc_rx_flag(struct baycom_state *bc)
+{
+	if (bc->hdlc_rx.len < 4) 
+		return;
+	if (!check_crc_ccitt(bc->hdlc_rx.buffer, bc->hdlc_rx.len)) 
+		return;
+       	bc->stat.rx_packets++;
+	if (!store_kiss_packet(&bc->rx_buf,
+			       bc->hdlc_rx.buffer,
+			       bc->hdlc_rx.len-2))
+		bc->stat.rx_bufferoverrun++;
+}
+
+static void hdlc_rx_word(struct baycom_state *bc, unsigned int word)
+{
+	int i;
+	unsigned int mask1, mask2, mask3, mask4, mask5, mask6;
+	
 	if (!bc) return;
 
-	bc->hdlc_rx.bitstream <<= 1;
-	if (bit)
-		bc->hdlc_rx.bitstream |= 1;
+	word &= 0xffff;
 #ifdef BAYCOM_DEBUG
-	add_bitbuffer(&bc->bitbuf_hdlc, bc->hdlc_rx.bitstream & 1);
+	add_bitbuffer_word(&bc->bitbuf_hdlc, word);
 #endif /* BAYCOM_DEBUG */
-	if(bc->hdlc_rx.rx_state) {
-		if ((bc->hdlc_rx.bitstream & 0x3f) != 0x3e) {
-			/* not a stuffed bit */
-			if (bc->hdlc_rx.bitstream & 1)
-				bc->hdlc_rx.assembly |= 0x100;
-			if (bc->hdlc_rx.assembly & 1) {
-				/* store byte */
-				if (bc->hdlc_rx.len >= sizeof(bc->hdlc_rx.buffer)) {
-					bc->hdlc_rx.rx_state = 0;
-				} else {
-					*bc->hdlc_rx.bp++ = bc->hdlc_rx.assembly>>1;
-					bc->hdlc_rx.len++;
-					bc->hdlc_rx.assembly = 0x80;
-				}
-			} else {
-				bc->hdlc_rx.assembly >>= 1;
-			}
-		}
-		if ((bc->hdlc_rx.bitstream & 0x7f) == 0x7e) {
-			if (bc->hdlc_rx.len >= 4) {
-				if (check_crc_ccitt(bc->hdlc_rx.buffer,bc->hdlc_rx.len)) {
-					bc->stat.rx_packets++;
-					if (!store_kiss_packet(&bc->rx_buf,bc->hdlc_rx.buffer,bc->hdlc_rx.len-2))
-						bc->stat.rx_bufferoverrun++;
-				}
+       	bc->hdlc_rx.bitstream >>= 16;
+	bc->hdlc_rx.bitstream |= word << 16;
+	bc->hdlc_rx.bitbuf >>= 16;
+	bc->hdlc_rx.bitbuf |= word << 16;
+	bc->hdlc_rx.numbits += 16;
+	for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00,
+	    mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; 
+	    i >= 0; 
+	    i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, 
+	    mask5 <<= 1, mask6 = (mask6 << 1) | 1) {
+		if ((bc->hdlc_rx.bitstream & mask1) == mask1)
+			bc->hdlc_rx.rx_state = 0; /* abort received */
+		else if ((bc->hdlc_rx.bitstream & mask2) == mask3) {
+			/* flag received */
+			if (bc->hdlc_rx.rx_state) {
+				hdlc_rx_add_bytes(bc, bc->hdlc_rx.bitbuf << 
+						  (8 + i), bc->hdlc_rx.numbits
+						  - 8 - i);
+				hdlc_rx_flag(bc);
 			}
 			bc->hdlc_rx.len = 0;
 			bc->hdlc_rx.bp = bc->hdlc_rx.buffer;
-			bc->hdlc_rx.assembly = 0x80;
-		}
-		if ((bc->hdlc_rx.bitstream & 0x7f) == 0x7f)
-			bc->hdlc_rx.rx_state = 0;
-	} else {
-		if ((bc->hdlc_rx.bitstream & 0x7f) == 0x7e) {
-			bc->hdlc_rx.len = 0;
-			bc->hdlc_rx.bp = bc->hdlc_rx.buffer;
-			bc->hdlc_rx.assembly = 0x80;
 			bc->hdlc_rx.rx_state = 1;
+			bc->hdlc_rx.numbits = i;
+		} else if ((bc->hdlc_rx.bitstream & mask4) == mask5) {
+			/* stuffed bit */
+			bc->hdlc_rx.numbits--;
+			bc->hdlc_rx.bitbuf = (bc->hdlc_rx.bitbuf & (~mask6)) |
+				((bc->hdlc_rx.bitbuf & mask6) << 1);
 		}
 	}
+	bc->hdlc_rx.numbits -= hdlc_rx_add_bytes(bc, bc->hdlc_rx.bitbuf,
+						 bc->hdlc_rx.numbits);
 }
 
 /* ---------------------------------------------------------------------- */
 
-static unsigned char hdlc_tx_bit(struct baycom_state *bc)
+static unsigned int hdlc_tx_word(struct baycom_state *bc)
 {
-	unsigned char bit;
+	unsigned int mask1, mask2, mask3;
+	int i;
 
 	if (!bc || !bc->hdlc_tx.ptt)
 		return 0;
-	for(;;) {
+	for (;;) {
+		if (bc->hdlc_tx.numbits >= 16) {
+			unsigned int ret = bc->hdlc_tx.bitbuf & 0xffff;
+			bc->hdlc_tx.bitbuf >>= 16;
+			bc->hdlc_tx.numbits -= 16;
+			return ret;
+		}
 		switch (bc->hdlc_tx.tx_state) {
 		default:
 			bc->hdlc_tx.ptt = 0;
@@ -621,80 +684,68 @@
 			return 0;
 		case 0:
 		case 1:
-			if (bc->hdlc_tx.current_byte > 1) {
-				/*
-				 * return bit 
-				 */
-				bit = bc->hdlc_tx.current_byte & 1;
-				bc->hdlc_tx.current_byte >>= 1;
-				return bit;
-			}
-			/*
-			 * get new bit 
-			 */
 			if (bc->hdlc_tx.numflags) {
 				bc->hdlc_tx.numflags--;
-				bc->hdlc_tx.current_byte = 0x17e;
-			} else {
-				if (bc->hdlc_tx.tx_state == 1) {
-					bc->hdlc_tx.ptt = 0;
-					return 0;
-				}
-				get_packet(&bc->tx_buf, &bc->hdlc_tx.bp,
-					   &bc->hdlc_tx.len);
-				if (!bc->hdlc_tx.bp || !bc->hdlc_tx.len) {
-					bc->hdlc_tx.tx_state = 1;
-					bc->hdlc_tx.current_byte = 0;
-					bc->hdlc_tx.numflags = tenms_to_flags
-						(bc, bc->ch_params.tx_tail);
-				} else if (bc->hdlc_tx.len >= BAYCOM_MAXFLEN) {
-					bc->hdlc_tx.tx_state = 0;
-					bc->hdlc_tx.current_byte = 0;
-					bc->hdlc_tx.numflags = 1;
-					ack_packet(&bc->tx_buf);
-				} else {
-					memcpy(bc->hdlc_tx.buffer,
-					       bc->hdlc_tx.bp, 
-					       bc->hdlc_tx.len);
-					ack_packet(&bc->tx_buf);
-					bc->hdlc_tx.bp = bc->hdlc_tx.buffer;
-					append_crc_ccitt(bc->hdlc_tx.buffer,
-							 bc->hdlc_tx.len);
-					/* the appended CRC */
-					bc->hdlc_tx.len += 2; 
-					bc->hdlc_tx.tx_state = 2;
-					bc->hdlc_tx.current_byte = 0;
-					bc->hdlc_tx.bitstream = 0;
-					bc->stat.tx_packets++;
-				}
+				bc->hdlc_tx.bitbuf |= 
+					0x7e7e << bc->hdlc_tx.numbits;
+				bc->hdlc_tx.numbits += 16;
+				break;
 			}
-			break;
-		case 2:
-			if ((bc->hdlc_tx.bitstream & 0x1f) == 0x1f) {
-				/*
-				 * bit stuffing
-				 */
-				bc->hdlc_tx.bitstream <<= 1;
+			if (bc->hdlc_tx.tx_state == 1) {
+				bc->hdlc_tx.ptt = 0;
 				return 0;
 			}
-			if (bc->hdlc_tx.current_byte > 1) {
-				/*
-				 * return bit 
-				 */
-				bc->hdlc_tx.bitstream <<= 1;
-				bit = bc->hdlc_tx.current_byte & 1;
-				bc->hdlc_tx.bitstream |= bit;
-				bc->hdlc_tx.current_byte >>= 1;
-				return bit;
+			get_packet(&bc->tx_buf, &bc->hdlc_tx.bp,
+				   &bc->hdlc_tx.len);
+			if (!bc->hdlc_tx.bp || !bc->hdlc_tx.len) {
+				bc->hdlc_tx.tx_state = 1;
+				bc->hdlc_tx.numflags = tenms_to_flags
+					(bc, bc->ch_params.tx_tail);
+				break;
+			}
+			if (bc->hdlc_tx.len >= BAYCOM_MAXFLEN) {
+				bc->hdlc_tx.tx_state = 0;
+				bc->hdlc_tx.numflags = 1;
+				ack_packet(&bc->tx_buf);
+				break;
 			}
+			memcpy(bc->hdlc_tx.buffer, bc->hdlc_tx.bp, 
+			       bc->hdlc_tx.len);
+			ack_packet(&bc->tx_buf);
+			bc->hdlc_tx.bp = bc->hdlc_tx.buffer;
+			append_crc_ccitt(bc->hdlc_tx.buffer, bc->hdlc_tx.len);
+			/* the appended CRC */
+			bc->hdlc_tx.len += 2; 
+			bc->hdlc_tx.tx_state = 2;
+			bc->hdlc_tx.bitstream = 0;
+			bc->stat.tx_packets++;
+			break;
+		case 2:
 			if (!bc->hdlc_tx.len) {
 				bc->hdlc_tx.tx_state = 0;
-				bc->hdlc_tx.current_byte = 0;
 				bc->hdlc_tx.numflags = 1;
-			} else {
-				bc->hdlc_tx.len--;
-				bc->hdlc_tx.current_byte = 0x100 | 
-					(*bc->hdlc_tx.bp++);
+				break;
+			}
+			bc->hdlc_tx.len--;
+			bc->hdlc_tx.bitbuf |= *bc->hdlc_tx.bp <<
+				bc->hdlc_tx.numbits;
+			bc->hdlc_tx.bitstream >>= 8;
+			bc->hdlc_tx.bitstream |= (*bc->hdlc_tx.bp++) << 16;
+			mask1 = 0x1f000;
+			mask2 = 0x10000;
+			mask3 = 0xffffffff >> (31-bc->hdlc_tx.numbits);
+			bc->hdlc_tx.numbits += 8;
+			for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, 
+			    mask3 = (mask3 << 1) | 1) {
+				if ((bc->hdlc_tx.bitstream & mask1) != mask1) 
+					continue;
+				bc->hdlc_tx.bitstream &= ~mask2;
+				bc->hdlc_tx.bitbuf = 
+					(bc->hdlc_tx.bitbuf & mask3) |
+						((bc->hdlc_tx.bitbuf & 
+						 (~mask3)) << 1);
+				bc->hdlc_tx.numbits++;
+				mask3 = (mask3 << 1) | 1;
 			}
 			break;
 		}
@@ -720,7 +771,7 @@
 	
 	if (!bc || bc->hdlc_tx.ptt || bc->modem.dcd)
 		return;
-	get_packet(&bc->tx_buf,&bp,&len);
+	get_packet(&bc->tx_buf, &bp, &len);
 	if (!bp || !len)
 		return;
 	
@@ -845,18 +896,34 @@
 		 * since this may take quite long
 		 */
 		outb(0x0e | (bc->modem.ser12.tx_bit ? 1 : 0), MCR(bc->iobase));
-		if (bc->calibrate > 0) {
-			bc->modem.ser12.tx_bit = !bc->modem.ser12.tx_bit;
-			bc->calibrate--;
-			return;
-		}
+		if (bc->hdlc_tx.shreg1 <= 1) {
+			if (bc->calibrate > 0) {
+				bc->hdlc_tx.shreg1 = 0x10000;
+				bc->calibrate--;
+			} else {
+#ifdef BAYCOM_USE_BH
+				bc->hdlc_tx.shreg1 = bc->hdlc_tx.shreg2;
+				bc->hdlc_tx.shreg2 = 0;
+				queue_task_irq_off(&bc->tq_transmitter, 
+						   &tq_baycom);
+				mark_bh(BAYCOM_BH);
 #ifdef HDLC_LOOPBACK
-		hdlc_rx_bit(bc, bc->modem.ser12.tx_bit == 
-			    bc->modem.ser12.last_rxbit);
-		bc->modem.ser12.last_rxbit = bc->modem.ser12.tx_bit;
+				bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1;
+				queue_task_irq_off(&bc->tq_receiver, 
+						   &tq_baycom);
 #endif /* HDLC_LOOPBACK */
-		if (!hdlc_tx_bit(bc))
+#else /* BAYCOM_USE_BH */
+				bc->hdlc_tx.shreg1 = hdlc_tx_word(bc) 
+					| 0x10000;
+#ifdef HDLC_LOOPBACK
+				hdlc_rx_word(bc, bc->hdlc_tx.shreg1);
+#endif /* HDLC_LOOPBACK */
+#endif /* BAYCOM_USE_BH */
+			}	
+		}
+		if (!(bc->hdlc_tx.shreg1 & 1))
 			bc->modem.ser12.tx_bit = !bc->modem.ser12.tx_bit;
+		bc->hdlc_tx.shreg1 >>= 1;
 		return;
 	}
 	/*
@@ -928,8 +995,10 @@
 				ser12_set_divisor(bc, 4);
 				break;
 			}
-			hdlc_rx_bit(bc, bc->modem.ser12.last_sample == 
-			    bc->modem.ser12.last_rxbit);
+			bc->hdlc_rx.shreg1 >>= 1;
+			if (bc->modem.ser12.last_sample == 
+			    bc->modem.ser12.last_rxbit)
+				bc->hdlc_rx.shreg1 |= 0x10000;
 			bc->modem.ser12.last_rxbit = 
 				bc->modem.ser12.last_sample;
 		}
@@ -965,15 +1034,32 @@
 				ser12_set_divisor(bc, 6);
 				break;
 			}
-			hdlc_rx_bit(bc, bc->modem.ser12.last_sample == 
-			    bc->modem.ser12.last_rxbit);
+			bc->hdlc_rx.shreg1 >>= 1;
+			if (bc->modem.ser12.last_sample == 
+			    bc->modem.ser12.last_rxbit)
+				bc->hdlc_rx.shreg1 |= 0x10000;
 			bc->modem.ser12.last_rxbit = 
 				bc->modem.ser12.last_sample;
 		}
 		bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
 	}
+	if (bc->hdlc_rx.shreg1 & 1) {
+#ifdef BAYCOM_USE_BH
+		bc->hdlc_rx.shreg2 = (bc->hdlc_rx.shreg1 >> 1) | 0x10000;
+		queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
+		mark_bh(BAYCOM_BH);
+#else /* BAYCOM_USE_BH */
+		hdlc_rx_word(bc, bc->hdlc_rx.shreg1 >> 1);
+#endif /* BAYCOM_USE_BH */
+		bc->hdlc_rx.shreg1 = 0x10000;
+	}
 	if (--bc->modem.arb_divider <= 0) {
+#ifdef BAYCOM_USE_BH
+		queue_task_irq_off(&bc->tq_arbitrate, &tq_baycom);
+		mark_bh(BAYCOM_BH);
+#else /* BAYCOM_USE_BH */
 		tx_arbitrate(bc);
+#endif /* BAYCOM_USE_BH */
 		bc->modem.arb_divider = bc->ch_params.slottime * 
 			SER12_ARB_DIVIDER(bc);
 	}
@@ -1126,7 +1212,7 @@
 {
 	register struct baycom_state *bc = (struct baycom_state *)dev_id;
 	int i;
-	unsigned int data, mask, mask2;
+	unsigned int data, rawdata, mask, mask2;
 	
 	if (!bc || bc->magic != BAYCOM_MAGIC)
 		return;
@@ -1144,12 +1230,12 @@
 		 * transmitter, since this may take quite long
 		 * do the differential encoder and the scrambler on the fly
 		 */
-		data = bc->modem.par96.tx_bits;
-		for(i = 0; i < PAR96_BURSTBITS; i++, data <<= 1) {
+		data = bc->hdlc_tx.shreg1;
+		for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) {
 			unsigned char val = PAR97_POWER;
 			bc->modem.par96.scram = ((bc->modem.par96.scram << 1) |
 						 (bc->modem.par96.scram & 1));
-			if (!(data & 0x8000))
+			if (!(data & 1))
 				bc->modem.par96.scram ^= 1;
 			if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))
 				bc->modem.par96.scram ^= 
@@ -1160,26 +1246,31 @@
 			outb(val | PAR96_BURST, LPT_DATA(bc->iobase));
 		}
 		if (bc->calibrate > 0) {
-			bc->modem.par96.tx_bits = 0;
+			bc->hdlc_tx.shreg1 = 0x10000;
 			bc->calibrate--;
-			return;
-		}
+		} else {
+#ifdef BAYCOM_USE_BH
+			bc->hdlc_tx.shreg1 = bc->hdlc_tx.shreg2;
+			bc->hdlc_tx.shreg2 = 0;
+			queue_task_irq_off(&bc->tq_transmitter, &tq_baycom);
+			mark_bh(BAYCOM_BH);
 #ifdef HDLC_LOOPBACK
-		for(mask = 0x8000, i = 0; i < PAR96_BURSTBITS; i++, mask >>= 1)
-			hdlc_rx_bit(bc, bc->modem.par96.tx_bits & mask);
+			bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1;
+			queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
 #endif /* HDLC_LOOPBACK */
-		bc->modem.par96.tx_bits = 0;
-		for(i = 0; i < PAR96_BURSTBITS; i++) {
-			bc->modem.par96.tx_bits <<= 1;
-			if (hdlc_tx_bit(bc))
-				bc->modem.par96.tx_bits |= 1;
+#else /* BAYCOM_USE_BH */
+			bc->hdlc_tx.shreg1 = hdlc_tx_word(bc);
+#ifdef HDLC_LOOPBACK
+			hdlc_rx_word(bc, bc->hdlc_tx.shreg1);
+#endif /* HDLC_LOOPBACK */
+#endif /* BAYCOM_USE_BH */
 		}
 		return;
 	}
 	/*
 	 * do receiver; differential decode and descramble on the fly
 	 */
-	for(data = i = 0; i < PAR96_BURSTBITS; i++) {
+	for(rawdata = data = i = 0; i < PAR96_BURSTBITS; i++) {
 		unsigned int descx;
 		bc->modem.par96.descram = (bc->modem.par96.descram << 1);
 		if (inb(LPT_STATUS(bc->iobase)) & PAR96_RXBIT)
@@ -1190,19 +1281,31 @@
 		outb(PAR97_POWER | PAR96_PTT, LPT_DATA(bc->iobase));
 		descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^
 			  (descx >> PAR96_DESCRAM_TAPSH2));
-		data <<= 1;
-		data |= !(descx & 1);
+		if (descx & 1)
+			bc->modem.par96.last_rxbit = 
+				!bc->modem.par96.last_rxbit;
+		data >>= 1;
+		if (bc->modem.par96.last_rxbit)
+			data |= 0x8000;
+		rawdata <<= 1;
+		rawdata |= !(descx & 1);
 		outb(PAR97_POWER | PAR96_PTT | PAR96_BURST, 
 		     LPT_DATA(bc->iobase));
 	}
-	for(mask = 0x8000, i = 0; i < PAR96_BURSTBITS; i++, mask >>= 1)
-		hdlc_rx_bit(bc, data & mask);
+#ifdef BAYCOM_USE_BH
+	bc->hdlc_rx.shreg2 = bc->hdlc_rx.shreg1;
+	bc->hdlc_rx.shreg1 = data | 0x10000;
+	queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
+	mark_bh(BAYCOM_BH);
+#else /* BAYCOM_USE_BH */
+	hdlc_rx_word(bc, data);
+#endif /* BAYCOM_USE_BH */
 	/*
 	 * do DCD algorithm
 	 */
 	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
 		bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg << 16)
-			| data;
+			| rawdata;
 		/* search for flags and set the dcd counter appropriately */
 		for(mask = 0x7f8000, mask2 = 0x3f0000, i = 0; 
 		    i < PAR96_BURSTBITS; i++, mask >>= 1, mask2 >>= 1)
@@ -1223,7 +1326,12 @@
 		bc->modem.dcd = !!(inb(LPT_STATUS(bc->iobase)) & PAR96_DCD);
 	}
 	if (--bc->modem.arb_divider <= 0) {
+#ifdef BAYCOM_USE_BH
+		queue_task_irq_off(&bc->tq_arbitrate, &tq_baycom);
+		mark_bh(BAYCOM_BH);
+#else /* BAYCOM_USE_BH */
 		tx_arbitrate(bc);
+#endif /* BAYCOM_USE_BH */
 		bc->modem.arb_divider = bc->ch_params.slottime * 6;
 	}
 }
@@ -1320,6 +1428,58 @@
 
 /* --------------------------------------------------------------------- */
 /*
+ * ===================== Bottom half (soft interrupt) ====================
+ */
+
+#ifdef BAYCOM_USE_BH
+static void bh_receiver(void *private)
+{
+	struct baycom_state *bc = (struct baycom_state *)private;
+	unsigned int temp;
+
+	if (!bc || bc->magic != BAYCOM_MAGIC)
+		return;
+	if (!bc->hdlc_rx.shreg2)
+		return;
+	temp = bc->hdlc_rx.shreg2;
+	bc->hdlc_rx.shreg2 = 0;
+	hdlc_rx_word(bc, temp);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void bh_transmitter(void *private)
+{
+	struct baycom_state *bc = (struct baycom_state *)private;
+
+	if (!bc || bc->magic != BAYCOM_MAGIC)
+		return;
+	if (bc->hdlc_tx.shreg2)
+		return;
+	bc->hdlc_tx.shreg2 = hdlc_tx_word(bc) | 0x10000;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void bh_arbitrate(void *private)
+{
+	struct baycom_state *bc = (struct baycom_state *)private;
+
+	if (!bc || bc->magic != BAYCOM_MAGIC)
+		return;
+	tx_arbitrate(bc);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void baycom_bottom_half(void)
+{
+	run_task_queue(&tq_baycom);
+}
+#endif /* BAYCOM_USE_BH */
+
+/* --------------------------------------------------------------------- */
+/*
  * ===================== TTY interface routines ==========================
  */
 
@@ -1369,7 +1529,7 @@
 			break;
 		bc->ch_params.ppersist = bc->kiss_decode.pkt_buf[1];
 #ifdef KISS_VERBOSE
-		printk("KERN_INFO baycom: p-persistence = %u\n", 
+		printk(KERN_INFO "baycom: p-persistence = %u\n", 
 		       bc->ch_params.ppersist);
 #endif /* KISS_VERBOSE */
 		break;
@@ -1379,7 +1539,7 @@
 			break;
 		bc->ch_params.slottime = bc->kiss_decode.pkt_buf[1];
 #ifdef KISS_VERBOSE
-		printk("baycom: slottime = %ums\n", 
+		printk(KERN_INFO "baycom: slottime = %ums\n", 
 		       bc->ch_params.slottime * 10);
 #endif /* KISS_VERBOSE */
 		break;
@@ -1477,6 +1637,10 @@
 		for(c = count, bp = buf; c > 0; c--,bp++)
 			baycom_put_char(tty, *bp);
 	}
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+	wake_up_interruptible(&tty->write_wait);
 	return count;
 }
 
@@ -1513,10 +1677,10 @@
 		return 0;
 	if (baycom_paranoia_check(bc = tty->driver_data, "chars_in_buffer"))
 		return 0;
-		
-	cnt = bc->rx_buf.wr - bc->rx_buf.rd;
+
+	cnt = bc->tx_buf.wr - bc->tx_buf.rd;
 	if (cnt < 0)
-		cnt += bc->rx_buf.buflen;
+		cnt += bc->tx_buf.buflen;
 		
 	return cnt;
 }
@@ -1669,7 +1833,7 @@
 		
 	case BAYCOMCTL_CALIBRATE:
 		bc->calibrate = arg * ((bc->modem_type == BAYCOM_MODEM_PAR96) ?
-				       600 : 1200);
+				       600 : 75);
 		return 0;
 
 	case BAYCOMCTL_GETPARAMS:
@@ -1917,16 +2081,28 @@
 
 #ifdef BAYCOM_DEBUG
 	bc->bitbuf_channel.rd = bc->bitbuf_channel.wr = 0;
-	bc->bitbuf_channel.shreg = 1;
+	bc->bitbuf_channel.shreg = 0x80;
 
 	bc->bitbuf_hdlc.rd = bc->bitbuf_hdlc.wr = 0;
-	bc->bitbuf_hdlc.shreg = 1;
+	bc->bitbuf_hdlc.shreg = 0x80;
 #endif /* BAYCOM_DEBUG */
 
 	bc->kiss_decode.dec_state = bc->kiss_decode.escaped = 
 	bc->kiss_decode.wr = 0;
 
 	bc->ch_params = dflt_ch_params;
+
+#ifdef BAYCOM_USE_BH
+	bc->tq_receiver.next = bc->tq_transmitter.next =
+		bc->tq_arbitrate.next = NULL;
+	bc->tq_receiver.sync = bc->tq_transmitter.sync =
+		bc->tq_arbitrate.sync = 0;
+	bc->tq_receiver.data = bc->tq_transmitter.data =
+		bc->tq_arbitrate.data = bc;
+	bc->tq_receiver.routine = bh_receiver;
+	bc->tq_transmitter.routine = bh_transmitter;
+	bc->tq_arbitrate.routine = bh_arbitrate;
+#endif /* BAYCOM_USE_BH */
 }
 
 static void init_datastructs(void)
@@ -1963,6 +2139,12 @@
 	 */
 	init_datastructs();
 	/*
+	 * initialize bottom half handler
+ 	 */
+#ifdef BAYCOM_USE_BH
+	init_bh(BAYCOM_BH, baycom_bottom_half);
+#endif /* BAYCOM_USE_BH */
+	/*
 	 * register the driver as tty driver
 	 */
 	memset(&baycom_driver, 0, sizeof(struct tty_driver));
@@ -2080,7 +2262,7 @@
 	if (i)
 		return i;
 
-	printk(KERN_INFO "baycom: version 0.2; "
+	printk(KERN_INFO "baycom: version 0.3; "
 	       "(C) 1996 by Thomas Sailer HB9JNX, sailer@ife.ee.ethz.ch\n");
 
 	return 0;
@@ -2092,10 +2274,6 @@
 {
 	int i;
 
-#if 0
-	if (MOD_IN_USE)
-		printk(KERN_INFO "baycom: device busy, remove delayed\n");
-#endif
 	printk(KERN_INFO "baycom: cleanup_module called\n");
 
 	if (tty_unregister_driver(&baycom_driver))
diff -u --recursive --new-file pre2.0.12/linux/drivers/char/pty.c linux/drivers/char/pty.c
--- pre2.0.12/linux/drivers/char/pty.c	Tue May  7 16:22:25 1996
+++ linux/drivers/char/pty.c	Thu Jun  6 13:42:16 1996
@@ -74,8 +74,10 @@
 	}
 	wake_up_interruptible(&tty->read_wait);
 	wake_up_interruptible(&tty->write_wait);
+	tty->packet = 0;
 	if (!tty->link)
 		return;
+	tty->link->packet = 0;
 	wake_up_interruptible(&tty->link->read_wait);
 	wake_up_interruptible(&tty->link->write_wait);
 	set_bit(TTY_OTHER_CLOSED, &tty->link->flags);
diff -u --recursive --new-file pre2.0.12/linux/drivers/char/random.c linux/drivers/char/random.c
--- pre2.0.12/linux/drivers/char/random.c	Sun May 12 10:16:07 1996
+++ linux/drivers/char/random.c	Thu Jun  6 13:42:15 1996
@@ -1,7 +1,7 @@
 /*
  * random.c -- A strong random number generator
  *
- * Version 0.98, last modified 7-May-96
+ * Version 1.00, last modified 26-May-96
  * 
  * Copyright Theodore Ts'o, 1994, 1995, 1996.  All rights reserved.
  *
@@ -226,7 +226,6 @@
  * Eastlake, Steve Crocker, and Jeff Schiller.
  */
 
-#include <linux/sched.h>
 #include <linux/utsname.h>
 #include <linux/kernel.h>
 #include <linux/major.h>
@@ -242,6 +241,8 @@
 /*
  * Configuration information
  */
+#undef RANDOM_BENCHMARK
+#undef BENCHMARK_NOINT
 
 /*
  * The pool is stirred with a primitive polynomial of degree 128
@@ -288,6 +289,29 @@
 	__u32 *pool;
 };
 
+#ifdef RANDOM_BENCHMARK
+/* For benchmarking only */
+struct random_benchmark {
+	unsigned long long 	start_time;
+	int			times;		/* # of samples */
+	unsigned long		min;
+	unsigned long		max;
+	unsigned long		accum;		/* accumulator for average */
+	const char		*descr;
+	int			unit;
+	unsigned long		flags;
+};
+
+#define BENCHMARK_INTERVAL 500
+
+static void initialize_benchmark(struct random_benchmark *bench,
+				 const char *descr, int unit);
+static void begin_benchmark(struct random_benchmark *bench);
+static void end_benchmark(struct random_benchmark *bench);
+
+struct random_benchmark timer_benchmark;
+#endif
+
 /* There is one of these per entropy source */
 struct timer_rand_state {
 	unsigned long	last_time;
@@ -315,12 +339,73 @@
 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,
+static inline void fast_add_entropy_word(struct random_bucket *r,
+					 const __u32 input);
+
+static void add_entropy_word(struct random_bucket *r,
 				    const __u32 input);
 
 #ifndef MIN
 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
 #endif
+
+/*
+ * Unfortunately, while the GCC optimizer for the i386 understands how
+ * to opimize a static rotate left of x bits, it doesn't know how to
+ * deal with a variable rotate of x bits.  So we use a bit of asm magic.
+ */
+#if (!defined (__i386__))
+extern inline __u32 rotate_left(int i, __u32 word)
+{
+	__u32 nbits = 0;
+	
+	return (word << i) | (word >> (32 - i));
+	
+}
+#else
+extern inline __u32 rotate_left(int i, __u32 word)
+{
+	__asm__("roll %%cl,%0"
+		:"=r" (word)
+		:"0" (word),"c" (i));
+	return word;
+}
+#endif
+
+/*
+ * More asm magic....
+ * 
+ * For entropy estimation, we need to do an integral base 2
+ * logarithm.  By default, use an open-coded C version, although we do
+ * have a version which takes advantage of the Intel's x86's "bsr"
+ * instruction.
+ */
+#if (!defined (__i386__))
+static inline __u32 int_ln(__u32 word)
+{
+	__u32 nbits = 0;
+	
+	while (1) {
+		word >>= 1;
+		if (!word)
+			break;
+		nbits++;
+	}
+	return nbits;
+}
+#else
+static inline __u32 int_ln(__u32 word)
+{
+	__asm__("bsrl %1,%0\n\t"
+		"jnz 1f\n\t"
+		"movl $0,%0\n"
+		"1:"
+		:"=r" (word)
+		:"r" (word));
+	return word;
+}
+#endif
+
 	
 /*
  * Initialize the random pool with standard stuff.
@@ -331,9 +416,11 @@
 {
 	__u32 word, *p;
 	int i;
-	
-	add_entropy_word(r, xtime.tv_sec);
-	add_entropy_word(r, xtime.tv_usec);
+	struct timeval 	tv;
+
+	do_gettimeofday(&tv);
+	add_entropy_word(r, tv.tv_sec);
+	add_entropy_word(r, tv.tv_usec);
 
 	for (p = (__u32 *) &system_utsname,
 	     i = sizeof(system_utsname) / sizeof(__u32);
@@ -364,6 +451,12 @@
 		irq_timer_state[i] = NULL;
 	for (i = 0; i < MAX_BLKDEV; i++)
 		blkdev_timer_state[i] = NULL;
+	memset(&keyboard_timer_state, 0, sizeof(struct timer_rand_state));
+	memset(&mouse_timer_state, 0, sizeof(struct timer_rand_state));
+	memset(&extract_timer_state, 0, sizeof(struct timer_rand_state));
+#ifdef RANDOM_BENCHMARK
+	initialize_benchmark(&timer_benchmark, "timer", 0);
+#endif
 	extract_timer_state.dont_count_entropy = 1;
 	random_wait = NULL;
 }
@@ -418,23 +511,25 @@
  * scancodes, for example), the upper bits of the entropy pool don't
  * get affected. --- TYT, 10/11/95
  */
-static inline void add_entropy_word(struct random_bucket *r,
-				    const __u32 input)
+static inline void fast_add_entropy_word(struct random_bucket *r,
+					 const __u32 input)
 {
 	unsigned i;
+	int new_rotate;
 	__u32 w;
 
-	w = (input << r->input_rotate) | (input >> (32 - r->input_rotate));
+	w = rotate_left(r->input_rotate, input);
 	i = r->add_ptr = (r->add_ptr - 1) & (POOLWORDS-1);
+	/*
+	 * Normally, we add 7 bits of rotation to the pool.  At the
+	 * beginning of the pool, add an extra 7 bits rotation, so
+	 * that successive passes spread the input bits across the
+	 * pool evenly.
+	 */
+	new_rotate = r->input_rotate + 14;
 	if (i)
-		r->input_rotate = (r->input_rotate + 7) & 31;
-	else
-		/*
-		 * At the beginning of the pool, add an extra 7 bits
-		 * rotation, so that successive passes spread the
-		 * input bits across the pool evenly.
-		 */
-		r->input_rotate = (r->input_rotate + 14) & 31;
+		new_rotate = r->input_rotate + 7;
+	r->input_rotate = new_rotate & 31;
 
 	/* XOR in the various taps */
 	w ^= r->pool[(i+TAP1)&(POOLWORDS-1)];
@@ -448,6 +543,15 @@
 }
 
 /*
+ * For places where we don't need the inlined version
+ */
+static void add_entropy_word(struct random_bucket *r,
+				    const __u32 input)
+{
+	fast_add_entropy_word(r, input);
+}
+
+/*
  * This function adds entropy to the entropy "pool" by using timing
  * delays.  It uses the timer_rand_state structure to make an estimate
  * of how many bits of entropy this call has added to the pool.
@@ -463,9 +567,11 @@
 				 struct timer_rand_state *state, unsigned num)
 {
 	int	delta, delta2, delta3;
-	unsigned	nbits;
 	__u32		time;
 
+#ifdef RANDOM_BENCHMARK
+	begin_benchmark(&timer_benchmark);
+#endif
 #if defined (__i386__)
 	if (x86_capability & 16) {
 		unsigned long low, high;
@@ -480,15 +586,16 @@
 	time = jiffies;
 #endif
 
-	add_entropy_word(r, (__u32) num);
-	add_entropy_word(r, time);
-
+	fast_add_entropy_word(r, (__u32) num);
+	fast_add_entropy_word(r, time);
+	
 	/*
 	 * Calculate number of bits of randomness we probably
 	 * added.  We take into account the first and second order
 	 * deltas in order to make our estimate.
 	 */
-	if (!state->dont_count_entropy) {
+	if (!state->dont_count_entropy &&
+	    (r->entropy_count < POOLBITS)) {
 		delta = time - state->last_time;
 		state->last_time = time;
 		if (delta < 0) delta = -delta;
@@ -502,17 +609,10 @@
 		if (delta3 < 0) delta3 = -delta3;
 
 		delta = MIN(MIN(delta, delta2), delta3) >> 1;
-		for (nbits = 0; delta; nbits++)
-			delta >>= 1;
-
-		/*
-		 * In no case do we assume we've added more than 12
-		 * bits of randomness.
-		 */
-		if (nbits > 12)
-			nbits = 12;
+		/* Limit entropy estimate to 12 bits */
+		delta &= (1 << 12) - 1;
 
-		r->entropy_count += nbits;
+		r->entropy_count += int_ln(delta);
 
 		/* Prevent overflow */
 		if (r->entropy_count > POOLBITS)
@@ -521,7 +621,10 @@
 		
 	/* Wake up waiting processes, if we have enough entropy. */
 	if (r->entropy_count >= WAIT_INPUT_BITS)
-		wake_up_interruptible(&random_wait);	
+		wake_up_interruptible(&random_wait);
+#ifdef RANDOM_BENCHMARK
+	end_benchmark(&timer_benchmark);
+#endif
 }
 
 void add_keyboard_randomness(unsigned char scancode)
@@ -830,21 +933,24 @@
  * bits of entropy are left in the pool, but it does not restrict the
  * number of bytes that are actually obtained.
  */
-static inline int extract_entropy(struct random_bucket *r, char * buf,
+static int extract_entropy(struct random_bucket *r, char * buf,
 				  int nbytes, int to_user)
 {
 	int ret, i;
 	__u32 tmp[HASH_BUFFER_SIZE];
 	char *cp,*dp;
+
+	if (to_user) {
+		ret = verify_area(VERIFY_WRITE, (void *) buf, nbytes);
+		if (ret)
+			return(ret);
+	}
 	
 	add_timer_randomness(r, &extract_timer_state, nbytes);
 	
 	/* Redundant, but just in case... */
 	if (r->entropy_count > POOLBITS) 
 		r->entropy_count = POOLBITS;
-	/* Why is this here?  Left in from Ted Ts'o.  Perhaps to limit time. */
-	if (nbytes > 32768)
-		nbytes = 32768;
 
 	ret = nbytes;
 	if (r->entropy_count / 8 >= nbytes)
@@ -947,6 +1053,11 @@
 			continue;
 		}
 		n = extract_entropy(&random_state, buf, n, 1);
+		if (n < 0) {
+			if (count == 0)
+				retval = n;
+			break;
+		}
 		count += n;
 		buf += n;
 		nbytes -= n;
@@ -1182,3 +1293,120 @@
 	NULL		/* no special release code */
 };
 
+/*
+ * TCP initial sequence number picking.  This uses the random number
+ * generator to pick an initial secret value.  This value is hashed
+ * along with the TCP endpoint information to provide a unique
+ * starting point for each pair of TCP endpoints.  This defeats
+ * attacks which rely on guessing the initial TCP sequence number.
+ * This algorithm was suggested by Steve Bellovin.
+ */
+__u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
+				 __u16 sport, __u16 dport)
+{
+	static int	is_init = 0;
+	static __u32	secret[16];
+	struct timeval 	tv;
+	__u32 		tmp[16];
+	__u32		seq;
+
+	/*
+	 * Pick a random secret the first time we open a TCP
+	 * connection.
+	 */
+	if (is_init == 0) {
+		get_random_bytes(&secret, sizeof(secret));
+		is_init = 1;
+	}
+
+	memcpy(tmp, secret, sizeof(tmp));
+	/*
+	 * Pick a unique starting offset for each
+	 * TCP connection endpoints (saddr, daddr, sport, dport)
+	 */
+	tmp[8]=saddr;
+	tmp[9]=daddr;
+	tmp[10]=(sport << 16) + dport;
+	HASH_TRANSFORM(tmp, tmp);
+	
+	/*
+	 *	As close as possible to RFC 793, which
+	 *	suggests using a 250kHz clock.
+	 *	Further reading shows this assumes 2MB/s networks.
+	 *	For 10MB/s ethernet, a 1MHz clock is appropriate.
+	 *	That's funny, Linux has one built in!  Use it!
+	 */
+	do_gettimeofday(&tv);
+	seq = tmp[1] + tv.tv_usec+tv.tv_sec*1000000;
+#if 0
+	printk("init_seq(%lx, %lx, %d, %d) = %d\n",
+	       saddr, daddr, sport, dport, seq);
+#endif
+	return (seq);
+}
+
+#ifdef RANDOM_BENCHMARK
+/*
+ * This is so we can do some benchmarking of the random driver, to see
+ * how much overhead add_timer_randomness really takes.  This only
+ * works on a Pentium, since it depends on the timer clock...
+ *
+ * Note: the results of this benchmark as of this writing (5/27/96)
+ *
+ * On a Pentium, add_timer_randomness() takes between 150 and 1000
+ * clock cycles, with an average of around 600 clock cycles.  On a 75
+ * MHz Pentium, this translates to 2 to 13 microseconds, with an
+ * average time of 8 microseconds.  This should be fast enough so we
+ * can use add_timer_randomness() even with the fastest of interrupts...
+ */
+static inline unsigned long long get_clock_cnt(void)
+{
+	unsigned long low, high;
+	__asm__(".byte 0x0f,0x31" :"=a" (low), "=d" (high));
+	return (((unsigned long long) high << 31) | low); 
+}
+
+static void initialize_benchmark(struct random_benchmark *bench,
+				 const char *descr, int unit)
+{
+	bench->times = 0;
+	bench->accum = 0;
+	bench->max = 0;
+	bench->min = 1 << 31;
+	bench->descr = descr;
+	bench->unit = unit;
+}
+
+static void begin_benchmark(struct random_benchmark *bench)
+{
+#ifdef BENCHMARK_NOINT
+	save_flags(bench->flags); cli();
+#endif
+	bench->start_time = get_clock_cnt();
+}
+
+static void end_benchmark(struct random_benchmark *bench)
+{
+	unsigned long ticks;
+	
+	ticks = (unsigned long) (get_clock_cnt() - bench->start_time);
+#ifdef BENCHMARK_NOINT
+	restore_flags(bench->flags);
+#endif
+	if (ticks < bench->min)
+		bench->min = ticks;
+	if (ticks > bench->max)
+		bench->max = ticks;
+	bench->accum += ticks;
+	bench->times++;
+	if (bench->times == BENCHMARK_INTERVAL) {
+		printk("Random benchmark: %s %d: %lu min, %lu avg, "
+		       "%lu max\n", bench->descr, bench->unit, bench->min,
+		       bench->accum / BENCHMARK_INTERVAL, bench->max);
+		bench->times = 0;
+		bench->accum = 0;
+		bench->max = 0;
+		bench->min = 1 << 31;
+	}
+}	
+#endif /* RANDOM_BENCHMARK */
diff -u --recursive --new-file pre2.0.12/linux/drivers/char/selection.c linux/drivers/char/selection.c
--- pre2.0.12/linux/drivers/char/selection.c	Sun Mar 31 00:13:17 1996
+++ linux/drivers/char/selection.c	Thu Jun  6 13:42:16 1996
@@ -123,6 +123,10 @@
 
 	  args = (unsigned short *)(arg + 1);
 	  if (user) {
+	  	  int err;
+		  err = verify_area(VERIFY_READ, args, sizeof(short) * 5);
+		  if (err)
+		  	return err;
 		  xs = get_user(args++) - 1;
 		  ys = get_user(args++) - 1;
 		  xe = get_user(args++) - 1;
diff -u --recursive --new-file pre2.0.12/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- pre2.0.12/linux/drivers/char/serial.c	Wed Jun  5 10:41:28 1996
+++ linux/drivers/char/serial.c	Thu Jun  6 13:42:16 1996
@@ -49,7 +49,7 @@
 #include <asm/bitops.h>
 
 static char *serial_name = "Serial driver";
-static char *serial_version = "4.12";
+static char *serial_version = "4.13";
 
 DECLARE_TASK_QUEUE(tq_serial);
 
@@ -90,6 +90,13 @@
 
 #define _INLINE_ inline
   
+#ifdef MODULE
+static int io[PORT_MAX] = { 0, };
+static int irq[PORT_MAX]  = { 0, };
+static int type[PORT_MAX]  = { 0, };
+static int flags[PORT_MAX]  = { 0, };
+#endif
+
 #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
 #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
  kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s)
@@ -1944,6 +1951,9 @@
 				    (unsigned long *) arg);
 			return 0;
 		case TIOCSSOFTCAR:
+			error = verify_area(VERIFY_READ, (void *) arg,sizeof(long));
+			if (error)
+				return error;
 			arg = get_fs_long((unsigned long *) arg);
 			tty->termios->c_cflag =
 				((tty->termios->c_cflag & ~CLOCAL) |
@@ -1967,6 +1977,10 @@
 			return get_serial_info(info,
 					       (struct serial_struct *) arg);
 		case TIOCSSERIAL:
+			error = verify_area(VERIFY_READ, (void *) arg,
+						sizeof(struct serial_struct));
+			if (error)
+				return error;
 			return set_serial_info(info,
 					       (struct serial_struct *) arg);
 		case TIOCSERCONFIG:
@@ -1991,6 +2005,9 @@
 		case TIOCSERSWILD:
 			if (!suser())
 				return -EPERM;
+			error = verify_area(VERIFY_READ, (void *) arg,sizeof(long));
+			if (error)
+				return error;
 			rs_wild_int_mask = get_fs_long((unsigned long *) arg);
 			if (rs_wild_int_mask < 0)
 				rs_wild_int_mask = check_wild_interrupts(0);
@@ -2013,6 +2030,10 @@
 			return get_multiport_struct(info,
 				       (struct serial_multiport_struct *) arg);
 		case TIOCSERSETMULTI:
+			error = verify_area(VERIFY_READ, (void *) arg,
+				    sizeof(struct serial_multiport_struct));
+			if (error)
+				return error;
 			return set_multiport_struct(info,
 				       (struct serial_multiport_struct *) arg);
 		/*
@@ -2804,13 +2825,25 @@
 			info->icount.rng = info->icount.dcd = 0;
 		info->next_port = 0;
 		info->prev_port = 0;
+#ifdef MODULE
+		if(irq[i])
+			info->irq=irq[i];
+		if (io[i])
+			info->port=io[i];
+		if (type[i])
+			info->type = type[i];
+		if (flags[i])
+			info->flags = flags[i];
+#endif
 		if (info->irq == 2)
 			info->irq = 9;
-		if (!(info->flags & ASYNC_BOOT_AUTOCONF))
-			continue;
-		autoconfig(info);
-		if (info->type == PORT_UNKNOWN)
-			continue;
+		if (info->type == PORT_UNKNOWN) {
+			if (!(info->flags & ASYNC_BOOT_AUTOCONF))
+				continue;
+			autoconfig(info);
+			if (info->type == PORT_UNKNOWN)
+				continue;
+		}
 		printk(KERN_INFO "tty%02d%s at 0x%04x (irq = %d)", info->line, 
 		       (info->flags & ASYNC_FOURPORT) ? " FourPort" : "",
 		       info->port, info->irq);
diff -u --recursive --new-file pre2.0.12/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- pre2.0.12/linux/drivers/char/tty_io.c	Fri May 17 15:32:14 1996
+++ linux/drivers/char/tty_io.c	Thu Jun  6 13:42:16 1996
@@ -431,6 +431,9 @@
 	tty->session = 0;
 	tty->pgrp = -1;
 	tty->ctrl_status = 0;
+	tty->packet = 0;
+	if (tty->link)
+		tty->link->packet = 0;
 	if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
 		*tty->termios = tty->driver.init_termios;
 	if (tty->driver.hangup)
@@ -1520,6 +1523,10 @@
 			retval = tty_check_change(tty);
 			if (retval)
 				return retval;
+			retval = verify_area(VERIFY_READ, (void *) arg,
+					     sizeof (int));
+			if (retval)
+				return retval;
 			arg = get_user((int *) arg);
 			return tty_set_ldisc(tty, arg);
 		case TIOCLINUX:
@@ -1558,9 +1565,15 @@
 			 * kernel-internal variable; programs not closely
 			 * related to the kernel should not use this.
 			 */
+					retval = verify_area(VERIFY_WRITE, (void *) arg, 1);
+					if (retval)
+						return retval;
 					put_user(shift_state,(char *) arg);
 					return 0;
 				case 7:
+					retval = verify_area(VERIFY_WRITE, (void *) arg, 1);
+					if (retval)
+						return retval;
 					put_user(mouse_reporting(),(char *) arg);
 					return 0;
 				case 10:
diff -u --recursive --new-file pre2.0.12/linux/drivers/char/vesa_blank.c linux/drivers/char/vesa_blank.c
--- pre2.0.12/linux/drivers/char/vesa_blank.c	Fri Mar 15 11:01:45 1996
+++ linux/drivers/char/vesa_blank.c	Thu Jun  6 13:42:16 1996
@@ -44,6 +44,7 @@
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/segment.h>
+#include <linux/mm.h>
 
 extern unsigned short video_port_reg, video_port_val;
 
@@ -267,7 +268,12 @@
 void set_vesa_blanking(const unsigned long arg)
 {
 	unsigned char *argp = (unsigned char *)(arg + 1);
-	unsigned int mode = get_user(argp);
+	unsigned int mode;
+
+	if (verify_area(VERIFY_READ, argp, 1))
+		return;
+
+	mode = get_user(argp);
 	vesa_blanking_mode = suspend_vesa_blanking_mode =
 		((mode <= VESA_POWERDOWN) ? mode : DEFAULT_VESA_BLANKING_MODE);
 }
diff -u --recursive --new-file pre2.0.12/linux/drivers/net/lance.c linux/drivers/net/lance.c
--- pre2.0.12/linux/drivers/net/lance.c	Tue May 21 19:52:36 1996
+++ linux/drivers/net/lance.c	Thu Jun  6 12:59:42 1996
@@ -165,6 +165,7 @@
  *	- added support for Linux/Alpha, but removed most of it, because
  *        it worked only for the PCI chip. 
  *      - added hook for the 32bit lance driver
+ *      - added PCnetPCI II (79C970A) to chip table
  *
  *	Paul Gortmaker (gpg109@rsphy1.anu.edu.au):
  *	- hopefully fix above so Linux/Alpha can use ISA cards too.
@@ -268,12 +269,15 @@
 	{0x2430, "PCnet32",					/* 79C965 PCnet for VL bus. */
 		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
 			LANCE_HAS_MISSED_FRAME},
+        {0x2621, "PCnet/PCI-II 79C970A",        /* 79C970A PCInetPCI II. */
+                LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+                        LANCE_HAS_MISSED_FRAME},
 	{0x0, 	 "PCnet (unknown)",
 		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
 			LANCE_HAS_MISSED_FRAME},
 };
 
-enum {OLD_LANCE = 0, PCNET_ISA=1, PCNET_ISAP=2, PCNET_PCI=3, PCNET_VLB=4, LANCE_UNKNOWN=5};
+enum {OLD_LANCE = 0, PCNET_ISA=1, PCNET_ISAP=2, PCNET_PCI=3, PCNET_VLB=4, PCNET_PCI_II=5, LANCE_UNKNOWN=6};
 
 /* Non-zero only if the current card is a PCI with BIOS-set IRQ. */
 static unsigned char pci_irq_line = 0;
@@ -437,7 +441,7 @@
 
 #ifdef CONFIG_LANCE32
         /* look if it's a PCI or VLB chip */
-        if (lance_version == PCNET_PCI || lance_version == PCNET_VLB) {
+        if (lance_version == PCNET_PCI || lance_version == PCNET_VLB || lance_version == PCNET_PCI_II) {
 	    extern void lance32_probe1 (struct device *dev, const char *chipname, int pci_irq_line);
 	    
 	    lance32_probe1 (dev, chipname, pci_irq_line);
diff -u --recursive --new-file pre2.0.12/linux/drivers/net/smc-ultra.c linux/drivers/net/smc-ultra.c
--- pre2.0.12/linux/drivers/net/smc-ultra.c	Tue May  7 16:22:31 1996
+++ linux/drivers/net/smc-ultra.c	Thu Jun  6 09:54:05 1996
@@ -40,10 +40,11 @@
 
 	Paul Gortmaker	: multiple card support for module users.
 	Donald Becker	: 4/17/96 PIO support, minor potential problems avoided.
+	Donald Becker	: 6/6/96 correctly set auto-wrap bit.
 */
 
 static const char *version =
-	"smc-ultra.c:v1.99 4/17/96 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+	"smc-ultra.c:v2.00 6/6/96 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
 
 
 #include <linux/module.h>
@@ -256,9 +257,10 @@
 
 	outb(0x00, ioaddr);	/* Disable shared memory for safety. */
 	outb(0x80, ioaddr + 5);
-	if (ei_status.block_input == &ultra_pio_input)
+	if (ei_status.block_input == &ultra_pio_input) {
 		outb(0x11, ioaddr + 6);		/* Enable interrupts and PIO. */
-	else
+		outb(0x01, ioaddr + 0x19);  	/* Enable ring read auto-wrap. */
+	} else
 		outb(0x01, ioaddr + 6);		/* Enable interrupts and memory. */
 	/* Set the early receive warning level in window 0 high enough not
 	   to receive ERW interrupts. */
diff -u --recursive --new-file pre2.0.12/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c
--- pre2.0.12/linux/drivers/scsi/BusLogic.c	Fri May 17 15:32:15 1996
+++ linux/drivers/scsi/BusLogic.c	Thu Jun  6 02:54:14 1996
@@ -24,8 +24,8 @@
 */
 
 
-#define BusLogic_DriverVersion		"2.0.3"
-#define BusLogic_DriverDate		"17 May 1996"
+#define BusLogic_DriverVersion		"2.0.4"
+#define BusLogic_DriverDate		"5 June 1996"
 
 
 #include <linux/module.h>
@@ -100,7 +100,7 @@
   which BusLogic Host Adapters may potentially be found.
 */
 
-static unsigned short
+static unsigned int
   BusLogic_IO_StandardAddresses[] =
     { 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0 };
 
@@ -112,19 +112,18 @@
   standard BusLogic I/O Addresses.
 */
 
-static unsigned short
+static unsigned int
   BusLogic_IO_AddressProbeList[BusLogic_IO_MaxProbeAddresses+1] =   { 0 };
 
 
 /*
   BusLogic_IRQ_UsageCount stores a count of the number of Host Adapters using
   a given IRQ Channel, which is necessary to support PCI, EISA, or MCA shared
-  interrupts.  Only IRQ Channels 9, 10, 11, 12, 14, and 15 are supported by
-  BusLogic Host Adapters.
+  interrupts.
 */
 
-static short
-  BusLogic_IRQ_UsageCount[7] =		{ 0 };
+static int
+  BusLogic_IRQ_UsageCount[NR_IRQS] =	{ 0 };
 
 
 /*
@@ -319,7 +318,7 @@
 
 static BusLogic_CCB_T *BusLogic_AllocateCCB(BusLogic_HostAdapter_T *HostAdapter)
 {
-  static unsigned int SerialNumber = 0;
+  static unsigned long SerialNumber = 0;
   BusLogic_CCB_T *CCB;
   int Allocated;
   CCB = HostAdapter->Free_CCBs;
@@ -406,8 +405,7 @@
   unsigned char *ParameterPointer = (unsigned char *) ParameterData;
   unsigned char *ReplyPointer = (unsigned char *) ReplyData;
   unsigned char StatusRegister = 0, InterruptRegister;
-  long TimeoutCounter;
-  int ReplyBytes = 0;
+  int ReplyBytes = 0, TimeoutCounter;
   /*
     Clear out the Reply Data if provided.
   */
@@ -591,7 +589,7 @@
   */
   if (pcibios_present())
     {
-      unsigned short BusDeviceFunction[BusLogic_IO_MaxProbeAddresses];
+      unsigned int BusDeviceFunction[BusLogic_IO_MaxProbeAddresses];
       unsigned short Index = 0, VendorID, DeviceID;
       boolean NonIncreasingScanningOrder = false;
       unsigned char Bus, DeviceFunction;
@@ -668,7 +666,7 @@
 		  for (j = 0; j < Bound; j++)
 		    if (BusDeviceFunction[j] > BusDeviceFunction[j+1])
 		      {
-			unsigned short Temp;
+			unsigned int Temp;
 			Temp = BusDeviceFunction[j];
 			BusDeviceFunction[j] = BusDeviceFunction[j+1];
 			BusDeviceFunction[j+1] = Temp;
@@ -765,7 +763,7 @@
 					     *HostAdapter)
 {
   boolean TraceHardReset = (BusLogic_GlobalOptions & BusLogic_TraceHardReset);
-  long TimeoutCounter = loops_per_sec >> 2;
+  int TimeoutCounter = loops_per_sec >> 2;
   unsigned char StatusRegister = 0;
   /*
     Issue a Hard Reset Command to the Host Adapter.  The Host Adapter should
@@ -958,18 +956,6 @@
       != sizeof(FirmwareVersion3rdDigit))
     return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE 3RD DIGIT");
   /*
-    Issue the Inquire Firmware Version Letter command.
-  */
-  FirmwareVersionLetter = '\0';
-  if (BoardID.FirmwareVersion1stDigit > '3' ||
-      (BoardID.FirmwareVersion1stDigit == '3' &&
-       BoardID.FirmwareVersion2ndDigit >= '3'))
-    if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersionLetter,
-			 NULL, 0, &FirmwareVersionLetter,
-			 sizeof(FirmwareVersionLetter))
-	!= sizeof(FirmwareVersionLetter))
-      return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE VERSION LETTER");
-  /*
     BusLogic Host Adapters can be identified by their model number and
     the major version number of their firmware as follows:
 
@@ -985,10 +971,53 @@
     0.xx	AMI FastDisk VLB/EISA BusLogic Clone Host Adapter
   */
   /*
+    Save the Model Name and Board Name in the Host Adapter structure.
+  */
+  TargetPointer = HostAdapter->ModelName;
+  *TargetPointer++ = 'B';
+  *TargetPointer++ = 'T';
+  *TargetPointer++ = '-';
+  for (i = 0; i < sizeof(BoardModelNumber); i++)
+    {
+      Character = BoardModelNumber[i];
+      if (Character == ' ' || Character == '\0') break;
+      *TargetPointer++ = Character;
+    }
+  *TargetPointer++ = '\0';
+  strcpy(HostAdapter->BoardName, "BusLogic ");
+  strcat(HostAdapter->BoardName, HostAdapter->ModelName);
+  strcpy(HostAdapter->InterruptLabel, HostAdapter->BoardName);
+  /*
+    Save the Firmware Version in the Host Adapter structure.
+  */
+  TargetPointer = HostAdapter->FirmwareVersion;
+  *TargetPointer++ = BoardID.FirmwareVersion1stDigit;
+  *TargetPointer++ = '.';
+  *TargetPointer++ = BoardID.FirmwareVersion2ndDigit;
+  if (FirmwareVersion3rdDigit != ' ' && FirmwareVersion3rdDigit != '\0')
+    *TargetPointer++ = FirmwareVersion3rdDigit;
+  *TargetPointer = '\0';
+  /*
+    Issue the Inquire Firmware Version Letter command.
+  */
+  if (strcmp(HostAdapter->FirmwareVersion, "3.3") >= 0)
+    {
+      if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersionLetter,
+			   NULL, 0, &FirmwareVersionLetter,
+			   sizeof(FirmwareVersionLetter))
+	  != sizeof(FirmwareVersionLetter))
+	return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE VERSION LETTER");
+      if (FirmwareVersionLetter != ' ' && FirmwareVersionLetter != '\0')
+	*TargetPointer++ = FirmwareVersionLetter;
+      *TargetPointer = '\0';
+    }
+  /*
     Issue the Inquire Generic I/O Port Information command to read the
-    termination information from "W" Series Host Adapters.
+    IRQ Channel from all PCI Host Adapters, and the Termination Information
+    from "W" Series Host Adapters.
   */
-  if (BoardID.FirmwareVersion1stDigit == '5')
+  if (HostAdapter->ModelName[3] == '9' &&
+      strcmp(HostAdapter->FirmwareVersion, "4.25") >= 0)
     {
       if (BusLogic_Command(HostAdapter,
 			   BusLogic_InquireGenericIOPortInformation,
@@ -998,9 +1027,14 @@
 	return BusLogic_Failure(HostAdapter,
 				"INQUIRE GENERIC I/O PORT INFORMATION");
       /*
+	Save the IRQ Channel in the Host Adapter structure.
+      */
+      HostAdapter->IRQ_Channel = GenericIOPortInformation.PCIAssignedIRQChannel;
+      /*
 	Save the Termination Information in the Host Adapter structure.
       */
-      if (GenericIOPortInformation.Valid)
+      if (HostAdapter->FirmwareVersion[0] == '5' &&
+	  GenericIOPortInformation.Valid)
 	{
 	  HostAdapter->TerminationInfoValid = true;
 	  HostAdapter->LowByteTerminated =
@@ -1010,10 +1044,10 @@
 	}
     }
   /*
-    Issue the Fetch Host Adapter Local RAM command to read the termination
-    information from the AutoSCSI area of "C" Series Host Adapters.
+    Issue the Fetch Host Adapter Local RAM command to read the Termination
+    Information from the AutoSCSI area of "C" Series Host Adapters.
   */
-  if (BoardID.FirmwareVersion1stDigit == '4')
+  if (HostAdapter->FirmwareVersion[0] == '4')
     {
       FetchHostAdapterLocalRAMRequest.ByteOffset =
 	BusLogic_AutoSCSI_BaseOffset + 15;
@@ -1033,58 +1067,23 @@
       HostAdapter->HighByteTerminated = AutoSCSIByte15.HighByteTerminated;
     }
   /*
-    Save the Model Name and Board Name in the Host Adapter structure.
+    Determine the IRQ Channel and save it in the Host Adapter structure.
   */
-  TargetPointer = HostAdapter->ModelName;
-  *TargetPointer++ = 'B';
-  *TargetPointer++ = 'T';
-  *TargetPointer++ = '-';
-  for (i = 0; i < sizeof(BoardModelNumber); i++)
+  if (HostAdapter->IRQ_Channel == 0)
     {
-      Character = BoardModelNumber[i];
-      if (Character == ' ' || Character == '\0') break;
-      *TargetPointer++ = Character;
+      if (Configuration.IRQ_Channel9)
+	HostAdapter->IRQ_Channel = 9;
+      else if (Configuration.IRQ_Channel10)
+	HostAdapter->IRQ_Channel = 10;
+      else if (Configuration.IRQ_Channel11)
+	HostAdapter->IRQ_Channel = 11;
+      else if (Configuration.IRQ_Channel12)
+	HostAdapter->IRQ_Channel = 12;
+      else if (Configuration.IRQ_Channel14)
+	HostAdapter->IRQ_Channel = 14;
+      else if (Configuration.IRQ_Channel15)
+	HostAdapter->IRQ_Channel = 15;
     }
-  *TargetPointer++ = '\0';
-  strcpy(HostAdapter->BoardName, "BusLogic ");
-  strcat(HostAdapter->BoardName, HostAdapter->ModelName);
-  strcpy(HostAdapter->InterruptLabel, HostAdapter->BoardName);
-  /*
-    Save the Firmware Version in the Host Adapter structure.
-  */
-  TargetPointer = HostAdapter->FirmwareVersion;
-  *TargetPointer++ = BoardID.FirmwareVersion1stDigit;
-  *TargetPointer++ = '.';
-  *TargetPointer++ = BoardID.FirmwareVersion2ndDigit;
-  if (FirmwareVersion3rdDigit != ' ' && FirmwareVersion3rdDigit != '\0')
-    *TargetPointer++ = FirmwareVersion3rdDigit;
-  if (FirmwareVersionLetter != ' ' && FirmwareVersionLetter != '\0')
-    *TargetPointer++ = FirmwareVersionLetter;
-  *TargetPointer++ = '\0';
-  /*
-    Determine the IRQ Channel and save it in the Host Adapter structure.
-  */
-  if (Configuration.IRQ_Channel9)
-    HostAdapter->IRQ_Channel = 9;
-  else if (Configuration.IRQ_Channel10)
-    HostAdapter->IRQ_Channel = 10;
-  else if (Configuration.IRQ_Channel11)
-    HostAdapter->IRQ_Channel = 11;
-  else if (Configuration.IRQ_Channel12)
-    HostAdapter->IRQ_Channel = 12;
-  else if (Configuration.IRQ_Channel14)
-    HostAdapter->IRQ_Channel = 14;
-  else if (Configuration.IRQ_Channel15)
-    HostAdapter->IRQ_Channel = 15;
-  /*
-    Determine the DMA Channel and save it in the Host Adapter structure.
-  */
-  if (Configuration.DMA_Channel5)
-    HostAdapter->DMA_Channel = 5;
-  else if (Configuration.DMA_Channel6)
-    HostAdapter->DMA_Channel = 6;
-  else if (Configuration.DMA_Channel7)
-    HostAdapter->DMA_Channel = 7;
   /*
     Save the Host Adapter SCSI ID in the Host Adapter structure.
   */
@@ -1098,28 +1097,30 @@
   HostAdapter->ParityChecking = SetupInformation.ParityCheckEnabled;
   /*
     Determine the Bus Type and save it in the Host Adapter structure,
-    overriding the DMA Channel if it is inappropriate for the bus type.
+    and determine and save the DMA Channel for ISA Host Adapters.
   */
   switch (HostAdapter->ModelName[3])
     {
     case '4':
       HostAdapter->BusType = BusLogic_VESA_Bus;
-      HostAdapter->DMA_Channel = 0;
       break;
     case '5':
       HostAdapter->BusType = BusLogic_ISA_Bus;
+      if (Configuration.DMA_Channel5)
+	HostAdapter->DMA_Channel = 5;
+      else if (Configuration.DMA_Channel6)
+	HostAdapter->DMA_Channel = 6;
+      else if (Configuration.DMA_Channel7)
+	HostAdapter->DMA_Channel = 7;
       break;
     case '6':
       HostAdapter->BusType = BusLogic_MCA_Bus;
-      HostAdapter->DMA_Channel = 0;
       break;
     case '7':
       HostAdapter->BusType = BusLogic_EISA_Bus;
-      HostAdapter->DMA_Channel = 0;
       break;
     case '9':
       HostAdapter->BusType = BusLogic_PCI_Bus;
-      HostAdapter->DMA_Channel = 0;
       break;
     }
   /*
@@ -1341,7 +1342,7 @@
     printk("%d, ", HostAdapter->DMA_Channel);
   else printk("None, ");
   if (HostAdapter->BIOS_Address > 0)
-    printk("BIOS Address: 0x%lX, ", HostAdapter->BIOS_Address);
+    printk("BIOS Address: 0x%X, ", HostAdapter->BIOS_Address);
   else printk("BIOS Address: None, ");
   printk("Host Adapter SCSI ID: %d\n", HostAdapter->SCSI_ID);
   printk("scsi%d:   Scatter/Gather Limit: %d of %d segments, "
@@ -1432,13 +1433,13 @@
     Acquire exclusive or shared access to the IRQ Channel.  A usage count is
     maintained so that PCI, EISA, or MCA shared interrupts can be supported.
   */
-  if (BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9]++ == 0)
+  if (BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel]++ == 0)
     {
       if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler,
 		      SA_INTERRUPT | SA_SHIRQ,
 		      HostAdapter->InterruptLabel, NULL) < 0)
 	{
-	  BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9]--;
+	  BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel]--;
 	  printk("scsi%d: UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n",
 		 HostAdapter->HostNumber, HostAdapter->IRQ_Channel);
 	  return false;
@@ -1498,7 +1499,7 @@
     Release exclusive or shared access to the IRQ Channel.
   */
   if (HostAdapter->IRQ_ChannelAcquired)
-    if (--BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9] == 0)
+    if (--BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel] == 0)
       free_irq(HostAdapter->IRQ_Channel, NULL);
   /*
     Release exclusive access to the DMA Channel.
@@ -1601,7 +1602,8 @@
     Initialize the Host Adapter's Pointer to the Outgoing/Incoming Mailboxes.
   */
   ExtendedMailboxRequest.MailboxCount = HostAdapter->MailboxCount;
-  ExtendedMailboxRequest.BaseMailboxAddress = HostAdapter->FirstOutgoingMailbox;
+  ExtendedMailboxRequest.BaseMailboxAddress =
+    Virtual_to_Bus(HostAdapter->FirstOutgoingMailbox);
   if (BusLogic_Command(HostAdapter, BusLogic_InitializeExtendedMailbox,
 		       &ExtendedMailboxRequest,
 		       sizeof(ExtendedMailboxRequest), NULL, 0) < 0)
@@ -1858,12 +1860,14 @@
 	else UntaggedDeviceCount++;
       }
   if (TaggedQueueDepth == 0 && TaggedDeviceCount > 0)
-    TaggedQueueDepth =
-      1 + ((HostAdapter->TotalQueueDepth
-	    - UntaggedDeviceCount * UntaggedQueueDepth)
-	   / TaggedDeviceCount);
-  if (TaggedQueueDepth > BusLogic_MaxTaggedQueueDepth)
-    TaggedQueueDepth = BusLogic_MaxTaggedQueueDepth;
+    {
+      TaggedQueueDepth =
+	1 + ((HostAdapter->TotalQueueDepth
+	      - UntaggedDeviceCount * UntaggedQueueDepth)
+	     / TaggedDeviceCount);
+      if (TaggedQueueDepth > BusLogic_PreferredTaggedQueueDepth)
+	TaggedQueueDepth = BusLogic_PreferredTaggedQueueDepth;
+    }
   for (Device = DeviceList; Device != NULL; Device = Device->next)
     if (Device->host == Host)
       {
@@ -2180,7 +2184,8 @@
 		      NextIncomingMailbox->CompletionCode) !=
 		     BusLogic_IncomingMailboxFree)
 		{
-		  BusLogic_CCB_T *CCB = NextIncomingMailbox->CCB;
+		  BusLogic_CCB_T *CCB =
+		    (BusLogic_CCB_T *) Bus_to_Virtual(NextIncomingMailbox->CCB);
 		  if (MailboxCompletionCode != BusLogic_AbortedCommandNotFound)
 		    if (CCB->Status == BusLogic_CCB_Active ||
 			CCB->Status == BusLogic_CCB_Reset)
@@ -2211,11 +2216,11 @@
 			  is not marked as status Active or Reset, then there
 			  is most likely a bug in the Host Adapter firmware.
 			*/
-			printk("scsi%d: Illegal CCB #%d status %d in "
+			printk("scsi%d: Illegal CCB #%ld status %d in "
 			       "Incoming Mailbox\n", HostAdapter->HostNumber,
 			       CCB->SerialNumber, CCB->Status);
 		      }
-		  else printk("scsi%d: Aborted CCB #%d to Target %d "
+		  else printk("scsi%d: Aborted CCB #%ld to Target %d "
 			      "Not Found\n", HostAdapter->HostNumber,
 			      CCB->SerialNumber, CCB->TargetID);
 		  NextIncomingMailbox->CompletionCode =
@@ -2252,8 +2257,8 @@
       */
       if (CCB->Opcode == BusLogic_BusDeviceReset)
 	{
-	  unsigned char TargetID = CCB->TargetID;
-	  printk("scsi%d: Bus Device Reset CCB #%d to Target %d Completed\n",
+	  int TargetID = CCB->TargetID;
+	  printk("scsi%d: Bus Device Reset CCB #%ld to Target %d Completed\n",
 		 HostAdapter->HostNumber, CCB->SerialNumber, TargetID);
 	  HostAdapter->TotalCommandCount[TargetID] = 0;
 	  HostAdapter->TaggedQueuingActive[TargetID] = false;
@@ -2301,7 +2306,7 @@
 	    {
 	    case BusLogic_IncomingMailboxFree:
 	    case BusLogic_AbortedCommandNotFound:
-	      printk("scsi%d: CCB #%d to Target %d Impossible State\n",
+	      printk("scsi%d: CCB #%ld to Target %d Impossible State\n",
 		     HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
 	      break;
 	    case BusLogic_CommandCompletedWithoutError:
@@ -2309,7 +2314,7 @@
 	      Command->result = DID_OK << 16;
 	      break;
 	    case BusLogic_CommandAbortedAtHostRequest:
-	      printk("scsi%d: CCB #%d to Target %d Aborted\n",
+	      printk("scsi%d: CCB #%ld to Target %d Aborted\n",
 		     HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
 	      Command->result = DID_ABORT << 16;
 	      break;
@@ -2321,7 +2326,7 @@
 		if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout)
 		  {
 		    int i;
-		    printk("scsi%d: CCB #%d Target %d: Result %X "
+		    printk("scsi%d: CCB #%ld Target %d: Result %X "
 			   "Host Adapter Status %02X Target Status %02X\n",
 			   HostAdapter->HostNumber, CCB->SerialNumber,
 			   CCB->TargetID, Command->result,
@@ -2332,7 +2337,7 @@
 		    printk("\n");
 		    printk("scsi%d: Sense ", HostAdapter->HostNumber);
 		    for (i = 0; i < CCB->SenseDataLength; i++)
-		      printk(" %02X", (*CCB->SenseDataPointer)[i]);
+		      printk(" %02X", Command->sense_buffer[i]);
 		    printk("\n");
 		  }
 	      break;
@@ -2388,7 +2393,7 @@
 	the Host Adapter is operating asynchronously and the locking code
 	does not protect against simultaneous access by the Host Adapter.
       */
-      NextOutgoingMailbox->CCB = CCB;
+      NextOutgoingMailbox->CCB = Virtual_to_Bus(CCB);
       NextOutgoingMailbox->ActionCode = ActionCode;
       BusLogic_StartMailboxCommand(HostAdapter);
       if (++NextOutgoingMailbox > HostAdapter->LastOutgoingMailbox)
@@ -2413,9 +2418,9 @@
   BusLogic_HostAdapter_T *HostAdapter =
     (BusLogic_HostAdapter_T *) Command->host->hostdata;
   unsigned char *CDB = Command->cmnd;
-  unsigned char CDB_Length = Command->cmd_len;
-  unsigned char TargetID = Command->target;
-  unsigned char LogicalUnit = Command->lun;
+  int CDB_Length = Command->cmd_len;
+  int TargetID = Command->target;
+  int LogicalUnit = Command->lun;
   void *BufferPointer = Command->request_buffer;
   int BufferLength = Command->request_bufflen;
   int SegmentCount = Command->use_sg;
@@ -2461,7 +2466,7 @@
     {
       CCB->Opcode = BusLogic_InitiatorCCB;
       CCB->DataLength = BufferLength;
-      CCB->DataPointer = BufferPointer;
+      CCB->DataPointer = Virtual_to_Bus(BufferPointer);
     }
   else
     {
@@ -2469,13 +2474,13 @@
       int Segment;
       CCB->Opcode = BusLogic_InitiatorCCB_ScatterGather;
       CCB->DataLength = SegmentCount * sizeof(BusLogic_ScatterGatherSegment_T);
-      CCB->DataPointer = CCB->ScatterGatherList;
+      CCB->DataPointer = Virtual_to_Bus(CCB->ScatterGatherList);
       for (Segment = 0; Segment < SegmentCount; Segment++)
 	{
 	  CCB->ScatterGatherList[Segment].SegmentByteCount =
 	    ScatterList[Segment].length;
 	  CCB->ScatterGatherList[Segment].SegmentDataPointer =
-	    ScatterList[Segment].address;
+	    Virtual_to_Bus(ScatterList[Segment].address);
 	}
     }
   switch (CDB[0])
@@ -2515,13 +2520,13 @@
     the Host Adapter and Target Device can establish Synchronous and Wide
     Transfer before Queue Tag messages can interfere with the Synchronous and
     Wide Negotiation message.  By waiting to enable Tagged Queuing until after
-    the first BusLogic_MaxTaggedQueueDepth commands have been sent, it is
+    the first BusLogic_PreferredQueueDepth commands have been sent, it is
     assured that after a Reset any pending commands are resent before Tagged
     Queuing is enabled and that the Tagged Queuing message will not occur while
     the partition table is being printed.
   */
   if (HostAdapter->TotalCommandCount[TargetID]++ ==
-        BusLogic_MaxTaggedQueueDepth &&
+        BusLogic_PreferredTaggedQueueDepth &&
       (HostAdapter->TaggedQueuingPermitted & (1 << TargetID)) &&
       Command->device->tagged_supported)
     {
@@ -2564,7 +2569,7 @@
 	}
     }
   memcpy(CCB->CDB, CDB, CDB_Length);
-  CCB->SenseDataPointer = (SCSI_SenseData_T *) &Command->sense_buffer;
+  CCB->SenseDataPointer = Virtual_to_Bus(&Command->sense_buffer);
   CCB->Command = Command;
   Command->scsi_done = CompletionRoutine;
   /*
@@ -2608,7 +2613,7 @@
 {
   BusLogic_HostAdapter_T *HostAdapter =
     (BusLogic_HostAdapter_T *) Command->host->hostdata;
-  unsigned char TargetID = Command->target;
+  int TargetID = Command->target;
   BusLogic_Lock_T Lock;
   BusLogic_CCB_T *CCB;
   int Result;
@@ -2666,7 +2671,7 @@
   if (HostAdapter->TaggedQueuingActive[TargetID] &&
       HostAdapter->FirmwareVersion[0] < '5')
     {
-      printk("scsi%d: Unable to Abort CCB #%d to Target %d - "
+      printk("scsi%d: Unable to Abort CCB #%ld to Target %d - "
 	     "Abort Tag Not Supported\n", HostAdapter->HostNumber,
 	     CCB->SerialNumber, TargetID);
       Result = SCSI_ABORT_SNOOZE;
@@ -2674,13 +2679,13 @@
   else if (BusLogic_WriteOutgoingMailbox(HostAdapter,
 					 BusLogic_MailboxAbortCommand, CCB))
     {
-      printk("scsi%d: Aborting CCB #%d to Target %d\n",
+      printk("scsi%d: Aborting CCB #%ld to Target %d\n",
 	     HostAdapter->HostNumber, CCB->SerialNumber, TargetID);
       Result = SCSI_ABORT_PENDING;
     }
   else
     {
-      printk("scsi%d: Unable to Abort CCB #%d to Target %d - "
+      printk("scsi%d: Unable to Abort CCB #%ld to Target %d - "
 	     "No Outgoing Mailboxes\n", HostAdapter->HostNumber,
 	     CCB->SerialNumber, TargetID);
       Result = SCSI_ABORT_BUSY;
@@ -2827,7 +2832,7 @@
 				       SCSI_Command_T *Command,
 				       unsigned int ResetFlags)
 {
-  unsigned char TargetID = Command->target;
+  int TargetID = Command->target;
   BusLogic_Lock_T Lock;
   BusLogic_CCB_T *CCB;
   int Result = -1;
@@ -2913,7 +2918,7 @@
   */
   CCB = BusLogic_AllocateCCB(HostAdapter);
   if (CCB == NULL) goto Done;
-  printk("scsi%d: Sending Bus Device Reset CCB #%d to Target %d\n",
+  printk("scsi%d: Sending Bus Device Reset CCB #%ld to Target %d\n",
 	 HostAdapter->HostNumber, CCB->SerialNumber, TargetID);
   CCB->Opcode = BusLogic_BusDeviceReset;
   CCB->TargetID = TargetID;
@@ -2984,9 +2989,8 @@
 {
   BusLogic_HostAdapter_T *HostAdapter =
     (BusLogic_HostAdapter_T *) Command->host->hostdata;
-  unsigned char TargetID = Command->target;
-  unsigned char ErrorRecoveryStrategy =
-    HostAdapter->ErrorRecoveryStrategy[TargetID];
+  int TargetID = Command->target;
+  int ErrorRecoveryStrategy = HostAdapter->ErrorRecoveryStrategy[TargetID];
   /*
     Disable Tagged Queuing if it is active for this Target Device and if
     it has been less than 10 minutes since the last reset occurred, or since
@@ -3255,7 +3259,8 @@
   BusLogic_CommandLineEntry_T *CommandLineEntry =
     &BusLogic_CommandLineEntries[BusLogic_CommandLineEntryCount++];
   static int ProbeListIndex = 0;
-  int IntegerCount = Integers[0], TargetID, i;
+  int IntegerCount = Integers[0];
+  int TargetID, i;
   CommandLineEntry->IO_Address = 0;
   CommandLineEntry->TaggedQueueDepth = 0;
   CommandLineEntry->BusSettleTime = 0;
@@ -3269,7 +3274,7 @@
     printk("BusLogic: Unexpected Command Line Integers ignored\n");
   if (IntegerCount >= 1)
     {
-      unsigned short IO_Address = Integers[1];
+      unsigned int IO_Address = Integers[1];
       if (IO_Address > 0)
 	{
 	  for (i = 0; ; i++)
diff -u --recursive --new-file pre2.0.12/linux/drivers/scsi/BusLogic.h linux/drivers/scsi/BusLogic.h
--- pre2.0.12/linux/drivers/scsi/BusLogic.h	Fri May 17 15:32:15 1996
+++ linux/drivers/scsi/BusLogic.h	Thu Jun  6 02:54:14 1996
@@ -119,12 +119,13 @@
 
 
 /*
-  Define the maximum and default Queue Depth to allow for Target Devices
-  depending on whether or not they support Tagged Queuing and whether or not
-  ISA Bounce Buffers are required.
+  Define the maximum, preferred, and default Queue Depth to allow for Target
+  Devices depending on whether or not they support Tagged Queuing and whether
+  or not ISA Bounce Buffers are required.
 */
 
-#define BusLogic_MaxTaggedQueueDepth		31
+#define BusLogic_MaxTaggedQueueDepth		63
+#define BusLogic_PreferredTaggedQueueDepth	28
 #define BusLogic_TaggedQueueDepth_BB		2
 #define BusLogic_UntaggedQueueDepth		3
 
@@ -192,6 +193,13 @@
 
 
 /*
+  Define a 32 bit bus address data type.
+*/
+
+typedef unsigned int bus_address_t;
+
+
+/*
   Define the BusLogic SCSI Host Adapter I/O Register Offsets.
 */
 
@@ -401,7 +409,7 @@
 typedef struct BusLogic_ExtendedMailboxRequest
 {
   unsigned char MailboxCount;				/* Byte 0 */
-  void *BaseMailboxAddress __attribute__ ((packed));	/* Bytes 1-4 */
+  bus_address_t BaseMailboxAddress __attribute__ ((packed)); /* Bytes 1-4 */
 }
 BusLogic_ExtendedMailboxRequest_T;
 
@@ -466,7 +474,7 @@
   unsigned char BIOS_Address;				/* Byte 1 */
   unsigned short ScatterGatherLimit;			/* Bytes 2-3 */
   unsigned char MailboxCount;				/* Byte 4 */
-  void *BaseMailboxAddress __attribute__ ((packed));	/* Bytes 5-8 */
+  bus_address_t BaseMailboxAddress __attribute__ ((packed)); /* Bytes 5-8 */
   struct { unsigned char :6;				/* Byte 9 Bits 0-5 */
 	   boolean LevelSensitiveInterrupts:1;		/* Byte 9 Bit 6 */
 	   unsigned char :1; } Misc;			/* Byte 9 Bit 7 */
@@ -709,23 +717,14 @@
 
 
 /*
-  Define the SCSI Sense Data.
-*/
-
-#define BusLogic_SenseDataMaxLength		255
-
-typedef unsigned char SCSI_SenseData_T[BusLogic_SenseDataMaxLength];
-
-
-/*
   Define the Scatter/Gather Segment structure required by the Host Adapter
   Firmware Interface.
 */
 
 typedef struct BusLogic_ScatterGatherSegment
 {
-  unsigned long SegmentByteCount;			/* Bytes 0-3 */
-  void *SegmentDataPointer;				/* Bytes 4-7 */
+  unsigned int SegmentByteCount;			/* Bytes 0-3 */
+  bus_address_t SegmentDataPointer;			/* Bytes 4-7 */
 }
 BusLogic_ScatterGatherSegment_T;
 
@@ -754,8 +753,8 @@
   BusLogic_QueueTag_T WideModeQueueTag:2;		/* Byte 1 Bits 6-7 */
   unsigned char CDB_Length;				/* Byte 2 */
   unsigned char SenseDataLength;			/* Byte 3 */
-  unsigned long DataLength;				/* Bytes 4-7 */
-  void *DataPointer;					/* Bytes 8-11 */
+  unsigned int DataLength;				/* Bytes 4-7 */
+  bus_address_t DataPointer;				/* Bytes 8-11 */
   unsigned char :8;					/* Byte 12 */
   unsigned char :8;					/* Byte 13 */
   BusLogic_HostAdapterStatus_T HostAdapterStatus:8;	/* Byte 14 */
@@ -767,8 +766,8 @@
   SCSI_CDB_T CDB;					/* Bytes 18-29 */
   unsigned char :8;					/* Byte 30 */
   unsigned char :8;					/* Byte 31 */
-  unsigned long :32;					/* Bytes 32-35 */
-  SCSI_SenseData_T *SenseDataPointer;			/* Bytes 36-39 */
+  unsigned int :32;					/* Bytes 32-35 */
+  bus_address_t SenseDataPointer;			/* Bytes 36-39 */
   /*
     BusLogic Linux Driver Portion.
   */
@@ -779,7 +778,7 @@
 	 BusLogic_CCB_Completed =   2,
 	 BusLogic_CCB_Reset =	    3 } Status;
   BusLogic_CompletionCode_T MailboxCompletionCode;
-  unsigned int SerialNumber;
+  unsigned long SerialNumber;
   struct BusLogic_CCB *Next;
   struct BusLogic_CCB *NextAll;
   BusLogic_ScatterGatherSegment_T
@@ -794,8 +793,8 @@
 
 typedef struct BusLogic_OutgoingMailbox
 {
-  BusLogic_CCB_T *CCB;					/* Bytes 0-3 */
-  unsigned long :24;					/* Byte 4 */
+  bus_address_t CCB;					/* Bytes 0-3 */
+  unsigned int :24;					/* Byte 4 */
   BusLogic_ActionCode_T ActionCode:8;			/* Bytes 5-7 */
 }
 BusLogic_OutgoingMailbox_T;
@@ -807,7 +806,7 @@
 
 typedef struct BusLogic_IncomingMailbox
 {
-  BusLogic_CCB_T *CCB;					/* Bytes 0-3 */
+  bus_address_t CCB;					/* Bytes 0-3 */
   BusLogic_HostAdapterStatus_T HostAdapterStatus:8;	/* Byte 4 */
   BusLogic_TargetDeviceStatus_T TargetDeviceStatus:8;	/* Byte 5 */
   unsigned char :8;					/* Byte 6 */
@@ -842,7 +841,7 @@
 
 typedef struct BusLogic_CommandLineEntry
 {
-  unsigned short IO_Address;
+  unsigned int IO_Address;
   unsigned short TaggedQueueDepth;
   unsigned short BusSettleTime;
   unsigned short LocalOptions;
@@ -860,12 +859,12 @@
 typedef struct BusLogic_HostAdapter
 {
   SCSI_Host_T *SCSI_Host;
+  unsigned int IO_Address;
   unsigned char HostNumber;
   unsigned char ModelName[9];
   unsigned char FirmwareVersion[6];
   unsigned char BoardName[18];
   unsigned char InterruptLabel[62];
-  unsigned short IO_Address;
   unsigned char IRQ_Channel;
   unsigned char DMA_Channel;
   unsigned char SCSI_ID;
@@ -901,7 +900,7 @@
   unsigned short LocalOptions;
   unsigned short DisconnectPermitted;
   unsigned short TaggedQueuingPermitted;
-  unsigned long BIOS_Address;
+  bus_address_t BIOS_Address;
   BusLogic_InstalledDevices_T InstalledDevices;
   BusLogic_SynchronousValues_T SynchronousValues;
   BusLogic_SynchronousPeriod_T SynchronousPeriod;
@@ -1060,6 +1059,22 @@
   sti();
   while (jiffies < TimeoutJiffies) ;
   restore_flags(ProcessorFlags);
+}
+
+
+/*
+  Virtual_to_Bus and Bus_to_Virtual map between Kernel Virtual Addresses
+  and PCI/VLB/EISA/ISA Bus Addresses.
+*/
+
+static inline bus_address_t Virtual_to_Bus(void *VirtualAddress)
+{
+  return (bus_address_t) virt_to_bus(VirtualAddress);
+}
+
+static inline void *Bus_to_Virtual(bus_address_t BusAddress)
+{
+  return (void *) bus_to_virt(BusAddress);
 }
 
 
diff -u --recursive --new-file pre2.0.12/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- pre2.0.12/linux/drivers/scsi/Config.in	Wed Jun  5 10:41:28 1996
+++ linux/drivers/scsi/Config.in	Wed Jun  5 14:42:26 1996
@@ -15,15 +15,13 @@
 comment 'SCSI low-level drivers'
 
 dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI
-dep_tristate 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X $CONFIG_SCSI
+dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI
 dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI
 dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
 dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
 dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
 dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
-if [ "$CONFIG_PCI" = "y" ]; then
-  bool 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974
-fi
+bool 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974
 dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI
 dep_tristate 'DTC3180/3280 SCSI support' CONFIG_SCSI_DTC3280 $CONFIG_SCSI
 dep_tristate 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA $CONFIG_SCSI
diff -u --recursive --new-file pre2.0.12/linux/drivers/scsi/README.BusLogic linux/drivers/scsi/README.BusLogic
--- pre2.0.12/linux/drivers/scsi/README.BusLogic	Fri May 17 15:32:16 1996
+++ linux/drivers/scsi/README.BusLogic	Thu Jun  6 02:54:14 1996
@@ -1,9 +1,9 @@
 		  BusLogic MultiMaster SCSI Driver for Linux
 
-		       Version 1.2.3 for Linux 1.2.13
-		       Version 2.0.3 for Linux 2.0.0
+		       Version 1.2.4 for Linux 1.2.13
+		       Version 2.0.4 for Linux 2.0.0
 
-				  17 May 1996
+				  5 June 1996
 
 			       Leonard N. Zubkoff
 			       Dandelion Digital
@@ -325,7 +325,7 @@
 			     INSTALLATION
 
 This distribution was prepared for Linux kernel version 1.2.13
-(BusLogic-1.2.3.tar.gz) or Linux kernel version 2.0.0 (BusLogic-2.0.3.tar.gz).
+(BusLogic-1.2.4.tar.gz) or Linux kernel version 2.0.0 (BusLogic-2.0.4.tar.gz).
 Installation in later versions will probably be successful as well, though
 BusLogic.patch may not be required.  Installation in earlier versions is not
 recommended.
@@ -335,7 +335,7 @@
 (substitute "1.2" or "2.0" for "x.y" in the tar command as appropriate):
 
   cd /usr/src
-  tar -xvzf BusLogic-x.y.3.tar.gz
+  tar -xvzf BusLogic-x.y.4.tar.gz
   mv README.* BusLogic.[ch] linux/drivers/scsi
   patch -p < BusLogic.patch	    (on Linux 1.2.13 only)
   patch -p < BusLogic.elf_patch	    (on Linux 1.2.13 ELF systems only)
diff -u --recursive --new-file pre2.0.12/linux/drivers/scsi/in2000.readme linux/drivers/scsi/in2000.readme
--- pre2.0.12/linux/drivers/scsi/in2000.readme	Sat May 11 10:42:06 1996
+++ linux/drivers/scsi/in2000.readme	Thu Jun  6 13:00:20 1996
@@ -97,6 +97,48 @@
 Regardless, you can choose your own default through the command-
 line with the 'period' keyword.
 
+
+------------------------------------------------
+***********  DIP switch settings  **************
+------------------------------------------------
+
+   sw1-1 sw1-2    BIOS address (hex)
+   -----------------------------------------
+    off   off     C8000 - CBFF0
+    on    off     D8000 - DBFF0
+    off   on      D0000 - D3FF0
+    on    on      BIOS disabled
+
+   sw1-3 sw1-4    IO port address (hex)
+   ------------------------------------
+    off   off     220 - 22F
+    on    off     200 - 20F
+    off   on      110 - 11F
+    on    on      100 - 10F
+
+   sw1-5 sw1-6 sw1-7    Interrupt
+   ------------------------------
+    off   off   off     15
+    off   on    off     14
+    off   off   on      11
+    off   on    on      10
+    on    -     -       disabled
+
+   sw1-8 function depends on BIOS version. In earlier versions this
+   controlled synchronous data transfer support for MSDOS:
+      off = disabled
+      on  = enabled
+   In later ROMs (starting with 01.3 in April 1994) sw1-8 controls
+   the "greater than 2 disk drive" feature that first appeared in
+   MSDOS 5.0 (ignored by linux):
+      off = 2 drives maximum
+      on  = 7 drives maximum
+
+   sw1-9    Floppy controller
+   --------------------------
+    off     disabled
+    on      enabled
+
 ------------------------------------------------
 
    I should mention that Drew Eckhardt's 'Generic NCR5380' sources
diff -u --recursive --new-file pre2.0.12/linux/drivers/scsi/qlogicisp.c linux/drivers/scsi/qlogicisp.c
--- pre2.0.12/linux/drivers/scsi/qlogicisp.c	Wed Jun  5 10:41:28 1996
+++ linux/drivers/scsi/qlogicisp.c	Wed Jun  5 13:41:03 1996
@@ -48,10 +48,10 @@
 #include <linux/types.h>
 #include <linux/bios32.h>
 #include <linux/pci.h>
-#include <unistd.h>
+#include <linux/delay.h>
+#include <linux/unistd.h>
 #include <asm/io.h>
 #include <asm/irq.h>
-#include <asm/delay.h>
 
 #include "sd.h"
 #include "hosts.h"
@@ -71,10 +71,6 @@
 
 #define USE_NVRAM_DEFAULTS	0
 
-/*  Set this macro to 1 if you want to create a scsi loadable module. */
-
-#define MODULE			0
-
 /*  Macros used for debugging */
 
 #define DEBUG_ISP1020		0
@@ -1681,8 +1677,8 @@
     return (sts->scsi_status & STATUS_MASK) | (host_status << 16);
 }
 
-#if MODULE
-Scsi_Host_Template driver_template = ISP1020;
+#ifdef MODULE
+Scsi_Host_Template driver_template = QLOGICISP;
 
 #include "scsi_module.c"
 #endif /* MODULE */
diff -u --recursive --new-file pre2.0.12/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- pre2.0.12/linux/drivers/scsi/scsi.h	Mon May 20 08:21:02 1996
+++ linux/drivers/scsi/scsi.h	Thu Jun  6 13:42:15 1996
@@ -21,6 +21,8 @@
  */
 #include <scsi/scsi.h>
 
+#include <linux/random.h>
+
 
 /*
  * Some defs, in case these are not defined elsewhere.
@@ -526,6 +528,7 @@
     if (req->sem != NULL) {
 	up(req->sem);
     }
+    add_blkdev_randomness(MAJOR(req->rq_dev));
     
     if (SCpnt->host->block) {
 	struct Scsi_Host * next;
diff -u --recursive --new-file pre2.0.12/linux/fs/Config.in linux/fs/Config.in
--- pre2.0.12/linux/fs/Config.in	Mon Jun  3 16:46:56 1996
+++ linux/fs/Config.in	Wed Jun  5 13:24:45 1996
@@ -28,6 +28,9 @@
     fi
   fi
   tristate 'SMB filesystem support (to mount WfW shares etc..)' CONFIG_SMB_FS
+  if [ "$CONFIG_SMB_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    bool 'SMB long filename support (EXPERIMENTAL)' CONFIG_SMB_LONG
+  fi
 fi
 if [ "$CONFIG_IPX" != "n" ]; then
   tristate 'NCP filesystem support (to mount NetWare volumes)' CONFIG_NCP_FS
diff -u --recursive --new-file pre2.0.12/linux/fs/file_table.c linux/fs/file_table.c
--- pre2.0.12/linux/fs/file_table.c	Mon May 13 23:02:49 1996
+++ linux/fs/file_table.c	Thu Jun  6 13:03:48 1996
@@ -101,8 +101,15 @@
 struct file * get_empty_filp(void)
 {
 	int i;
+	int max = max_files;
 	struct file * f;
 
+	/*
+	 * Reserve a few files for the super-user..
+	 */
+	if (current->euid)
+		max -= 10;
+
 	/* if the return is taken, we are in deep trouble */
 	if (!first_file && !grow_files())
 		return NULL;
@@ -117,7 +124,7 @@
 				f->f_version = ++event;
 				return f;
 			}
-	} while (nr_files < max_files && grow_files());
+	} while (nr_files < max && grow_files());
 
 	return NULL;
 }
diff -u --recursive --new-file pre2.0.12/linux/fs/nfs/rpcsock.c linux/fs/nfs/rpcsock.c
--- pre2.0.12/linux/fs/nfs/rpcsock.c	Fri May 17 15:32:18 1996
+++ linux/fs/nfs/rpcsock.c	Wed Jun  5 14:42:26 1996
@@ -302,7 +302,7 @@
 rpc_send(struct rpc_sock *rsock, struct rpc_wait *slot)
 {
 	struct rpc_ioreq *req = slot->w_req;
-	struct iovec	iov[MAX_IOVEC];
+	struct iovec	iov[UIO_MAXIOV];
 
 	if (rsock->shutdown)
 		return -EIO;
@@ -336,7 +336,7 @@
 {
 	struct rpc_wait	*rovr;
 	struct rpc_ioreq *req;
-	struct iovec	iov[MAX_IOVEC];
+	struct iovec	iov[UIO_MAXIOV];
 	u32		xid;
 	int		safe, result;
 
diff -u --recursive --new-file pre2.0.12/linux/fs/read_write.c linux/fs/read_write.c
--- pre2.0.12/linux/fs/read_write.c	Fri May 31 08:02:20 1996
+++ linux/fs/read_write.c	Wed Jun  5 14:42:26 1996
@@ -203,7 +203,7 @@
 	const struct iovec * vector, unsigned long count)
 {
 	size_t tot_len;
-	struct iovec iov[MAX_IOVEC];
+	struct iovec iov[UIO_MAXIOV];
 	int retval, i;
 	IO_fn_t fn;
 
@@ -213,7 +213,7 @@
 	 */
 	if (!count)
 		return 0;
-	if (count > MAX_IOVEC)
+	if (count > UIO_MAXIOV)
 		return -EINVAL;
 	retval = verify_area(VERIFY_READ, vector, count*sizeof(*vector));
 	if (retval)
diff -u --recursive --new-file pre2.0.12/linux/fs/smbfs/dir.c linux/fs/smbfs/dir.c
--- pre2.0.12/linux/fs/smbfs/dir.c	Fri Apr 12 15:52:04 1996
+++ linux/fs/smbfs/dir.c	Wed Jun  5 13:22:47 1996
@@ -937,20 +937,3 @@
 	iput(new_dir);
 	return res;
 }
-	
-/*
- * 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: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff -u --recursive --new-file pre2.0.12/linux/fs/smbfs/file.c linux/fs/smbfs/file.c
--- pre2.0.12/linux/fs/smbfs/file.c	Tue Jan  2 14:18:43 1996
+++ linux/fs/smbfs/file.c	Wed Jun  5 13:22:47 1996
@@ -258,20 +258,3 @@
 	NULL,			/* bmap */
 	NULL			/* truncate */
 };
-
-/*
- * 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: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff -u --recursive --new-file pre2.0.12/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c
--- pre2.0.12/linux/fs/smbfs/inode.c	Fri Apr 12 15:52:04 1996
+++ linux/fs/smbfs/inode.c	Wed Jun  5 13:22:48 1996
@@ -444,20 +444,3 @@
 }
 
 #endif
-
-/*
- * 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: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff -u --recursive --new-file pre2.0.12/linux/fs/smbfs/ioctl.c linux/fs/smbfs/ioctl.c
--- pre2.0.12/linux/fs/smbfs/ioctl.c	Fri Nov 10 07:54:00 1995
+++ linux/fs/smbfs/ioctl.c	Wed Jun  5 13:22:48 1996
@@ -31,21 +31,3 @@
 		return -EINVAL;
 	}
 }
-
-
-/*
- * 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: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff -u --recursive --new-file pre2.0.12/linux/fs/smbfs/mmap.c linux/fs/smbfs/mmap.c
--- pre2.0.12/linux/fs/smbfs/mmap.c	Sun Jan  7 18:27:06 1996
+++ linux/fs/smbfs/mmap.c	Wed Jun  5 13:22:48 1996
@@ -120,20 +120,3 @@
 	vma->vm_ops = &smb_file_mmap;
 	return 0;
 }
-
-/*
- * 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: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff -u --recursive --new-file pre2.0.12/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c
--- pre2.0.12/linux/fs/smbfs/proc.c	Sun Jan 14 16:22:17 1996
+++ linux/fs/smbfs/proc.c	Wed Jun  5 13:22:48 1996
@@ -5,6 +5,7 @@
  *
  */
 
+#include <linux/config.h>
 #include <linux/fs.h>
 #include <linux/smbno.h>
 #include <linux/smb_fs.h>
@@ -1603,6 +1604,7 @@
           { PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"},
           { PROTOCOL_LANMAN1,"LANMAN1.0"},
 #endif
+#ifdef CONFIG_SMB_LONG
 #ifdef LANMAN2
           { PROTOCOL_LANMAN2,"LM1.2X002"},
 #endif
@@ -1610,6 +1612,7 @@
 	  { PROTOCOL_NT1,"NT LM 0.12"},
 	  { PROTOCOL_NT1,"NT LANMAN 1.0"},
 #endif
+#endif
           {-1, NULL} };
 	char dev[] = "A:";
 	int i, plength;
@@ -2026,20 +2029,3 @@
 }
 
 #endif /* DEBUG_SMB > 0 */
-
-/*
- * 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: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff -u --recursive --new-file pre2.0.12/linux/fs/smbfs/sock.c linux/fs/smbfs/sock.c
--- pre2.0.12/linux/fs/smbfs/sock.c	Fri May 17 15:32:18 1996
+++ linux/fs/smbfs/sock.c	Wed Jun  5 13:22:48 1996
@@ -768,20 +768,3 @@
 
 	return result;
 }
-
-/*
- * 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: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff -u --recursive --new-file pre2.0.12/linux/include/asm-i386/resource.h linux/include/asm-i386/resource.h
--- pre2.0.12/linux/include/asm-i386/resource.h	Fri Nov 17 16:05:40 1995
+++ linux/include/asm-i386/resource.h	Thu Jun  6 09:24:31 1996
@@ -14,8 +14,9 @@
 #define RLIMIT_NPROC	6		/* max number of processes */
 #define RLIMIT_NOFILE	7		/* max number of open files */
 #define RLIMIT_MEMLOCK	8		/* max locked-in-memory address space */
+#define RLIMIT_AS	9		/* address space limit */
 
-#define RLIM_NLIMITS	9
+#define RLIM_NLIMITS	10
 
 #ifdef __KERNEL__
 
@@ -29,6 +30,7 @@
 	{ LONG_MAX, LONG_MAX },				\
 	{ MAX_TASKS_PER_USER, MAX_TASKS_PER_USER },	\
 	{ NR_OPEN, NR_OPEN },				\
+	{ LONG_MAX, LONG_MAX },				\
 	{ LONG_MAX, LONG_MAX },				\
 }
 
diff -u --recursive --new-file pre2.0.12/linux/include/linux/baycom.h linux/include/linux/baycom.h
--- pre2.0.12/linux/include/linux/baycom.h	Fri May 17 15:32:18 1996
+++ linux/include/linux/baycom.h	Thu Jun  6 14:22:57 1996
@@ -53,6 +53,11 @@
 #define KISS_CMD_FULLDUP    5
 
 /*
+ * use bottom halves? (HDLC processing done with interrupts on or off)
+ */
+#define BAYCOM_USE_BH
+
+/*
  * modem types
  */
 
diff -u --recursive --new-file pre2.0.12/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- pre2.0.12/linux/include/linux/interrupt.h	Mon Jun  3 16:46:58 1996
+++ linux/include/linux/interrupt.h	Thu Jun  6 12:59:42 1996
@@ -33,6 +33,7 @@
 	DIGI_BH,
 	SERIAL_BH,
 	RISCOM8_BH,
+	BAYCOM_BH,
 	NET_BH,
 	IMMEDIATE_BH,
 	KEYBOARD_BH,
diff -u --recursive --new-file pre2.0.12/linux/include/linux/mcdx.h linux/include/linux/mcdx.h
--- pre2.0.12/linux/include/linux/mcdx.h	Tue May 21 19:52:38 1996
+++ linux/include/linux/mcdx.h	Wed Jun  5 10:47:05 1996
@@ -1,7 +1,7 @@
 /*
  * Definitions for the Mitsumi CDROM interface
- * (H) Hackright 1996 by Marcin Dalecki <dalecki@namu03.gwdg.de>
- * VERSION: 2.5
+ * Copyright (C) 1995 1996 Heiko Schlittermann <heiko@lotte.sax.de>
+ * VERSION: @VERSION@
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -16,102 +16,169 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; see the file COPYING.  If not, write to
  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Thanks to
+ *  The Linux Community at all and ...
+ *  Martin Harris (he wrote the first Mitsumi Driver)
+ *  Eberhard Moenkeberg (he gave me much support and the initial kick)
+ *  Bernd Huebner, Ruediger Helsch (Unifix-Software Gmbh, they
+ *      improved the original driver)
+ *  Jon Tombs, Bjorn Ekwall (module support)
+ *  Daniel v. Mosnenck (he sent me the Technical and Programming Reference)
+ *  Gerd Knorr (he lent me his PhotoCD)
+ *  Nils Faerber and Roger E. Wolff (extensivly tested the LU portion)
+ *  Andreas Kies (testing the mysterious hang up's)
+ *  ... somebody forgotten?
+ *  Marcin Dalecki
+ *  
  */
 
-#ifndef __MCDX_H
-#define __MCDX_H
 /*
- * 	PLEASE CONFIGURE THIS ACCORDING TO YOUR HARDWARE/JUMPER SETTINGS.
+ *	The following lines are for user configuration
+ *	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  *
- *      o       MCDX_NDRIVES  :  number of used entries of the following table
- *      o       MCDX_DRIVEMAP :  table of {i/o base, irq} per controller
+ *	{0|1} -- 1 if you want the driver detect your drive, may crash and
+ *	needs a long time to seek.  The higher the address the longer the
+ *	seek.
  *
- *      NOTE: Don't even think about connecting the drive to IRQ 9(2).
- *	In the AT architecture this interrupt is used to cascade the two
- *	interrupt controllers and isn't therefore usable for anything else!
- */
- /* #define I_WAS_IN_MCDX_H */
-#define MCDX_NDRIVES 1
-#define MCDX_DRIVEMAP {	{0x230, 11},	\
+ *  WARNING: AUTOPROBE doesn't work.
+ */
+#define MCDX_AUTOPROBE 0
+
+/*
+ *	Drive specific settings according to the jumpers on the controller
+ *	board(s).
+ *	o	MCDX_NDRIVES  :  number of used entries of the following table
+ *	o	MCDX_DRIVEMAP :  table of {i/o base, irq} per controller
+ *
+ *	NOTE: I didn't get a drive at irq 9(2) working.  Not even alone.
+ */
+#if MCDX_AUTOPROBE == 0
+	#define MCDX_NDRIVES 1
+	#define MCDX_DRIVEMAP {		\
+			{0x300, 11},	\
 			{0x304, 05},  	\
 			{0x000, 00},  	\
 			{0x000, 00},  	\
 			{0x000, 00},  	\
-}
-	  	
-/* 
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!NO USER INTERVENTION NEEDED BELOW
- * If You are sure that all configuration is done, please uncomment the
- * line below. 
- */
-
-#undef MCDX_DEBUG	/* This is *REALLY* only for development! */
-
-#ifdef MCDX_DEBUG
-#define MCDX_TRACE(x) printk x
-#define MCDX_TRACE_IOCTL(x) printk x
+	  	}
 #else
-#define MCDX_TRACE(x)
-#define MCDX_TRACE_IOCTL(x)
+	#error Autoprobing is not implemented yet.
 #endif
 
-/*      The name of the device */
-#define MCDX "mcdx"
+#ifndef MCDX_QUIET
+#define MCDX_QUIET   1
+#endif
 
-/*
- *      Per controller 4 bytes i/o are needed. 
+#ifndef MCDX_DEBUG
+#define MCDX_DEBUG   0
+#endif
+
+/* *** make the following line uncommented, if you're sure,
+ * *** all configuration is done */
+/* #define I_WAS_HERE */
+#define I_WAS_HERE   /* delete this line, it's for heiko only */
+
+/*	The name of the device */
+#define MCDX "mcdx"	
+
+/* Flags for DEBUGGING */
+#define INIT 		0
+#define MALLOC 		0
+#define IOCTL 		0
+#define PLAYTRK     0
+#define SUBCHNL     0
+#define TOCHDR      0
+#define MS          0
+#define PLAYMSF     0
+#define READTOC     0
+#define OPENCLOSE 	0
+#define HW		    0
+#define TALK		0
+#define IRQ 		0
+#define XFER 		0
+#define REQUEST	 	0
+#define SLEEP		0
+
+/*	The following addresses are taken from the Mitsumi Reference 
+ *  and describe the possible i/o range for the controller.
  */
+#define MCDX_IO_BEGIN	((char*) 0x300)	/* first base of i/o addr */
+#define MCDX_IO_END		((char*) 0x3fc)	/* last base of i/o addr */
+
+/*	Per controller 4 bytes i/o are needed. */
 #define MCDX_IO_SIZE		4
 
-/* 
- * Masks for the status byte, returned from every command, set if
- * the description is true 
+/*
+ *	Bits
  */
+
+/* The status byte, returned from every command, set if
+ * the description is true */
 #define MCDX_RBIT_OPEN       0x80	/* door is open */
 #define MCDX_RBIT_DISKSET    0x40	/* disk set (recognised) */
 #define MCDX_RBIT_CHANGED    0x20	/* disk was changed */
 #define MCDX_RBIT_CHECK      0x10	/* disk rotates, servo is on */
-#define MCDX_RBIT_AUDIOTR    0x08	/* current track is audio */
+#define MCDX_RBIT_AUDIOTR    0x08   /* current track is audio */
 #define MCDX_RBIT_RDERR      0x04	/* read error, refer SENSE KEY */
 #define MCDX_RBIT_AUDIOBS    0x02	/* currently playing audio */
 #define MCDX_RBIT_CMDERR     0x01	/* command, param or format error */
 
-/* 
- * The I/O Register holding the h/w status of the drive,
- * can be read at i/o base + 1 
- */
+/* The I/O Register holding the h/w status of the drive,
+ * can be read at i/o base + 1 */
 #define MCDX_RBIT_DOOR       0x10	/* door is open */
 #define MCDX_RBIT_STEN       0x04	/* if 0, i/o base contains drive status */
 #define MCDX_RBIT_DTEN       0x02	/* if 0, i/o base contains data */
 
 /*
- *    The commands.
+ *	The commands.
+ */
+
+#define OPCODE	1		/* offset of opcode */
+#define MCDX_CMD_REQUEST_TOC		1, 0x10
+#define MCDX_CMD_REQUEST_STATUS		1, 0x40 
+#define MCDX_CMD_RESET				1, 0x60
+#define MCDX_CMD_REQUEST_DRIVE_MODE	1, 0xc2
+#define MCDX_CMD_SET_INTERLEAVE		2, 0xc8, 0
+#define MCDX_CMD_DATAMODE_SET		2, 0xa0, 0
+	#define MCDX_DATAMODE1		0x01
+	#define MCDX_DATAMODE2		0x02
+#define MCDX_CMD_LOCK_DOOR		2, 0xfe, 0
+
+#define READ_AHEAD			4	/* 8 Sectors (4K) */
+
+/*	Useful macros */
+#define e_door(x)		((x) & MCDX_RBIT_OPEN)
+#define e_check(x)		(~(x) & MCDX_RBIT_CHECK)
+#define e_notset(x)		(~(x) & MCDX_RBIT_DISKSET)
+#define e_changed(x)	((x) & MCDX_RBIT_CHANGED)
+#define e_audio(x)		((x) & MCDX_RBIT_AUDIOTR)
+#define e_audiobusy(x)	((x) & MCDX_RBIT_AUDIOBS)
+#define e_cmderr(x)		((x) & MCDX_RBIT_CMDERR)
+#define e_readerr(x)	((x) & MCDX_RBIT_RDERR)
+
+/**	no drive specific */
+#define MCDX_CDBLK	2048	/* 2048 cooked data each blk */
+
+#define MCDX_DATA_TIMEOUT	(HZ/10)	/* 0.1 second */
+
+/*
+ * Access to the msf array
  */
-#define MCDX_CMD_GET_TOC		0x10
-#define MCDX_CMD_GET_MDISK_INFO		0x11
-#define MCDX_CMD_GET_SUBQ_CODE		0x20
-#define MCDX_CMD_GET_STATUS		0x40
-#define MCDX_CMD_SET_DRIVE_MODE		0x50
-#define MCDX_CMD_RESET			0x60
-#define MCDX_CMD_HOLD			0x70
-#define MCDX_CMD_CONFIG			0x90
-#define MCDX_CMD_SET_ATTENATOR		0xae
-#define MCDX_CMD_PLAY			0xc0
-#define MCDX_CMD_PLAY_2X		0xc1
-#define MCDX_CMD_GET_DRIVE_MODE		0xc2
-#define MCDX_CMD_SET_INTERLEAVE		0xc8
-#define MCDX_CMD_GET_FIRMWARE		0xdc
-#define MCDX_CMD_SET_DATA_MODE		0xa0
-#define MCDX_CMD_STOP			0xf0
-#define MCDX_CMD_EJECT			0xf6
-#define MCDX_CMD_CLOSE_DOOR		0xf8
-#define MCDX_CMD_LOCK_DOOR		0xfe
+#define MSF_MIN		0			/* minute */
+#define MSF_SEC		1			/* second */
+#define MSF_FRM		2			/* frame  */
 
-#define READ_AHEAD			8	/* 16 Sectors (4K) */
+/*
+ * Errors
+ */
+#define MCDX_E		1			/* unspec error */
+#define MCDX_ST_EOM 0x0100		/* end of media */
+#define MCDX_ST_DRV 0x00ff		/* mask to query the drive status */
 
-#ifndef I_WAS_IN_MCDX_H
+#ifndef I_WAS_HERE
 #warning You have not edited mcdx.h
 #warning Perhaps irq and i/o settings are wrong.
 #endif
 
-#endif 	/* __MCDX_H */
+/* ex:set ts=4 sw=4: */
diff -u --recursive --new-file pre2.0.12/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- pre2.0.12/linux/include/linux/netdevice.h	Sat Jun  1 20:11:36 1996
+++ linux/include/linux/netdevice.h	Thu Jun  6 16:35:37 1996
@@ -28,7 +28,6 @@
 #include <linux/if.h>
 #include <linux/if_ether.h>
 #include <linux/skbuff.h>
-#include <linux/interrupt.h>
 
 /* for future expansion when we will have different priorities. */
 #define DEV_NUMBUFFS	3
@@ -205,6 +204,7 @@
 
 #ifdef __KERNEL__
 
+#include <linux/interrupt.h>
 #include <linux/notifier.h>
 
 /* Used by dev_rint */
diff -u --recursive --new-file pre2.0.12/linux/include/linux/random.h linux/include/linux/random.h
--- pre2.0.12/linux/include/linux/random.h	Sat May 11 10:42:07 1996
+++ linux/include/linux/random.h	Thu Jun  6 13:42:15 1996
@@ -53,6 +53,9 @@
 
 extern void get_random_bytes(void *buf, int nbytes);
 
+extern __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
+					__u16 sport, __u16 dport);
+
 #ifndef MODULE
 extern struct file_operations random_fops, urandom_fops;
 #endif
diff -u --recursive --new-file pre2.0.12/linux/include/linux/rpcsock.h linux/include/linux/rpcsock.h
--- pre2.0.12/linux/include/linux/rpcsock.h	Fri Apr 12 15:52:08 1996
+++ linux/include/linux/rpcsock.h	Wed Jun  5 14:42:27 1996
@@ -53,10 +53,10 @@
 	struct rpc_wait *	rq_slot;
 	struct sockaddr	*	rq_addr;
 	int			rq_alen;
-	struct iovec		rq_svec[MAX_IOVEC];
+	struct iovec		rq_svec[UIO_MAXIOV];
 	unsigned int		rq_snr;
 	unsigned long		rq_slen;
-	struct iovec		rq_rvec[MAX_IOVEC];
+	struct iovec		rq_rvec[UIO_MAXIOV];
 	unsigned int		rq_rnr;
 	unsigned long		rq_rlen;
 };
diff -u --recursive --new-file pre2.0.12/linux/include/linux/sbpcd.h linux/include/linux/sbpcd.h
--- pre2.0.12/linux/include/linux/sbpcd.h	Wed Feb  7 08:17:50 1996
+++ linux/include/linux/sbpcd.h	Wed Jun  5 09:01:46 1996
@@ -108,6 +108,7 @@
 
 #if DISTRIBUTION
 #define READ_AUDIO 0
+#define KLOGD_PAUSE 55
 #else
 /* max. number of audio frames to read with one     */
 /* request (allocates n* 2352 bytes kernel memory!) */
@@ -115,6 +116,14 @@
 /* runtime by use of the CDROMAUDIOBUFSIZ ioctl.    */
 #define READ_AUDIO 75
 
+/*
+ * Time to wait after giving a message.
+ * This gets important if you enable non-standard DBG_xxx flags.
+ * You will see what happens if you omit the pause or make it
+ * too short. Be warned!
+ */
+#define KLOGD_PAUSE 1
+
 /* tray control: eject tray if no disk is in (0 or 1) */
 #define JUKEBOX 1
 
@@ -134,6 +143,7 @@
 /*==========================================================================*/
 #define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */
 #undef  FUTURE
+#undef SAFE_MIXED
 
 #define TEST_UPC 0
 #define SPEA_TEST 0
diff -u --recursive --new-file pre2.0.12/linux/include/linux/smb.h linux/include/linux/smb.h
--- pre2.0.12/linux/include/linux/smb.h	Sun Mar 24 13:41:06 1996
+++ linux/include/linux/smb.h	Wed Jun  5 13:22:48 1996
@@ -104,21 +104,3 @@
 
 #endif  /* __KERNEL__ */
 #endif  /* _LINUX_SMB_H */
-
-
-/*
- * 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: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff -u --recursive --new-file pre2.0.12/linux/include/linux/smb_fs_i.h linux/include/linux/smb_fs_i.h
--- pre2.0.12/linux/include/linux/smb_fs_i.h	Sun Mar 24 13:41:07 1996
+++ linux/include/linux/smb_fs_i.h	Wed Jun  5 13:22:48 1996
@@ -32,20 +32,3 @@
 
 #endif
 #endif
-
-/*
- * 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: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff -u --recursive --new-file pre2.0.12/linux/include/linux/smb_mount.h linux/include/linux/smb_mount.h
--- pre2.0.12/linux/include/linux/smb_mount.h	Mon Mar 25 10:26:15 1996
+++ linux/include/linux/smb_mount.h	Wed Jun  5 13:22:48 1996
@@ -36,5 +36,3 @@
 };
 
 #endif
-
-
diff -u --recursive --new-file pre2.0.12/linux/include/linux/uio.h linux/include/linux/uio.h
--- pre2.0.12/linux/include/linux/uio.h	Tue Aug  8 09:20:36 1995
+++ linux/include/linux/uio.h	Wed Jun  5 14:42:27 1996
@@ -12,7 +12,7 @@
 
 
 /* A word of warning: Our uio structure will clash with the C library one (which is now obsolete). Remove the C
-   library one from sys/uio.h */
+   library one from sys/uio.h if you have a very old library set */
 
 struct iovec
 {
@@ -20,6 +20,7 @@
 	int iov_len;
 };
 
-#define MAX_IOVEC	8	/* Maximum iovec's in one operation */
+#define UIO_MAXIOV	16	/* Maximum iovec's in one operation 
+				   16 matches BSD */
 
 #endif
diff -u --recursive --new-file pre2.0.12/linux/include/net/ip_masq.h linux/include/net/ip_masq.h
--- pre2.0.12/linux/include/net/ip_masq.h	Mon Jun  3 16:46:58 1996
+++ linux/include/net/ip_masq.h	Thu Jun  6 16:39:31 1996
@@ -26,8 +26,12 @@
 #define IP_MASQ_F_NO_DPORT    		0x04	/* no dport set yet */
 #define IP_MASQ_F_NO_DADDR      	0x08 	/* no daddr yet */
 #define IP_MASQ_F_HASHED		0x10 	/* hashed entry */
-#define IP_MASQ_F_SAW_FIN		0x20 	/* tcp fin pkt seen */
-#define IP_MASQ_F_SAW_RST		0x40 	/* tcp rst pkt seen */
+#define IP_MASQ_F_SAW_RST		0x20 	/* tcp rst pkt seen */
+#define IP_MASQ_F_SAW_FIN_IN		0x40 	/* tcp fin pkt seen incoming */
+#define IP_MASQ_F_SAW_FIN_OUT		0x80 	/* tcp fin pkt seen outgoing */
+#define IP_MASQ_F_SAW_FIN		(IP_MASQ_F_SAW_FIN_IN | \
+					 IP_MASQ_F_SAW_FIN_OUT)
+						/* tcp fin pkts seen */
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file pre2.0.12/linux/include/net/sock.h linux/include/net/sock.h
--- pre2.0.12/linux/include/net/sock.h	Wed Jun  5 10:41:29 1996
+++ linux/include/net/sock.h	Thu Jun  6 16:35:42 1996
@@ -214,7 +214,8 @@
 	__u32                   lastwin_seq;    /* sequence number when we last updated the window we offer */
 	__u32			high_seq;	/* sequence number when we did current fast retransmit */
 	volatile unsigned long  ato;            /* ack timeout */
-	volatile unsigned long  lrcvtime;       /* jiffies at last rcv */
+	volatile unsigned long  lrcvtime;       /* jiffies at last data rcv */
+	volatile unsigned long  idletime;       /* jiffies at last rcv */
 	unsigned short		bytes_rcv;
 /*
  *	mss is min(mtu, max_window) 
@@ -391,7 +392,7 @@
 #define SOCK_DESTROY_TIME (10*HZ)
 
 /*
- *	Sockets 0-1023 can't be bound too unless you are superuser 
+ *	Sockets 0-1023 can't be bound to unless you are superuser 
  */
  
 #define PROT_SOCK	1024
diff -u --recursive --new-file pre2.0.12/linux/kernel/sys.c linux/kernel/sys.c
--- pre2.0.12/linux/kernel/sys.c	Mon May 13 23:02:50 1996
+++ linux/kernel/sys.c	Thu Jun  6 09:28:51 1996
@@ -18,8 +18,6 @@
 #include <linux/stat.h>
 #include <linux/mman.h>
 #include <linux/mm.h>
-#include <linux/pagemap.h>
-#include <linux/swap.h>
 #include <linux/fcntl.h>
 #include <linux/acct.h>
 #include <linux/tty.h>
@@ -548,70 +546,6 @@
 		put_user(current->cstime,&tbuf->tms_cstime);
 	}
 	return jiffies;
-}
-
-asmlinkage unsigned long sys_brk(unsigned long brk)
-{
-	int freepages;
-	unsigned long rlim;
-	unsigned long newbrk, oldbrk;
-
-	if (brk < current->mm->end_code)
-		return current->mm->brk;
-	newbrk = PAGE_ALIGN(brk);
-	oldbrk = PAGE_ALIGN(current->mm->brk);
-	if (oldbrk == newbrk)
-		return current->mm->brk = brk;
-
-	/*
-	 * Always allow shrinking brk
-	 */
-	if (brk <= current->mm->brk) {
-		current->mm->brk = brk;
-		do_munmap(newbrk, oldbrk-newbrk);
-		return brk;
-	}
-	/*
-	 * Check against rlimit and stack..
-	 */
-	rlim = current->rlim[RLIMIT_DATA].rlim_cur;
-	if (rlim >= RLIM_INFINITY)
-		rlim = ~0;
-	if (brk - current->mm->end_code > rlim)
-		return current->mm->brk;
-	/*
-	 * Check against existing mmap mappings.
-	 */
-	if (find_vma_intersection(current, oldbrk, newbrk+PAGE_SIZE))
-		return current->mm->brk;
-	/*
-	 * stupid algorithm to decide if we have enough memory: while
-	 * simple, it hopefully works in most obvious cases.. Easy to
-	 * fool it, but this should catch most mistakes.
-	 */
-	freepages = buffermem >> PAGE_SHIFT;
-	freepages += page_cache_size;
-	freepages >>= 1;
-	freepages += nr_free_pages;
-	freepages += nr_swap_pages;
-	freepages -= MAP_NR(high_memory) >> 4;
-	freepages -= (newbrk-oldbrk) >> PAGE_SHIFT;
-	if (freepages < 0)
-		return current->mm->brk;
-#if 0
-	freepages += current->mm->rss;
-	freepages -= oldbrk >> 12;
-	if (freepages < 0)
-		return current->mm->brk;
-#endif
-	/*
-	 * Ok, we have probably got enough memory - let it rip.
-	 */
-	current->mm->brk = brk;
-	do_mmap(NULL, oldbrk, newbrk-oldbrk,
-		PROT_READ|PROT_WRITE|PROT_EXEC,
-		MAP_FIXED|MAP_PRIVATE, 0);
-	return brk;
 }
 
 /*
diff -u --recursive --new-file pre2.0.12/linux/mm/mmap.c linux/mm/mmap.c
--- pre2.0.12/linux/mm/mmap.c	Wed Jun  5 10:41:29 1996
+++ linux/mm/mmap.c	Thu Jun  6 09:29:59 1996
@@ -12,6 +12,8 @@
 #include <linux/mman.h>
 #include <linux/string.h>
 #include <linux/malloc.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -40,6 +42,78 @@
 };
 
 /*
+ * Check that a process has enough memory to allocate a
+ * new virtual mapping.
+ */
+static inline int vm_enough_memory(long pages)
+{
+	/*
+	 * stupid algorithm to decide if we have enough memory: while
+	 * simple, it hopefully works in most obvious cases.. Easy to
+	 * fool it, but this should catch most mistakes.
+	 */
+	long freepages;
+	freepages = buffermem >> PAGE_SHIFT;
+	freepages += page_cache_size;
+	freepages >>= 1;
+	freepages += nr_free_pages;
+	freepages += nr_swap_pages;
+	freepages -= MAP_NR(high_memory) >> 4;
+	return freepages > pages;
+}
+
+asmlinkage unsigned long sys_brk(unsigned long brk)
+{
+	unsigned long rlim;
+	unsigned long newbrk, oldbrk;
+
+	if (brk < current->mm->end_code)
+		return current->mm->brk;
+	newbrk = PAGE_ALIGN(brk);
+	oldbrk = PAGE_ALIGN(current->mm->brk);
+	if (oldbrk == newbrk)
+		return current->mm->brk = brk;
+
+	/*
+	 * Always allow shrinking brk
+	 */
+	if (brk <= current->mm->brk) {
+		current->mm->brk = brk;
+		do_munmap(newbrk, oldbrk-newbrk);
+		return brk;
+	}
+	/*
+	 * Check against rlimit and stack..
+	 */
+	rlim = current->rlim[RLIMIT_DATA].rlim_cur;
+	if (rlim >= RLIM_INFINITY)
+		rlim = ~0;
+	if (brk - current->mm->end_code > rlim)
+		return current->mm->brk;
+
+	/*
+	 * Check against existing mmap mappings.
+	 */
+	if (find_vma_intersection(current, oldbrk, newbrk+PAGE_SIZE))
+		return current->mm->brk;
+
+	/*
+	 * Check if we have enough memory..
+	 */
+	if (!vm_enough_memory((newbrk-oldbrk) >> PAGE_SHIFT))
+		return current->mm->brk;
+
+	/*
+	 * Ok, looks good - let it rip.
+	 */
+	current->mm->brk = brk;
+	do_mmap(NULL, oldbrk, newbrk-oldbrk,
+		PROT_READ|PROT_WRITE|PROT_EXEC,
+		MAP_FIXED|MAP_PRIVATE, 0);
+	return brk;
+}
+
+/*
  * Combine the mmap "prot" and "flags" argument into one "vm_flags" used
  * internally. Essentially, translate the "PROT_xxx" and "MAP_xxx" bits
  * into "VM_xxx".
@@ -178,6 +252,14 @@
 	vma->vm_pte = 0;
 
 	do_munmap(addr, len);	/* Clear old maps */
+
+	/* Private writable mapping? Check memory availability.. */
+	if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE) {
+		if (!vm_enough_memory(len >> PAGE_SHIFT)) {
+			kfree(vma);
+			return -ENOMEM;
+		}
+	}
 
 	if (file) {
 		int error = file->f_op->mmap(file->f_inode, file, vma);
diff -u --recursive --new-file pre2.0.12/linux/mm/vmscan.c linux/mm/vmscan.c
--- pre2.0.12/linux/mm/vmscan.c	Fri Apr 12 15:52:10 1996
+++ linux/mm/vmscan.c	Wed Jun  5 14:36:35 1996
@@ -334,7 +334,12 @@
 {
 	static int state = 0;
 	int i=6;
+	int stop;
 
+	/* we don't try as hard if we're not waiting.. */
+	stop = 3;
+	if (wait)
+		stop = 0;
 	switch (state) {
 		do {
 		case 0:
@@ -349,7 +354,8 @@
 			if (swap_out(i, dma, wait))
 				return 1;
 			state = 0;
-		} while (i--);
+		i--;
+		} while ((i - stop) >= 0);
 	}
 	return 0;
 }
diff -u --recursive --new-file pre2.0.12/linux/net/Config.in linux/net/Config.in
--- pre2.0.12/linux/net/Config.in	Sun May  5 08:52:05 1996
+++ linux/net/Config.in	Wed Jun  5 14:42:27 1996
@@ -20,9 +20,9 @@
   bool 'AX.25 over Ethernet' CONFIG_BPQETHER
   bool 'Amateur Radio NET/ROM' CONFIG_NETROM
 fi
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  bool 'Bridging (EXPERIMENTAL)' CONFIG_BRIDGE
-fi
+#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
diff -u --recursive --new-file pre2.0.12/linux/net/core/dev.c linux/net/core/dev.c
--- pre2.0.12/linux/net/core/dev.c	Sat Jun  1 20:11:36 1996
+++ linux/net/core/dev.c	Thu Jun  6 09:54:05 1996
@@ -413,14 +413,8 @@
 				dev_kfree_skb(skb, FREE_WRITE);
 				return;
 			}
-			cli();
-			skb_device_unlock(skb);		/* Buffer is on the device queue and can be freed safely */
-			__skb_queue_tail(list, skb);
-			skb = __skb_dequeue(list);
-			skb_device_lock(skb);		/* New buffer needs locking down */
-			restore_flags(flags);
 		}
-		
+
 		/* copy outgoing packets to any sniffer packet handlers */
 		if (dev_nit) {
 			struct packet_type *ptype;
@@ -441,6 +435,15 @@
 					ptype->func(skb2, skb->dev, ptype);
 				}
 			}
+		}
+
+		if (skb_queue_len(list)) {
+			cli();
+			skb_device_unlock(skb);		/* Buffer is on the device queue and can be freed safely */
+			__skb_queue_tail(list, skb);
+			skb = __skb_dequeue(list);
+			skb_device_lock(skb);		/* New buffer needs locking down */
+			restore_flags(flags);
 		}
 	}
 	if (dev->hard_start_xmit(skb, dev) == 0) {
diff -u --recursive --new-file pre2.0.12/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- pre2.0.12/linux/net/core/skbuff.c	Sat Jun  1 20:11:36 1996
+++ linux/net/core/skbuff.c	Thu Jun  6 09:45:39 1996
@@ -283,7 +283,6 @@
 
 void __skb_queue_tail(struct sk_buff_head *list_, struct sk_buff *newsk)
 {
-	unsigned long flags;
 	struct sk_buff *list = (struct sk_buff *)list_;
 
 	if (newsk->next || newsk->prev)
@@ -308,7 +307,7 @@
 
 struct sk_buff *skb_dequeue(struct sk_buff_head *list_)
 {
-	long flags;
+	unsigned long flags;
 	struct sk_buff *result;
 	struct sk_buff *list = (struct sk_buff *)list_;
 
diff -u --recursive --new-file pre2.0.12/linux/net/ipv4/Config.in linux/net/ipv4/Config.in
--- pre2.0.12/linux/net/ipv4/Config.in	Sat Jun  1 20:11:37 1996
+++ linux/net/ipv4/Config.in	Thu Jun  6 12:58:21 1996
@@ -9,9 +9,12 @@
     bool 'IP: firewall packet logging' CONFIG_IP_FIREWALL_VERBOSE
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       bool 'IP: masquerading (EXPERIMENTAL)' CONFIG_IP_MASQUERADE
+      if [ "$CONFIG_IP_MASQUERADE" != "n" ]; then
+        comment 'Protocol-specific masquerading support will be built as modules.'
+      fi
       bool 'IP: transparent proxy support (EXPERIMENTAL)' CONFIG_IP_TRANSPARENT_PROXY
+      bool 'IP: always defragment' CONFIG_IP_ALWAYS_DEFRAG
     fi
-    bool 'IP: always defragment' CONFIG_IP_ALWAYS_DEFRAG
   fi
 fi
 bool 'IP: accounting' CONFIG_IP_ACCT
diff -u --recursive --new-file pre2.0.12/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- pre2.0.12/linux/net/ipv4/arp.c	Mon Jun  3 16:46:59 1996
+++ linux/net/ipv4/arp.c	Wed Jun  5 14:42:27 1996
@@ -2025,36 +2025,31 @@
 
 	while ((entry = *entryp) != NULL)
 	{
-		if (entry->ip == ip && entry->mask == mask && entry->dev == dev)
-			break;
+		/* User supplied arp entries are definitive - RHP 960603 */
+
+		if (entry->ip == ip && entry->mask == mask && entry->dev == dev) {
+			*entryp=entry->next;
+			arp_free_entry(entry);
+			continue;
+		}
 		if ((entry->mask & mask) != mask)
-		{
-			entry = NULL;
 			break;
-		}
 		entryp = &entry->next;
 	}
 
-	/*
-	 *	Do we need to create a new entry?
-	 */
-	
+	entry = arp_alloc_entry();
 	if (entry == NULL)
 	{
-		entry = arp_alloc_entry();
-		if (entry == NULL)
-		{
-			arp_unlock();
-			return -ENOMEM;
-		}
-		entry->ip = ip;
-		entry->dev = dev;
-		entry->mask = mask;
-		entry->flags = r->arp_flags;
-
-		entry->next = *entryp;
-		*entryp = entry;
+		arp_unlock();
+		return -ENOMEM;
 	}
+	entry->ip = ip;
+	entry->dev = dev;
+	entry->mask = mask;
+	entry->flags = r->arp_flags;
+
+	entry->next = *entryp;
+	*entryp = entry;
 
 	ha = r->arp_ha.sa_data;
 	if (empty(ha, dev->addr_len))
diff -u --recursive --new-file pre2.0.12/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- pre2.0.12/linux/net/ipv4/ip_forward.c	Mon Jun  3 16:46:59 1996
+++ linux/net/ipv4/ip_forward.c	Wed Jun  5 14:42:27 1996
@@ -469,7 +469,7 @@
 			 *	Count mapping we shortcut
 			 */
 			 
-			ip_fw_chk(iph,dev2,NULL,ip_acct_chain,IP_FW_F_ACCEPT,IP_FW_MODE_ACCT_OUT);
+			ip_fw_chk(iph,dev2,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
 #endif			
 			
 			/*
diff -u --recursive --new-file pre2.0.12/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- pre2.0.12/linux/net/ipv4/ip_input.c	Sat Jun  1 20:11:37 1996
+++ linux/net/ipv4/ip_input.c	Wed Jun  5 14:42:27 1996
@@ -257,7 +257,7 @@
 	 */
 
 #ifdef CONFIG_IP_ACCT
-	ip_fw_chk(iph,dev,NULL,ip_acct_chain,IP_FW_F_ACCEPT,IP_FW_MODE_ACCT_IN);
+	ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN);
 #endif	
 
 	/*
diff -u --recursive --new-file pre2.0.12/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
--- pre2.0.12/linux/net/ipv4/ip_masq.c	Mon Jun  3 16:46:59 1996
+++ linux/net/ipv4/ip_masq.c	Thu Jun  6 16:23:49 1996
@@ -14,6 +14,7 @@
  *	Juan Jose Ciarlante	:	Added NO_ADDR status flag.
  *	Nigel Metheringham	:	Added ICMP handling for demasquerade
  *	Nigel Metheringham	:	Checksum checking of masqueraded data
+ *	Nigel Metheringham	:	Better handling of timeouts of TCP conns
  *
  *	
  */
@@ -539,20 +540,29 @@
  		struct tcphdr *th;
  		th = (struct tcphdr *)portptr;
 
+		/* Set the flags up correctly... */
+		if (th->fin)
+		{
+			ms->flags |= IP_MASQ_F_SAW_FIN_OUT;
+		}
+
+		if (th->rst)
+		{
+			ms->flags |= IP_MASQ_F_SAW_RST;
+		}
+
  		/*
- 		 *	Timeout depends if FIN packet was seen
+ 		 *	Timeout depends if FIN packet has been seen
 		 *	Very short timeout if RST packet seen.
  		 */
- 		if (ms->flags & IP_MASQ_F_SAW_RST || th->rst)
-                {
+ 		if (ms->flags & IP_MASQ_F_SAW_RST)
+		{
                         timeout = 1;
- 			ms->flags |= IP_MASQ_F_SAW_RST;
- 		}
- 		else if (ms->flags & IP_MASQ_F_SAW_FIN || th->fin)
-                {
+		}
+ 		else if ((ms->flags & IP_MASQ_F_SAW_FIN) == IP_MASQ_F_SAW_FIN)
+		{
                         timeout = ip_masq_expire->tcp_fin_timeout;
- 			ms->flags |= IP_MASQ_F_SAW_FIN;
- 		}
+		}
  		else timeout = ip_masq_expire->tcp_timeout;
 
 		skb->csum = csum_partial((void *)(th + 1), size - sizeof(*th), 0);
@@ -775,6 +785,7 @@
  	__u16	*portptr;
  	struct ip_masq	*ms;
 	unsigned short len;
+	unsigned long 	timeout;
 
 	switch (iph->protocol) {
 	case IPPROTO_ICMP:
@@ -827,6 +838,9 @@
 
         if (ms != NULL)
         {
+		/* Stop the timer ticking.... */
+		ip_masq_set_expire(ms,0);
+
                 /*
                  *	Set dport if not defined yet.
                  */
@@ -869,16 +883,13 @@
 
                 /*
                  * Yug! adjust UDP/TCP and IP checksums, also update
-		 * UDP timeouts since you cannot depend on traffic
-		 * going through the other way to hold the timeout open.
-		 * (With TCP the ACK packets hold the tunnel open).
-		 * If a TCP RST is seen collapse the tunnel!
+		 * timeouts.
+		 * If a TCP RST is seen collapse the tunnel (by using short timeout)!
                  */
                 if (iph->protocol==IPPROTO_UDP)
 		{
                         recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,len);
-			ip_masq_set_expire(ms, 0);
-			ip_masq_set_expire(ms, ip_masq_expire->udp_timeout);
+			timeout = ip_masq_expire->udp_timeout;
 		}
                 else
                 {
@@ -886,16 +897,30 @@
                         skb->csum = csum_partial((void *)(((struct tcphdr *)portptr) + 1),
                                                  len - sizeof(struct tcphdr), 0);
                         tcp_send_check((struct tcphdr *)portptr,iph->saddr,iph->daddr,len,skb);
-			/* Check if TCP RST */
+
+			/* Check if TCP FIN or RST */
 			th = (struct tcphdr *)portptr;
+			if (th->fin)
+			{
+				ms->flags |= IP_MASQ_F_SAW_FIN_IN;
+			}
 			if (th->rst)
 			{
-				ip_masq_set_expire(ms, 0);
 				ms->flags |= IP_MASQ_F_SAW_RST;
-				ip_masq_set_expire(ms, 1);
 			}
-
+			
+			/* Now set the timeouts */
+			if (ms->flags & IP_MASQ_F_SAW_RST)
+			{
+				timeout = 1;
+			}
+			else if ((ms->flags & IP_MASQ_F_SAW_FIN) == IP_MASQ_F_SAW_FIN)
+			{
+				timeout = ip_masq_expire->tcp_fin_timeout;
+			}
+			else timeout = ip_masq_expire->tcp_timeout;
                 }
+		ip_masq_set_expire(ms, timeout);
                 ip_send_check(iph);
 #ifdef DEBUG_CONFIG_IP_MASQUERADE
                 printk("I-routed to %lX:%X\n",ntohl(iph->daddr),ntohs(portptr[1]));
diff -u --recursive --new-file pre2.0.12/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- pre2.0.12/linux/net/ipv4/ip_output.c	Sat Jun  1 20:11:37 1996
+++ linux/net/ipv4/ip_output.c	Thu Jun  6 12:34:27 1996
@@ -320,6 +320,44 @@
 	iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
 }
 
+
+/*
+ *	If a sender wishes the packet to remain unfreed
+ *	we add it to his send queue. This arguably belongs
+ *	in the TCP level since nobody else uses it. BUT
+ *	remember IPng might change all the rules.
+ */
+static inline void add_to_send_queue(struct sock * sk, struct sk_buff * skb)
+{
+	unsigned long flags;
+
+	/* The socket now has more outstanding blocks */
+	sk->packets_out++;
+
+	/* Protect the list for a moment */
+	save_flags(flags);
+	cli();
+
+	if (skb->link3 != NULL)
+	{
+		NETDEBUG(printk("ip.c: link3 != NULL\n"));
+		skb->link3 = NULL;
+	}
+	if (sk->send_head == NULL)
+	{
+		sk->send_tail = skb;
+		sk->send_head = skb;
+		sk->send_next = skb;
+	}
+	else
+	{
+		sk->send_tail->link3 = skb;
+		sk->send_tail = skb;
+	}
+	restore_flags(flags);
+}
+
+
 /*
  * Queues a packet to be sent, and starts the transmitter
  * if necessary.  if free = 1 then we free the block after
@@ -332,15 +370,8 @@
 void ip_queue_xmit(struct sock *sk, struct device *dev,
 	      struct sk_buff *skb, int free)
 {
+	unsigned int tot_len;
 	struct iphdr *iph;
-/*	unsigned char *ptr;*/
-
-	/* Sanity check */
-	if (dev == NULL)
-	{
-		NETDEBUG(printk("IP: ip_queue_xmit dev = NULL\n"));
-		return;
-	}
 
 	IS_SKB(skb);
 
@@ -348,7 +379,7 @@
 	 *	Do some book-keeping in the packet for later
 	 */
 
-
+	skb->sk = sk;
 	skb->dev = dev;
 	skb->when = jiffies;
 
@@ -361,32 +392,30 @@
 	 */
 
 	iph = skb->ip_hdr;
-	iph->tot_len = htons(skb->len-(((unsigned char *)iph)-skb->data));
-
-	/*
-	 *	No reassigning numbers to fragments...
-	 */
-
-	if(free!=2)
-		iph->id      = htons(ip_id_count++);
-	else
-		free=1;
+	tot_len = skb->len - (((unsigned char *)iph) - skb->data);
+	iph->tot_len = htons(tot_len);
 
-	/* All buffers without an owner socket get freed */
-	if (sk == NULL)
-		free = 1;
+	switch (free) {
+		/* No reassigning numbers to fragments... */
+		default:
+			free = 1;
+			break;
+		case 0:
+			add_to_send_queue(sk, skb);
+			/* fall through */
+		case 1:
+			iph->id = htons(ip_id_count++);
+	}
 
 	skb->free = free;
 
+	/* Sanity check */
+	if (dev == NULL)
+		goto no_device;
+
 #ifdef CONFIG_FIREWALL
-	if(call_out_firewall(PF_INET, skb->dev, iph, NULL) < FW_ACCEPT) {
-		/* just don't send this packet */
-		/* and free socket buffers ;) <aldem@barnet.kharkov.ua> */
-		if (free)
-		  skb->sk = sk;		/* I am not sure *this* really need, */
-		kfree_skb(skb, FREE_WRITE);	/* but *this* must be here */
-		return;
-	}
+	if (call_out_firewall(PF_INET, skb->dev, iph, NULL) < FW_ACCEPT)
+		goto out;
 #endif	
 
 	/*
@@ -395,13 +424,8 @@
 	 *	bits of it.
 	 */
 
-	if(ntohs(iph->tot_len)> dev->mtu)
-	{
-		ip_fragment(sk,skb,dev,0);
-		IS_SKB(skb);
-		kfree_skb(skb,FREE_WRITE);
-		return;
-	}
+	if (tot_len > dev->mtu)
+		goto fragment;
 
 	/*
 	 *	Add an IP checksum
@@ -410,10 +434,6 @@
 	ip_send_check(iph);
 
 	/*
-	 *	Print the frame when debugging
-	 */
-
-	/*
 	 *	More debugging. You cannot queue a packet already on a list
 	 *	Spot this and moan loudly.
 	 */
@@ -424,57 +444,14 @@
 	}
 
 	/*
-	 *	If a sender wishes the packet to remain unfreed
-	 *	we add it to his send queue. This arguably belongs
-	 *	in the TCP level since nobody else uses it. BUT
-	 *	remember IPng might change all the rules.
-	 */
-
-	if (!free)
-	{
-		unsigned long flags;
-		/* The socket now has more outstanding blocks */
-
-		sk->packets_out++;
-
-		/* Protect the list for a moment */
-		save_flags(flags);
-		cli();
-
-		if (skb->link3 != NULL)
-		{
-			NETDEBUG(printk("ip.c: link3 != NULL\n"));
-			skb->link3 = NULL;
-		}
-		if (sk->send_head == NULL)
-		{
-			sk->send_tail = skb;
-			sk->send_head = skb;
-			sk->send_next = skb;
-		}
-		else
-		{
-			sk->send_tail->link3 = skb;
-			sk->send_tail = skb;
-		}
-		/* skb->link3 is NULL */
-
-		/* Interrupt restore */
-		restore_flags(flags);
-	}
-	else
-		/* Remember who owns the buffer */
-		skb->sk = sk;
-
-	/*
 	 *	If the indicated interface is up and running, send the packet.
 	 */
 	 
 	ip_statistics.IpOutRequests++;
 #ifdef CONFIG_IP_ACCT
-	ip_fw_chk(iph,dev,NULL,ip_acct_chain,IP_FW_F_ACCEPT,IP_FW_MODE_ACCT_OUT);
+	ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
 #endif	
-	
+
 #ifdef CONFIG_IP_MULTICAST	
 
 	/*
@@ -505,14 +482,12 @@
 		}
 		/* Multicasts with ttl 0 must not go beyond the host */
 		
-		if(skb->ip_hdr->ttl==0)
-		{
-			kfree_skb(skb, FREE_READ);
-			return;
-		}
+		if (iph->ttl==0)
+			goto out;
 	}
 #endif
-	if((dev->flags&IFF_BROADCAST) && (iph->daddr==dev->pa_brdaddr||iph->daddr==0xFFFFFFFF) && !(dev->flags&IFF_LOOPBACK))
+	if ((dev->flags & IFF_BROADCAST) && !(dev->flags & IFF_LOOPBACK)
+	    && (iph->daddr==dev->pa_brdaddr || iph->daddr==0xFFFFFFFF))
 		ip_loopback(dev,skb);
 		
 	if (dev->flags & IFF_UP)
@@ -521,24 +496,28 @@
 		 *	If we have an owner use its priority setting,
 		 *	otherwise use NORMAL
 		 */
+		int priority = SOPRI_NORMAL;
+		if (sk)
+			priority = sk->priority;
 
-		if (sk != NULL)
-		{
-			dev_queue_xmit(skb, dev, sk->priority);
-		}
-		else
-		{
-			dev_queue_xmit(skb, dev, SOPRI_NORMAL);
-		}
-	}
-	else
-	{
-		if(sk)
-			sk->err = ENETDOWN;
-		ip_statistics.IpOutDiscards++;
-		if (free)
-			kfree_skb(skb, FREE_WRITE);
+		dev_queue_xmit(skb, dev, priority);
+		return;
 	}
+	if(sk)
+		sk->err = ENETDOWN;
+	ip_statistics.IpOutDiscards++;
+out:
+	if (free)
+		kfree_skb(skb, FREE_WRITE);
+	return;
+
+no_device:
+	NETDEBUG(printk("IP: ip_queue_xmit dev = NULL\n"));
+	goto out;
+
+fragment:
+	ip_fragment(sk,skb,dev,0);
+	goto out;
 }
 
 
@@ -720,7 +699,7 @@
 		}
 #endif
 #ifdef CONFIG_IP_ACCT
-		ip_fw_chk(iph,dev,NULL,ip_acct_chain, IP_FW_F_ACCEPT,IP_FW_MODE_ACCT_OUT);
+		ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
 #endif		
 		if(dev->flags&IFF_UP)
 			dev_queue_xmit(skb,dev,sk->priority);
@@ -926,7 +905,7 @@
 #endif		
 #ifdef CONFIG_IP_ACCT
 		if(!offset)
-			ip_fw_chk(iph, dev, NULL, ip_acct_chain, IP_FW_F_ACCEPT, IP_FW_MODE_ACCT_OUT);
+			ip_fw_chk(iph, dev, NULL, ip_acct_chain, 0, IP_FW_MODE_ACCT_OUT);
 #endif	
 		offset -= (maxfraglen-fragheaderlen);
 		fraglen = maxfraglen;
diff -u --recursive --new-file pre2.0.12/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- pre2.0.12/linux/net/ipv4/tcp_input.c	Mon Jun  3 16:46:59 1996
+++ linux/net/ipv4/tcp_input.c	Thu Jun  6 09:54:05 1996
@@ -104,6 +104,7 @@
 	 */
 	
 	m = jiffies - oskb->when;  /* RTT */
+
 	if (sk->rtt != 0) {
 		if(m<=0)
 			m=1;		/* IS THIS RIGHT FOR <0 ??? */
@@ -421,6 +422,8 @@
 	newsk->cong_count = 0;
 	newsk->ssthresh = 0x7fffffff;
 
+	newsk->lrcvtime = 0;
+	newsk->idletime = 0;
 	newsk->high_seq = 0;
 	newsk->backoff = 0;
 	newsk->blog = 0;
@@ -653,13 +656,6 @@
 		goto uninteresting_ack;
 
 	/*
-	 *	If there is data set flag 1
-	 */
-	 
-	if (len != th->doff*4) 
-		flag |= 1;
-
-	/*
 	 *	Have we discovered a larger window
 	 */
 	window_seq = ntohs(th->window);
@@ -679,10 +675,8 @@
 	/*
 	 *	See if our window has been shrunk. 
 	 */
-	if (after(sk->window_seq, window_seq)) {
-		flag |= 4;
+	if (after(sk->window_seq, window_seq))
 		tcp_window_shrunk(sk, window_seq);
-	}
 
 	/*
 	 *	Pipe has emptied
@@ -758,7 +752,7 @@
 
 	if (sk->rcv_ack_seq == ack
 		&& sk->window_seq == window_seq
-		&& !(flag&1)
+		&& len != th->doff*4
 		&& before(ack, sk->sent_seq)
 		&& after(ack, sk->high_seq))
 	{
@@ -777,9 +771,16 @@
 		 * of what we are doing here.
 		 */
 		if (sk->rcv_ack_cnt == MAX_DUP_ACKS+1) {
+			int tmp;
+
+			/* We need to be a bit careful to preserve the
+			 * count of packets that are out in the system here.
+			 */
 			sk->ssthresh = max(sk->cong_window >> 1, 2);
 			sk->cong_window = sk->ssthresh+MAX_DUP_ACKS+1;
+			tmp = sk->packets_out;
 			tcp_do_retransmit(sk,0);
+			sk->packets_out = tmp;
 		} else if (sk->rcv_ack_cnt > MAX_DUP_ACKS+1) {
 			sk->cong_window++;
 			/*
@@ -884,6 +885,7 @@
 		if ((sk->send_head = skb->link3) == NULL)
 		{
 			sk->send_tail = NULL;
+			sk->send_next = NULL;
 			sk->retransmits = 0;
 		}
 
@@ -912,10 +914,15 @@
 		if (sk->packets_out > 0) 
 			sk->packets_out --;
 
+		/* This is really only supposed to be called when we
+		 * are actually ACKing new data, which should exclude
+		 * the ACK handshake on an initial SYN packet as well.
+		 * Rather than introducing a new test here for this
+		 * special case, we just reset the initial values for
+		 * rtt immediatly after we move to the established state.
+		 */
 		if (!(flag&2)) 	/* Not retransmitting */
 			tcp_rtt_estimator(sk,skb);
-		flag |= (2|4);	/* 2 is really more like 'don't adjust the rtt 
-				   In this case as we just set it up */
 		IS_SKB(skb);
 
 		/*
@@ -949,7 +956,6 @@
 		/*
 		 *	Add more data to the send queue.
 		 */
-		flag |= 1;
 		tcp_write_xmit(sk);
 	}
 
@@ -1034,7 +1040,6 @@
 	    && skb_queue_empty(&sk->write_queue)
 	    && sk->send_head == NULL) 
 	{
-		flag |= 1;
 		tcp_send_partial(sk);
 	}
 
@@ -1055,7 +1060,6 @@
 				sk->rcv_ack_seq,sk->write_seq,sk->acked_seq,sk->fin_seq);
 		if (sk->rcv_ack_seq == sk->write_seq /*&& sk->acked_seq == sk->fin_seq*/) 
 		{
-			flag |= 1;
 			sk->shutdown = SHUTDOWN_MASK;
 			tcp_set_state(sk,TCP_CLOSE);
 			return 1;
@@ -1076,7 +1080,6 @@
 			sk->state_change(sk);
 		if (sk->rcv_ack_seq == sk->write_seq) 
 		{
-			flag |= 1;
 			sk->shutdown |= SEND_SHUTDOWN;
 			tcp_set_state(sk, TCP_FIN_WAIT2);
 			/* If the socket is dead, then there is no
@@ -1101,7 +1104,6 @@
 			sk->state_change(sk);
 		if (sk->rcv_ack_seq == sk->write_seq) 
 		{
-			flag |= 1;
 			tcp_time_wait(sk);
 		}
 	}
@@ -1110,7 +1112,7 @@
 	 *	Final ack of a three way shake 
 	 */
 	 
-	if(sk->state==TCP_SYN_RECV)
+	if (sk->state==TCP_SYN_RECV)
 	{
 		tcp_set_state(sk, TCP_ESTABLISHED);
 		tcp_options(sk,th);
@@ -1123,6 +1125,13 @@
 			sk->max_window=32;	/* Sanity check */
 			sk->mss=min(sk->max_window,sk->mtu);
 		}
+		/* Reset the RTT estimator to the initial
+		 * state rather than testing to avoid
+		 * updating it on the ACK to the SYN packet.
+		 */
+		sk->rtt = 0;
+		sk->rto = TCP_TIMEOUT_INIT;
+		sk->mdev = TCP_TIMEOUT_INIT;
 	}
 	
 	/*
@@ -1679,7 +1688,7 @@
 	struct sock *sk;
 	int syn_ok=0;
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
-	int r=0;
+	int r;
 #endif
 
 	/*
@@ -1763,6 +1772,11 @@
 	 
 	skb->sk=sk;
 	atomic_add(skb->truesize, &sk->rmem_alloc);
+
+	/*
+	 * Mark the time of the last received packet.
+	 */
+	sk->idletime = jiffies;
 	
 	/*
 	 *	We should now do header prediction.
@@ -1897,6 +1911,13 @@
 					sk->max_window = 32;
 					sk->mss = min(sk->max_window, sk->mtu);
 				}
+				/* Reset the RTT estimator to the initial
+				 * state rather than testing to avoid
+				 * updating it on the ACK to the SYN packet.
+				 */
+				sk->rtt = 0;
+				sk->rto = TCP_TIMEOUT_INIT;
+				sk->mdev = TCP_TIMEOUT_INIT;
 			}
 			else
 			{
diff -u --recursive --new-file pre2.0.12/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- pre2.0.12/linux/net/ipv4/tcp_output.c	Sat Jun  1 20:11:37 1996
+++ linux/net/ipv4/tcp_output.c	Thu Jun  6 09:54:05 1996
@@ -25,9 +25,11 @@
 
 #include <linux/config.h>
 #include <net/tcp.h>
-
+#include <linux/ip_fw.h>
+#include <linux/firewall.h>
 #include <linux/interrupt.h>
 
+
 /*
  * RFC 1122 says:
  *
@@ -146,13 +148,22 @@
 	}
 
 	/*
+	 * Jacobson recommends this in the appendix of his SIGCOMM'88 paper.
+	 * The idea is to do a slow start again if we haven't been doing
+	 * anything for a long time, in which case we have no reason to
+	 * believe that our congestion window is still correct.
+	 */
+	if (sk->send_head == 0 && (jiffies - sk->idletime) > sk->rto)
+		sk->cong_window = 1;
+
+	/*
 	 *	Actual processing.
 	 */
-	 
+
 	tcp_statistics.TcpOutSegs++;  
 	skb->seq = ntohl(th->seq);
 	skb->end_seq = skb->seq + size - 4*th->doff;
-	
+
 	/*
 	 *	We must queue if
 	 *
@@ -394,7 +405,7 @@
 		sk->send_next = sk->send_head;
 		sk->packets_out = 0;
 	}
-	skb = sk->send_head;
+	skb = sk->send_next;
 
 	while (skb != NULL)
 	{
@@ -468,6 +479,11 @@
 				skb->sk->err_soft=ENETUNREACH;
 				skb->sk->error_report(skb->sk);
 			}
+			/* Can't transmit this packet, no reason
+			 * to transmit the later ones, even if
+			 * the congestion window allows.
+			 */
+			break;
 		}
 		else
 		{
@@ -475,6 +491,17 @@
 			skb->raddr=rt->rt_gateway;
 			skb->dev=dev;
 			skb->arp=1;
+#ifdef CONFIG_FIREWALL
+        		if (call_out_firewall(PF_INET, skb->dev, iph, NULL) < FW_ACCEPT) {
+				/* The firewall wants us to dump the packet.
+			 	* We have to check this here, because
+			 	* the drop in ip_queue_xmit only catches the
+			 	* first time we send it. We must drop on
+				* every resend as well.
+			 	*/
+				break;
+        		}
+#endif 
 			if (rt->rt_hh)
 			{
 				memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len);
diff -u --recursive --new-file pre2.0.12/linux/net/netsyms.c linux/net/netsyms.c
--- pre2.0.12/linux/net/netsyms.c	Mon Jun  3 16:47:00 1996
+++ linux/net/netsyms.c	Wed Jun  5 14:42:27 1996
@@ -180,6 +180,7 @@
 #ifdef CONFIG_NETLINK
 	X(netlink_attach),
 	X(netlink_detach),
+	X(netlink_donothing),
 	X(netlink_post),
 #endif /* CONFIG_NETLINK */
 	
diff -u --recursive --new-file pre2.0.12/linux/net/socket.c linux/net/socket.c
--- pre2.0.12/linux/net/socket.c	Fri May 17 15:32:21 1996
+++ linux/net/socket.c	Wed Jun  5 14:42:27 1996
@@ -1113,7 +1113,7 @@
 	struct socket *sock;
 	struct file *file;
 	char address[MAX_SOCK_ADDR];
-	struct iovec iov[MAX_IOVEC];
+	struct iovec iov[UIO_MAXIOV];
 	struct msghdr msg_sys;
 	int err;
 	int total_len;
@@ -1134,7 +1134,7 @@
 	memcpy_fromfs(&msg_sys,msg,sizeof(struct msghdr));
 
 	/* do not move before msg_sys is valid */
-	if(msg_sys.msg_iovlen>MAX_IOVEC)
+	if(msg_sys.msg_iovlen>UIO_MAXIOV)
 		return -EINVAL;
 
 	/* This will also move the address data into kernel space */
@@ -1154,7 +1154,7 @@
 {
 	struct socket *sock;
 	struct file *file;
-	struct iovec iov[MAX_IOVEC];
+	struct iovec iov[UIO_MAXIOV];
 	struct msghdr msg_sys;
 	int err;
 	int total_len;
@@ -1177,7 +1177,7 @@
 	if(err)
 		return err;
 	memcpy_fromfs(&msg_sys,msg,sizeof(struct msghdr));
-	if(msg_sys.msg_iovlen>MAX_IOVEC)
+	if(msg_sys.msg_iovlen>UIO_MAXIOV)
 		return -EINVAL;
 
 	/*
diff -u --recursive --new-file pre2.0.12/linux/scripts/tkgen.c linux/scripts/tkgen.c
--- pre2.0.12/linux/scripts/tkgen.c	Mon Apr 15 12:20:22 1996
+++ linux/scripts/tkgen.c	Thu Jun  6 13:30:41 1996
@@ -989,9 +989,11 @@
 	case tok_dep_tristate:
 	case tok_define:
 	case tok_choose:
-	  if(cfg->flags & GLOBAL_WRITTEN) break;
-	  cfg->flags |= GLOBAL_WRITTEN;
-	  printf("\tglobal %s\n", cfg->optionname);
+	  if(!(cfg->flags & GLOBAL_WRITTEN))
+	    {
+	      cfg->flags |= GLOBAL_WRITTEN;
+	      printf("\tglobal %s\n", cfg->optionname);
+	    }
 	  /* fall through */
 	case tok_make:
 	case tok_comment:
