diff -u --recursive --new-file v1.3.53/linux/CREDITS linux/CREDITS
--- v1.3.53/linux/CREDITS	Tue Jan  2 16:46:20 1996
+++ linux/CREDITS	Thu Jan  4 14:07:58 1996
@@ -187,6 +187,10 @@
 S: Bellevue, Washington 98007
 S: USA
 
+N: Juan Jose Ciarlante
+E: jjciarla@raiz.uncu.edu.ar
+D: Network driver alias support
+
 N: Hamish Coleman
 E: hamish@zot.apana.org.au
 D: SEEQ8005 network driver
@@ -195,11 +199,13 @@
 S: Australia
 
 N: Alan Cox
-E: iialan@iifeak.swan.ac.uk
-E: alan@cymru.net (use for commercial traffic)
-E: gw4pts@gw4pts.ampr.org
+E: alan@lxorguk.ukuu.org.uk (linux related - except big patches)
+E: iialan@www.linux.org.uk (linux.org.uk/big patches)
+E: alan@cymru.net (commercial CymruNET stuff)
+E: gw4pts@gw4pts.ampr.org (amateur radio stuff)
 E: GW4PTS@GB7SWN (packet radio)
-S: c/o I^2IT Limited
+E: Please don't use iialan@iifeak.swan.ac.uk for Linux stuff
+S: c/o 3Com/I^2IT Limited
 S: The Innovation Centre
 S: University Of Wales
 S: Swansea, SA2 8PP
@@ -209,6 +215,8 @@
 D: Initial AX.25 & IPX releases
 D: Original Linux netatalk patches.
 D: Current 3c501 hacker. >>More 3c501 info/tricks wanted<<.
+D: Watchdog timer drivers
+D: Linux/SMP
 
 N: Laurence Culhane
 E: loz@holmes.demon.co.uk
@@ -557,6 +565,7 @@
 N: Volker Lendecke
 E: lendecke@namu01.gwdg.de
 D: Kernel smbfs (to mount WfW, NT and OS/2 network drives.)
+D: NCP filesystem support (to mount NetWare volumes)
 S: Innersteweg 11
 S: 37081 Goettingen
 S: Germany
@@ -798,6 +807,13 @@
 N: Greg Page
 E: greg@caldera.com
 D: IPX development and support
+
+N: Avery Pennarun
+E: apenwarr@foxnet.net
+D: ARCnet driver, various small changes
+S: RR #5, 497 Pole Line Road
+S: Thunder Bay, Ontario
+S: CANADA P7C 5M9
 
 N: Kai Petzke
 E: wpp@marie.physik.tu-berlin.de
diff -u --recursive --new-file v1.3.53/linux/Documentation/CodingStyle linux/Documentation/CodingStyle
--- v1.3.53/linux/Documentation/CodingStyle	Tue Jan  2 16:46:20 1996
+++ linux/Documentation/CodingStyle	Thu Jan  4 07:43:40 1996
@@ -177,9 +177,34 @@
 make a good program). 
 
 So, you can either get rid of GNU emacs, or change it to use saner
-values.  I did the first, so don't ask me how to do the latter.  But
-even if you fail in getting emacs to do sane formatting, not everything
-is lost: use "indent". 
+values.  To do the latter, you can stick the following in your .emacs file:
+
+(defun linux-c-mode ()
+  "C mode with adjusted defaults for use with the Linux kernel."
+  (interactive)
+  (c-mode)
+  (setq c-indent-level 8)
+  (setq c-brace-imaginary-offset 0)
+  (setq c-brace-offset -8)
+  (setq c-argdecl-indent 8)
+  (setq c-label-offset -8)
+  (setq c-continued-statement-offset 8)
+  (setq indent-tabs-mode nil)
+  (setq tab-width 8))
+
+This will define the M-x linux-c-mode command.  When hacking on a
+module, if you put the string -*- linux-c -*- somewhere on the first
+two lines, this mode will be automatically invoked. Also, you may want
+to add
+
+(setq auto-mode-alist (cons '("/usr/src/linux.*/.*\\.[ch]$" . linux-c-mode)
+                       auto-mode-alist))
+
+to your .emacs file if you want to have linux-c-mode switched on
+automagically when you edit source files under /usr/src/linux.
+
+But even if you fail in getting emacs to do sane formatting, not
+everything is lost: use "indent".
 
 Now, again, GNU indent has the same brain dead settings that GNU emacs
 has, which is why you need to give it a few command line options. 
diff -u --recursive --new-file v1.3.53/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.53/linux/Documentation/Configure.help	Tue Jan  2 16:46:20 1996
+++ linux/Documentation/Configure.help	Thu Jan  4 07:35:47 1996
@@ -177,6 +177,17 @@
   consider updating your networking tools too; read net/README for
   details. 
 
+Network aliasing
+CONFIG_NET_ALIAS
+  This is for setting several network addresses on the same low-level network
+  device driver. Typically used for services that act different based
+  on the address they listen (eg. Apache httpd) or for connecting to
+  different logical networks through the same physical interface.
+  This is the generic part, later when configuring network protocol
+  options you will be asked for protocol-specific aliasing support.
+  See net/core/README.alias for more info.
+  If you need this features (for any protocol, like IP) say Y.  
+
 Sun floppy controller support
 CONFIG_BLK_DEV_SUNFD
   This is support for floppy drives on Sun Sparc workstations. Say Y
@@ -492,6 +503,12 @@
   has nothing to do with the computer architecture of the same
   name. If you want this, say Y.
 
+IP: aliasing support
+CONFIG_IP_ALIAS
+  You need this to enable IP layer network aliasing. This will also
+  enable ARP resolution for alias devices. If you don't need several
+  IP addresses per interface, answer N.
+
 IP: multicast routing(in progress)
 CONFIG_IP_MROUTE
   This is used if you want your machine to act as a router for IP
@@ -787,9 +804,10 @@
   the documentation in drivers/scsi/README.BusLogic for more information.
   BusLogic FlashPoint SCSI Host Adapters are not supported by this driver.
   If this driver does not work correctly without modification, please
-  consult the author. This driver is not currently available as a module.
-  You might want to read the SCSI-HOWTO, available via ftp (user: 
-  anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO.
+  consult the author.  This driver may also be built as a module, but
+  only a single instance may be loaded.  You might also want to read
+  the SCSI-HOWTO, available via anonymous ftp from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO.
  
 EATA-DMA (DPT,NEC&ATT for ISA,EISA,PCI) support
 CONFIG_SCSI_EATA_DMA
diff -u --recursive --new-file v1.3.53/linux/Documentation/networking/alias.txt linux/Documentation/networking/alias.txt
--- v1.3.53/linux/Documentation/networking/alias.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/networking/alias.txt	Wed Jan  3 20:36:23 1996
@@ -0,0 +1,92 @@
+NET_ALIAS device aliasing v0.4x
+===============================
+  The main step taken in versions 0.40+ is the implementation of a
+  device aliasing mechanism that creates *actual* devices.
+  This development includes NET_ALIAS (generic aliasing) plus IP_ALIAS
+  (specific IP) support.
+
+Features
+--------
+o  ACTUAL alias devices created & inserted in dev chain
+o  AF_ independent: net_alias_type objects. Generic aliasing engine.
+o  AF_INET optimized
+o  hashed alias address lookup
+o  net_alias_type objs registration/unreg., module-ables.
+o  /proc/net/aliases & /proc/net/alias_types entries
+
+o IP alias implementation: static or runtime module.
+
+Usage (IP aliasing)
+-------------------
+  A very first step to test if you are running a net_alias-ed kernel
+  is to check /proc/net/aliases & /proc/net/alias_types entries:
+     # cat /proc/net/alias*
+
+  For IP aliasing you must have IP_ALIAS support included by
+  static linking ('y' to 2nd question above), or runtime module
+  insertion ('m' to 2nd q. above):
+     # insmod /usr/src/linux/modules/ip_alias.o (1.3.xx)
+     # insmod /usr/src/ip_alias/ip_alias.o      (1.2.xx) see above.
+
+o Alias creation.
+  Alias creation is done by 'magic' iface naming: eg. to create a
+  200.1.1.1 alias for eth0 ...
+  
+    # ifconfig eth0:0 200.1.1.1  etc,etc....
+                   ~~ -> request alias #0 creation (if it not exists) for eth0
+    and routing stuff also ...
+    # route add -host 200.1.1.1 dev eth0:0  (if same IP network as
+					    main device)
+   
+    # route add -net 200.1.1.0 dev eth0:0   (if completely new network wanted
+					    for eth0:0)
+
+o Alias deletion.
+  Also done by magic naming, eg:
+
+    # ifconfig eth0:0-  0  (maybe any address)
+                   ~~~ -> will delete alias (note '-' after dev name)
+  alias device is closed before deletion, so all network stuff that
+  points to it (routes, arp entries, ...) will be released.
+  		   		   
+Alias (re-)configuring
+  Aliases *are* devices, so you configure and refer to them as usual (ifconfig,
+  route, etc).
+  
+o Procfs entries
+  2 entries are added to help fetching alias runtime configuration:
+  a) /proc/net/alias_types
+     Will show you alias_types registered (ie. address families that
+     can be aliased).
+     eg. for IP aliasing with 1 alias configured:
+
+     # cat /proc/net/alias_types
+     type    name            n_attach
+     2       ip              1      
+     
+  b) /proc/net/aliases
+     Will show aliased devices info, eg (same as above):
+
+     # cat /proc/net/aliases
+     device           family address                                
+     eth0:0           2      200.1.1.1
+
+Relationship with main device
+-----------------------------
+  - On main device closing, all aliases will be closed and freed.
+  - Each new alias created is inserted in dev_chain just before next
+    main device (aliases get 'stacked' after main_dev), eg:
+      lo->eth0->eth0:0->eth0:2->eth1->0
+    If eth0 is unregistered, all it aliases will also be:
+      lo->eth1->0     
+
+Contact
+-------
+Please finger or e-mail me:
+   Juan Jose Ciarlante <jjciarla@raiz.uncu.edu.ar>
+   
+		   		
+; local variables:
+; mode: indented-text
+; mode: auto-fill
+; end:
diff -u --recursive --new-file v1.3.53/linux/Documentation/ramdisk.txt linux/Documentation/ramdisk.txt
--- v1.3.53/linux/Documentation/ramdisk.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/ramdisk.txt	Tue Jan  2 16:46:59 1996
@@ -0,0 +1,197 @@
+
+Using the RAM disk block device with Linux
+------------------------------------------
+
+Contents:
+
+	1) Overview
+	2) Kernel Command Line Parameters
+	3) Using "rdev -r" With New Kernels
+	4) An Example of Creating a Compressed ramdisk 
+
+
+1) Overview
+-----------
+
+As of kernel v1.3.48, the ramdisk driver was substantially changed.
+
+The older versions would grab a chunk of memory off the top before
+handing the remainder to the kernel at boot time. Thus a size parameter
+had to be specified via "ramdisk=1440" or "rdev -r /dev/fd0 1440" so
+that the driver knew how much memory to grab.
+
+Now the ramdisk dynamically grows as more space is required. It does
+this by using RAM from the buffer cache. The driver marks the buffers
+it is using with a new "BH_Protected" flag so that the kernel does 
+not try to reuse them later. This means that the old size parameter
+is no longer used, new command line parameters exist, and the behavior
+of the "rdev -r" or "ramsize" (usually a symbolic link to "rdev")
+command has changed.
+
+The old "ramdisk=<ram_size>" is now obsolete. The kernel will ignore
+such old commands, and thus they will be passed on through to the init
+program, which will then complain. You should remove any of these old
+style commands from config files such as "/etc/lilo.config".
+
+The new ramdisk also has the ability to load compressed ramdisk images,
+allowing one to squeeze more programs onto an average installation or 
+rescue floppy disk.
+
+Notes: You may have "dev/ram" or "/dev/ramdisk" or both. They are
+equivalent from the standpoint of this document. Also, the new ramdisk
+is a config option. When running "make config", make sure you enable
+ramdisk support for the kernel you intend to use the ramdisk with.
+
+
+2) Kernel Command Line Parameters
+---------------------------------
+
+	ramdisk_start=NNN
+	=================
+
+To allow a kernel image to reside on a floppy disk along with a compressed
+ramdisk image, the "ramdisk_start=<offset>" command was added. The kernel
+can't be included into the compressed ramdisk filesystem image, because
+it needs to be stored starting at block zero so that the BIOS can load the 
+bootsector and then the kernel can bootstrap itself to get going.
+
+Note: If you are using an uncompressed ramdisk image, then the kernel can
+be a part of the filesystem image that is being loaded into the ramdisk,
+and the floppy can be booted with LILO, or the two can be separate as
+is done for the compressed images.
+
+If you are using a two-disk boot/root setup (kernel on #1, ramdisk image
+on #2) then the ramdisk would start at block zero, and an offset of
+zero would be used. Since this is the default value, you would not need
+to actually use the command at all.
+
+If instead, you have a "zImage" of about 350k, and a "fs_image.gz" of
+say about 1MB, and you want them both on the same disk, then you
+would use an offset. If you stored the "fs_image.gz" onto the floppy
+starting at an offset of 400kB, you would use "ramdisk_start=400".
+
+
+	load_ramdisk=N
+	==============
+
+This parameter tells the kernel whether it is to try and load a
+ramdisk image or not. Specifying "load_ramdisk=1" will tell the
+kernel to load a floppy into the ramdisk. The default value is
+zero, meaning that the kernel should not try to load a ramdisk.
+
+
+	prompt_ramdisk=N
+	================
+
+This parameter tells the kernel whether or not to give you a prompt
+asking you to insert the floppy containing the ramdisk image. In
+a single floppy configuration the ramdisk image is on the same floppy
+as the kernel that just finished loading/booting and so a prompt
+is not needed. In this case one can use "prompt_ramdisk=0". In a
+two floppy configuration, you will need the chance to switch disks,
+and thus "prompt_ramdisk=1" can be used. Since this is the default 
+value, it doesn't really need to be specified.
+
+
+3) Using "rdev -r" With New Kernels
+-----------------------------------
+
+The usage of the word (two bytes) that "rdev -r" sets in the kernel image
+has changed. The low 11 bits (0 -> 10) specify an offset (in 1k blocks) 
+of up to 2MB (2^11) of where to find the ramdisk (this used to be the 
+size). Bit 14 indicates that a ramdisk is to be loaded, and bit 15
+indicates whether a prompt/wait sequence is to be given before trying
+to read the ramdisk. Since the ramdisk dynamically grows as data is
+being written into it, a size field is no longer required. Bits 11
+to 13 are not presently used and may as well be zero. These numbers
+are no magical secrets, as seen below:
+
+./arch/i386/kernel/setup.c:#define RAMDISK_IMAGE_START_MASK     0x07FF
+./arch/i386/kernel/setup.c:#define RAMDISK_PROMPT_FLAG          0x8000
+./arch/i386/kernel/setup.c:#define RAMDISK_LOAD_FLAG            0x4000
+
+Consider a typical two floppy disk setup, where you will have the 
+kernel on disk one, and have already put a ramdisk image onto disk #2.
+
+Hence you want to set bits 0 to 13 as zero, meaning that your ramdisk
+starts at an offset of zero kB from the beginning of the floppy.
+The command line equivalent is: "ramdisk_start=0"
+
+You want bit 14 as one, indicating that a ramdisk is to be loaded.
+The command line equivalent is: "load_ramdisk=1"
+
+You want bit 15 as one, indicating that you want a prompt/keypress
+sequence so that you have a chance to switch floppy disks.
+The command line equivalent is: "prompt_ramdisk=1"
+
+Putting that together gives 2^15 + 2^14 + 0 = 49152 for an rdev word.
+So to create disk one of the set, you would do:
+
+	/usr/src/linux# cat arch/i386/boot/zImage > /dev/fd0
+	/usr/src/linux# rdev /dev/fd0 /dev/fd0
+	/usr/src/linux# rdev -r /dev/fd0 49152
+
+If you make a boot disk that has LILO, then for the above, you would use:
+	append = "ramdisk_start=0 load_ramdisk=1 prompt_ramdisk=1"
+Since the default start = 0 and the default prompt = 1, you could use:
+	append = "load_ramdisk=1"
+
+
+4) An Example of Creating a Compressed ramdisk 
+----------------------------------------------
+
+To create a ramdisk image, you will need a spare block device to
+construct it on. This can be the ramdisk device itself, or an
+unused disk partition (such as an unmounted swap partition). For this 
+example, we will use the ramdisk device, "/dev/ram".
+
+Note: This technique should not be done on a machine with less than 8MB
+of RAM. If using a spare disk partition instead of /dev/ram, then this
+restriction does not apply.
+
+a) Decide on the ramdisk size that you want. Say 2MB for this example.
+   Create it by writing to the ramdisk device. (This step is not presently
+   required, but may be in the future.) It is wise to zero out the
+   area (esp. for disks) so that maximal compression is achieved for
+   the unused blocks of the image that you are about to create.
+
+	dd if=/dev/zero of=/dev/ram bs=1k count=2048
+
+b) Make a filesystem on it. Say ext2fs for this example.
+
+	mke2fs -vm0 /dev/ram 2048
+
+c) Mount it, copy the files you want to it (eg: /etc/* /dev/* ...)
+   and unmount it again.
+
+d) Compress the contents of the ramdisk. The level of compression
+   will be approximately 50% of the space used by the files. Unused
+   space on the ramdisk will compress to almost nothing.
+
+	dd if=/dev/ram bs=1k count=2048 | gzip -v9 > /tmp/ram_image.gz
+
+e) Put the kernel onto the floppy
+
+	cat zImage > /dev/fd0
+
+f) Put the ramdisk image onto the floppy, after the kernel. Use an offset
+   that is slightly larger than the kernel, so that you can put another
+   (possibly larger) kernel onto the same floppy later without overlapping
+   the ramdisk image. An offset of 400kB for kernels about 350kB in
+   size would be reasonable. Make sure offset+size of ram_image.gz is
+   not larger than the total space on your floppy (usually 1440kB).
+
+	dd if=/tmp/ram_image.gz of=/dev/fd0 bs=1k skip=400
+
+g) Use "rdev" to set the boot device, ramdisk offset, prompt flag, etc.
+   For ramdisk_start=400, load_ramdisk=1, ramdisk_start=400, one would
+   have 2^15 + 2^14 + 400 = 49552.
+
+	rdev /dev/fd0 /dev/fd0
+	rdev -r /dev/fd0 49552
+
+That is it. You now have your boot/root compressed ramdisk floppy. Some
+users may wish to combine steps (d) and (f) by using a pipe.
+
+--------------------------------------------------------------------------
+						Paul Gortmaker 12/95	
diff -u --recursive --new-file v1.3.53/linux/Makefile linux/Makefile
--- v1.3.53/linux/Makefile	Tue Jan  2 16:46:20 1996
+++ linux/Makefile	Wed Jan  3 13:55:28 1996
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 53
+SUBLEVEL = 54
 
 ARCH = i386
 
@@ -249,7 +249,7 @@
 endif
 
 modules: include/linux/version.h
-	@set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i CFLAGS="-dc $(CFLAGS) $(MODFLAGS)" modules; done
+	@set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i CFLAGS="$(CFLAGS) $(MODFLAGS)" modules; done
 
 modules_install:
 	@( \
diff -u --recursive --new-file v1.3.53/linux/README linux/README
--- v1.3.53/linux/README	Wed Sep 13 12:45:28 1995
+++ linux/README	Wed Jan  3 14:04:53 1996
@@ -71,6 +71,18 @@
    failed patches (xxx# or xxx.rej). If there are, either you or me has
    made a mistake.
 
+   Alternatively, the script patch-kernel can be used to automate this
+   process.  It determines the current kernel version and applies any
+   patches found.
+
+		cd /usr/src
+		linux/scripts/patch-kernel
+
+   The default directory for the kernel source is /usr/src/linux, but
+   can be specified as the first argument.  Patches are applied from
+   the current directory, but an alternative directory can be specified
+   as the second argument.
+
  - make sure your /usr/include/linux and /usr/include/asm directories
    are just symlinks to the kernel sources:
 
diff -u --recursive --new-file v1.3.53/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
--- v1.3.53/linux/arch/alpha/defconfig	Tue Jan  2 16:46:20 1996
+++ linux/arch/alpha/defconfig	Thu Jan  4 14:14:20 1996
@@ -153,10 +153,10 @@
 # CONFIG_UMSDOS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_NFS_FS is not set
+# CONFIG_SMB_FS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
-# CONFIG_SMB_FS is not set
 
 #
 # character devices
diff -u --recursive --new-file v1.3.53/linux/arch/alpha/kernel/bios32.c linux/arch/alpha/kernel/bios32.c
--- v1.3.53/linux/arch/alpha/kernel/bios32.c	Tue Dec 26 04:45:34 1995
+++ linux/arch/alpha/kernel/bios32.c	Tue Jan  2 16:49:27 1996
@@ -643,10 +643,13 @@
 					  PCI_INTERRUPT_LINE, dev->irq);
 #endif
 	}
-	/* now, set any level-triggered IRQs */
-	if (level_bits)
-		outw(level_bits, 0x4d0);
-
+	/*
+	 * Now, make all PCI interrupts level sensitive.  Notice:
+	 * these registers must be accessed byte-wise.  outw() doesn't
+	 * work, for some reason.
+	 */
+	outb((level_bits >> 0) & 0xff, 0x4d0);
+	outb((level_bits >> 8) & 0xff, 0x4d1);
 
 #if PCI_MODIFY
 	{
diff -u --recursive --new-file v1.3.53/linux/arch/alpha/mm/init.c linux/arch/alpha/mm/init.c
--- v1.3.53/linux/arch/alpha/mm/init.c	Sun Dec 17 11:43:09 1995
+++ linux/arch/alpha/mm/init.c	Thu Jan  4 07:50:59 1996
@@ -21,7 +21,6 @@
 #include <asm/pgtable.h>
 #include <asm/hwrpb.h>
 
-extern void scsi_mem_init(unsigned long);
 extern void die_if_kernel(char *,struct pt_regs *,long);
 extern void show_net_buffers(void);
 
@@ -154,11 +153,6 @@
 		mem_map[MAP_NR(tmp)].reserved = 1;
 		tmp += PAGE_SIZE;
 	}
-
-
-#ifdef CONFIG_SCSI
-	scsi_mem_init(high_memory);
-#endif
 
 	for (tmp = PAGE_OFFSET ; tmp < high_memory ; tmp += PAGE_SIZE) {
 		if (mem_map[MAP_NR(tmp)].reserved)
diff -u --recursive --new-file v1.3.53/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v1.3.53/linux/arch/i386/kernel/smp.c	Wed Dec 27 09:12:12 1995
+++ linux/arch/i386/kernel/smp.c	Wed Jan  3 20:36:23 1996
@@ -14,6 +14,7 @@
  *		Felix Koop	:	NR_CPUS used properly
  *		Jose Renau	:	Handle single CPU case.
  *		Alan Cox	:	By repeated request 8) - Total BogoMIP report.
+ *		Greg Wright	:	Fix for kernel stacks panic.
  *
  */
 
@@ -42,7 +43,7 @@
 volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};	/* We always use 0 the rest is ready for parallel delivery */
 volatile unsigned long smp_invalidate_needed;		/* Used for the invalidate map thats also checked in the spinlock */
 struct cpuinfo_x86 cpu_data[NR_CPUS];			/* Per cpu bogomips and other parameters 		*/
-static unsigned int num_processors = 0;			/* Internal processor count				*/
+static unsigned int num_processors = 1;			/* Internal processor count				*/
 static unsigned long io_apic_addr = 0;			/* Address of the I/O apic (not yet used) 		*/
 unsigned char boot_cpu_id = 0;				/* Processor that is doing the boot up 			*/
 static unsigned char *kstack_base,*kstack_end;		/* Kernel stack list pointers 				*/
@@ -249,7 +250,6 @@
 {
 	unsigned long *bp=(unsigned long *)base;
 	struct intel_mp_floating *mpf;
-	num_processors = 1;		/* The boot processor */
 	
 /*	printk("Scan SMP from %p for %ld bytes.\n",
 		bp,length);*/
diff -u --recursive --new-file v1.3.53/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v1.3.53/linux/arch/i386/mm/init.c	Wed Nov  8 07:11:30 1995
+++ linux/arch/i386/mm/init.c	Thu Jan  4 07:50:59 1996
@@ -28,7 +28,6 @@
 #undef USE_PENTIUM_MM
 #endif
 
-extern void scsi_mem_init(unsigned long);
 extern void die_if_kernel(char *,struct pt_regs *,long);
 extern void show_net_buffers(void);
 
@@ -232,9 +231,6 @@
 		mem_map[MAP_NR(start_mem)].reserved = 0;
 		start_mem += PAGE_SIZE;
 	}
-#ifdef CONFIG_SCSI
-	scsi_mem_init(high_memory);
-#endif
 	for (tmp = 0 ; tmp < high_memory ; tmp += PAGE_SIZE) {
 		if (mem_map[MAP_NR(tmp)].reserved) {
 			if (tmp >= 0xA0000 && tmp < 0x100000)
diff -u --recursive --new-file v1.3.53/linux/arch/mips/mm/init.c linux/arch/mips/mm/init.c
--- v1.3.53/linux/arch/mips/mm/init.c	Sun Dec 17 11:43:11 1995
+++ linux/arch/mips/mm/init.c	Thu Jan  4 07:50:59 1996
@@ -24,7 +24,6 @@
 #include <asm/pgtable.h>
 
 extern void deskstation_tyne_dma_init(void);
-extern void scsi_mem_init(unsigned long);
 extern void sound_mem_init(void);
 extern void die_if_kernel(char *,struct pt_regs *,long);
 extern void show_net_buffers(void);
@@ -257,9 +256,6 @@
 
 #ifdef CONFIG_DESKSTATION_TYNE
 	deskstation_tyne_dma_init();
-#endif
-#ifdef CONFIG_SCSI
-	scsi_mem_init(high_memory);
 #endif
 #ifdef CONFIG_SOUND
 	sound_mem_init();
Binary files v1.3.53/linux/arch/ppc/boot/mkboot and linux/arch/ppc/boot/mkboot differ
diff -u --recursive --new-file v1.3.53/linux/arch/ppc/kernel/misc.S linux/arch/ppc/kernel/misc.S
--- v1.3.53/linux/arch/ppc/kernel/misc.S	Tue Jan  2 16:46:21 1996
+++ linux/arch/ppc/kernel/misc.S	Sat Dec 30 15:48:22 1995
@@ -624,4 +624,4 @@
 	.globl	floppy_track_buffer
 floppy_track_buffer:
 	.space	512*2*38		/* Space for one entire cylinder! */	
-#endif
+#endif
\ No newline at end of file
diff -u --recursive --new-file v1.3.53/linux/arch/ppc/mm/init.c linux/arch/ppc/mm/init.c
--- v1.3.53/linux/arch/ppc/mm/init.c	Tue Jan  2 16:46:24 1996
+++ linux/arch/ppc/mm/init.c	Thu Jan  4 07:50:59 1996
@@ -45,9 +45,6 @@
 #ifdef CONFIG_DESKSTATION_TYNE
 extern void deskstation_tyne_dma_init(void);
 #endif
-#ifdef CONFIG_SCSI
-extern void scsi_mem_init(unsigned long);
-#endif
 #ifdef CONFIG_SOUND
 extern void sound_mem_init(void);
 #endif
@@ -224,9 +221,6 @@
 	}
 #ifdef CONFIG_DESKSTATION_TYNE
 	deskstation_tyne_dma_init();
-#endif
-#ifdef CONFIG_SCSI
-	scsi_mem_init(high_memory);
 #endif
 #ifdef CONFIG_SOUND
 	sound_mem_init();
diff -u --recursive --new-file v1.3.53/linux/drivers/block/README.ide linux/drivers/block/README.ide
--- v1.3.53/linux/drivers/block/README.ide	Sun Dec 17 11:43:11 1995
+++ linux/drivers/block/README.ide	Thu Jan  4 07:43:40 1996
@@ -391,6 +391,9 @@
 	program is run (like Norton "speeddisk"), it may move the Linux boot
 	files around, confusing LILO and making the (Linux) system unbootable.
 	Be sure to keep a kernel "boot floppy" at hand for such circumstances.
+	A possible workaround is to mark the Linux files as S+H+R (System,
+	Hidden, Readonly), to prevent most defragmentation programs from
+	moving the files around.
 
 If you "don't do DOS", then partition as you please, but remember to create
 a small partition to hold the /boot directory (and vmlinuz) as described above
diff -u --recursive --new-file v1.3.53/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v1.3.53/linux/drivers/block/floppy.c	Tue Dec 26 04:45:35 1995
+++ linux/drivers/block/floppy.c	Tue Jan  2 16:47:00 1996
@@ -272,7 +272,7 @@
 /*
  * globals used by 'result()'
  */
-#define MAX_REPLIES 10
+#define MAX_REPLIES 17
 static unsigned char reply_buffer[MAX_REPLIES];
 static int inr; /* size of reply buffer, when called from interrupt */
 #define ST0 (reply_buffer[0])
@@ -3656,8 +3656,38 @@
 		printk("FDC init: UNLOCK: unexpected return of %d bytes.\n", r);
 		return FDC_UNKNOWN;
 	}
-	printk("FDC %d is a post-1991 82077\n",fdc);
-	return FDC_82077;	/* Revised 82077AA passes all the tests */
+	output_byte(FD_PARTID);
+	r = result();
+	if (r != 1) {
+		printk("FDC init: PARTID: unexpected return of %d bytes.\n",r);
+		return FDC_UNKNOWN;
+	}
+	if (reply_buffer[0] == 0x80) {
+		printk("FDC %d is a post-1991 82077\n",fdc);
+		return FDC_82077;	/* Revised 82077AA passes all the tests */
+	}
+	switch (reply_buffer[0] >> 5) {
+	case 0x0:
+		output_byte(FD_SAVE);
+		r = result();
+		if (r != 17) {
+			printk("FDC init: SAVE: unexpected return of %d bytes.\n",r);
+			return FDC_UNKNOWN;
+		}
+		if (!(reply_buffer[0] & 0x40)) {
+			printk("FDC %d is a 3Volt 82078SL.\n",fdc);
+			return FDC_82078;
+		}
+		/* Either a 82078-1 or a 82078SL running at 5Volt */
+		printk("FDC %d is a 82078-1.\n",fdc);
+		return FDC_82078_1;
+	case 0x1:
+		printk("FDC %d is a 44pin 82078\n",fdc);
+		return FDC_82078;
+	default:
+		printk("FDC %d init: PARTID returned an unknown ID: %d.\n", fdc, reply_buffer[0] >> 5);
+		return FDC_UNKNOWN;
+	}
 } /* get_fdc_version */
 
 /* lilo configuration */
diff -u --recursive --new-file v1.3.53/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v1.3.53/linux/drivers/block/ide-tape.c	Sun Dec 17 11:43:12 1995
+++ linux/drivers/block/ide-tape.c	Thu Jan  4 07:43:40 1996
@@ -1,7 +1,7 @@
 /*
- * linux/drivers/block/ide-tape.c	Version 1.1 - ALPHA	Dec  14, 1995
+ * linux/drivers/block/ide-tape.c	Version 1.2 - ALPHA	Jan   1, 1996
  *
- * Copyright (C) 1995 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
+ * Copyright (C) 1995, 1996 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
  *
  * This driver was constructed as a student project in the software laboratory
  * of the faculty of electrical engineering in the Technion - Israel's
@@ -22,7 +22,7 @@
  * interface, on the other hand, creates new requests, adds them
  * to the request-list of the block device, and waits for their completion.
  *
- * Pipelined operation mode is now supported on writes.
+ * Pipelined operation mode is now supported on both reads and writes.
  *
  * The block device major and minor numbers are determined from the
  * tape's relative position in the ide interfaces, as explained in ide.c.
@@ -45,8 +45,18 @@
  * Our own ide-tape ioctl's can can be issued to either the block device or
  * the character device interface.
  *
- * Testing was done with a 2 GB CONNER CTMA 4000 IDE ATAPI Streaming Tape Drive.
+ * Maximal throughput with minimal bus load will usually be achieved in the
+ * following scenario:
+ *
+ *	1.	ide-tape is operating in the pipelined operation mode.
+ *	2.	All character device read/write requests consist of an
+ *		integral number of the tape's recommended data transfer unit
+ *		(which is shown on initialization and can be received with
+ *		 an ioctl).
+ *	3.	No buffering is performed by the user backup program.
  *
+ * Testing was done with a 2 GB CONNER CTMA 4000 IDE ATAPI Streaming Tape Drive.
+ * 
  * Ver 0.1   Nov  1 95   Pre-working code :-)
  * Ver 0.2   Nov 23 95   A short backup (few megabytes) and restore procedure
  *                        was successful ! (Using tar cvf ... on the block
@@ -135,6 +145,22 @@
  *                        downfalls as well, you may want to disable it.
  *                        A short explanation of the pipelined operation mode
  *                        is available below.
+ * Ver 1.2   Jan  1 96   Eliminated pipelined mode race condition.
+ *                       Added pipeline read mode. As a result, restores
+ *                        are now as fast as backups.
+ *                       Optimized shared interface behavior. The new behavior
+ *                        typically results in better IDE bus efficiency and
+ *                        higher tape throughput.
+ *                       Pre-calculation of the expected read/write request
+ *                        service time, based on the tape's parameters. In
+ *                        the pipelined operation mode, this allows us to
+ *                        adjust our polling frequency to a much lower value,
+ *                        and thus to dramatically reduce our load on Linux,
+ *                        without any decrease in performance.
+ *                       Implemented additional mtio.h operations.
+ *                       The recommended user block size is returned by
+ *                        the MTIOCGET ioctl.
+ *                       Additional minor changes.
  *
  * We are currently in an *alpha* stage. The driver is not complete and not
  * much tested. I would strongly suggest to:
@@ -153,34 +179,40 @@
  */
 
 /*
- * A short explanation of the pipelined operation mode.
+ * An overview of the pipelined operation mode.
  *
- * Pipelined mode is currently only implemented on writes. Reads are still
- * performed in the slow non-pipelined mode.
+ * In the pipelined write mode, we will usually just add requests to our
+ * pipeline and return immediately, before we even start to service them. The
+ * user program will then have enough time to prepare the next request while
+ * we are still busy servicing previous requests. In the pipelined read mode,
+ * the situation is similar - we add read-ahead requests into the pipeline,
+ * before the user even requested them.
+ *
+ * The pipeline can be viewed as a "safety net" which will be activated when
+ * the system load is high and prevents the user backup program from keeping up
+ * with the current tape speed. At this point, the pipeline will get
+ * shorter and shorter but the tape will still be streaming at the same speed.
+ * Assuming we have enough pipeline stages, the system load will hopefully
+ * decrease before the pipeline is completely empty, and the backup program
+ * will be able to "catch up" and refill the pipeline again.
+ * 
+ * When using the pipelined mode, it would be best to disable any type of
+ * buffering done by the user program, as ide-tape already provides all the
+ * benefits in the kernel, where it can be done in a more efficient way.
+ * As we will usually not block the user program on a request, the most
+ * efficient user code will then be a simple read-write-read-... cycle.
+ * Any additional logic will usually just slow down the backup process.
  *
- * The pipeline mode, when enough pipeline stages are available, manages to
- * keep the tape constantly streaming with the maximum device supported
- * throughput, regardless of the user backup program, since even when Linux
- * is busy doing other tasks, we still have job to be done.
- *
- * On my tape drive, using pipelined mode and giving the tape its own
- * interface and irq, I get a constant over 400 KBps throughput, which seems
- * to be the maximum throughput supported by my tape. When sharing the
- * interface between the tape and another ata-2 disk drive, I receive around
- * 350 KBps.
- *
- * Using the non-pipelined mode, I get anything between 150 to 380 KBps,
- * with the average being around 150 or 250 KBps, depending mainly on
- * the double buffering capabilities of the user backup program, but also
- * on some additional factors, such as the user block size and the ongoing
- * disk activity.
+ * Using the pipelined mode, I get a constant over 400 KBps throughput,
+ * which seems to be the maximum throughput supported by my tape.
  *
  * However, there are some downfalls:
  *
  *	1.	We use memory (for data buffers) in proportional to the number
  *		of pipeline stages (each stage is about 26 KB with my tape).
- *	2.	We cheat and postpone error codes to the user task. Again,
- *		the postponing period is proportional to the number of stages.
+ *	2.	In the pipelined write mode, we cheat and postpone error codes
+ *		to the user task. In read mode, the actual tape position
+ *		will be a bit further than the last requested block.
  *
  * Concerning (1):
  *
@@ -207,17 +239,46 @@
  *
  * Concerning (2):
  *
- *	In pipelined mode, ide-tape can not return accurate error codes to
- *      the user program since we usually just add the request to the
+ *	In pipelined write mode, ide-tape can not return accurate error codes
+ *	to the user program since we usually just add the request to the
  *      pipeline without waiting for it to be serviced. In case an error
  *      occurs, I will report it on the next user request.
  *
- *	For accurate error codes, you should disable pipelined mode.
+ *	In the pipelined read mode, subsequent read requests or forward
+ *	filemark spacing will perform correctly, as we preserve all blocks
+ *	and filemarks which we encountered during our excess read-ahead.
+ * 
+ *	For accurate tape positioning and error reporting, disabling
+ *	pipelined mode might be the best option.
  *
  * You can enable/disable/tune the pipelined operation mode by adjusting
  * the compile time parameters in ide-tape.h.
  */
- 
+
+/*
+ *	Possible improvements.
+ *
+ *	1.	Support for the ATAPI overlap protocol.
+ *
+ *		In order to maximize bus throughput, we currently use the DSC
+ *		overlap method which enables ide.c to service requests from the
+ *		other device while the tape is busy executing a command. The
+ *		DSC overlap method involves polling the tape's status register
+ *		for the DSC bit, and servicing the other device while the tape
+ *		isn't ready.
+ *
+ *		In the current QIC development standard (December 1995),
+ *		it is recommended that new tape drives will *in addition* 
+ *		implement the ATAPI overlap protocol, which is used for the
+ *		same purpose - efficient use of the IDE bus, but is interrupt
+ *		driven and thus has much less CPU overhead.
+ *
+ *		ATAPI overlap is likely to be supported in most new ATAPI
+ *		devices, including new ATAPI cdroms, and thus provides us
+ *		a method by which we can achieve higher throughput when
+ *		sharing a (fast) ATA-2 disk with any (slow) new ATAPI device.
+ */
+
 #include <linux/hdreg.h>
 #include <linux/types.h>
 #include <linux/string.h>
@@ -263,6 +324,7 @@
 #define	IDETAPE_INQUIRY_CMD		0x12
 #define	IDETAPE_ERASE_CMD		0x19
 #define	IDETAPE_MODE_SENSE_CMD		0x1a
+#define	IDETAPE_LOAD_UNLOAD_CMD		0x1b
 #define	IDETAPE_LOCATE_CMD		0x2b
 #define	IDETAPE_READ_POSITION_CMD	0x34
 
@@ -276,6 +338,14 @@
 #define	IDETAPE_SPACE_TO_EOD		3
 
 /*
+ *	Some defines for the LOAD UNLOAD command
+ */
+ 
+#define	IDETAPE_LU_LOAD_MASK		1
+#define	IDETAPE_LU_RETENSION_MASK	2
+#define	IDETAPE_LU_EOT_MASK		4
+
+/*
  *	Our ioctls - We will use 0x034n and 0x035n
  *
  *	Nothing special meanwhile.
@@ -345,6 +415,15 @@
 #define	IDETAPE_RQ_POSTPONED		0x1234
 
 /*
+ *	Error codes which are returned in rq->errors to the higher part
+ *	of the driver.
+ */
+
+#define	IDETAPE_RQ_ERROR_GENERAL	1 
+#define	IDETAPE_RQ_ERROR_FILEMARK	2
+#define	IDETAPE_RQ_ERROR_EOD		3
+
+/*
  *	ATAPI Task File Registers (Re-definition of the ATA Task File
  *	Registers for an ATAPI packet command).
  * 	From Table 3-2 of QIC-157C.
@@ -709,7 +788,6 @@
 void idetape_output_data (ide_drive_t *drive,void *buffer, unsigned long bcount);
 void idetape_discard_data (ide_drive_t *drive, unsigned long bcount);
 
-
 /*
  *	Packet command related functions.
  */
@@ -724,6 +802,7 @@
 void idetape_postpone_request (ide_drive_t *drive);
 void idetape_poll_for_dsc (unsigned long data);
 void idetape_poll_for_dsc_direct (unsigned long data);
+void idetape_put_back_postponed_request (ide_drive_t *drive);
 void idetape_media_access_finished (ide_drive_t *drive);
 
 /*
@@ -734,10 +813,16 @@
 void idetape_retry_pc (ide_drive_t *drive);
 void idetape_zero_packet_command (idetape_packet_command_t *pc);
 void idetape_queue_pc_head (ide_drive_t *drive,idetape_packet_command_t *pc,struct request *rq);
+void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result);
 
 idetape_packet_command_t *idetape_next_pc_storage (ide_drive_t *drive);
 struct request *idetape_next_rq_storage (ide_drive_t *drive);
 
+/*
+ *	idetape_end_request is used to finish servicing a request, and to
+ *	insert a pending pipeline request into the main device queue.
+ */
+ 
 void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
 
 /*
@@ -749,6 +834,7 @@
 void idetape_create_locate_cmd (idetape_packet_command_t *pc,unsigned long block,byte partition);
 void idetape_create_rewind_cmd (idetape_packet_command_t *pc);
 void idetape_create_write_filemark_cmd (idetape_packet_command_t *pc,int write_filemark);
+void idetape_create_load_unload_cmd (idetape_packet_command_t *pc,int cmd);
 void idetape_create_space_cmd (idetape_packet_command_t *pc,long count,byte cmd);
 void idetape_create_erase_cmd (idetape_packet_command_t *pc);
 void idetape_create_test_unit_ready_cmd (idetape_packet_command_t *pc);
@@ -763,7 +849,6 @@
 void idetape_request_sense_callback (ide_drive_t *drive);
 
 void idetape_display_inquiry_result (byte *buffer);
-void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result);
 
 /*
  *	Character device callback functions.
@@ -791,11 +876,26 @@
 int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count);
 
 /*
+ *	idetape_space_over_filemarks handles the MTFSF, MTFSFM, ... mtio.h
+ *	commands.
+ */
+ 
+int idetape_space_over_filemarks (ide_drive_t *drive,short mt_op,int mt_count);
+
+/*
+ *	idetape_add_chrdev_read_request is called from idetape_chrdev_read
+ *	to service a character device read request and add read-ahead
+ *	requests to our pipeline.
+ */
+ 
+int idetape_add_chrdev_read_request (ide_drive_t *drive,int blocks,char *buffer);
+
+/*
  *	idetape_add_chrdev_write_request adds a character device write
  *	request to the pipeline.
  */
  
-int idetape_add_chrdev_write_request (ide_drive_t *drive,int cmd,int blocks,char *buffer);
+int idetape_add_chrdev_write_request (ide_drive_t *drive,int blocks,char *buffer);
 
 /*
  *	idetape_queue_rw_tail will add a command to the tail of the device
@@ -814,6 +914,7 @@
 
 int idetape_position_tape (ide_drive_t *drive,unsigned long block);
 int idetape_rewind_tape (ide_drive_t *drive);
+int idetape_flush_tape_buffers (ide_drive_t *drive);
 
 /*
  *	Used to get device information
@@ -838,8 +939,10 @@
 void idetape_copy_buffer_to_stage (idetape_pipeline_stage_t *stage,char *buffer);
 void idetape_increase_max_pipeline_stages (ide_drive_t *drive);
 void idetape_add_stage_tail (ide_drive_t *drive,idetape_pipeline_stage_t *stage);
+void idetape_remove_stage_head (ide_drive_t *drive);
 void idetape_active_next_stage (ide_drive_t *drive);
-void idetape_empty_pipeline (ide_drive_t *drive);
+void idetape_empty_read_pipeline (ide_drive_t *drive);
+void idetape_empty_write_pipeline (ide_drive_t *drive);
 void idetape_insert_pipeline_into_queue (ide_drive_t *drive);
 
 /*
@@ -901,14 +1004,14 @@
 	struct idetape_id_gcw gcw;
 	unsigned short *ptr;
 	int support=1;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	unsigned short mask,i;
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		
 	ptr=(unsigned short *) &gcw;
 	*ptr=id->config;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Dumping ATAPI Identify Device tape parameters\n");
 	
 	printk ("Protocol Type: ");
@@ -1004,7 +1107,7 @@
 	else {
 		printk ("According to the device, fields 64-70 are not valid.\n");
 	}
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	/* Check that we can support this device */
 
@@ -1100,10 +1203,11 @@
 {
 	idetape_tape_t *tape=&(drive->tape);
 	unsigned int allocation_length;
-	
-#if IDETAPE_DEBUG
+	double service_time,nr_units;
+		
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Reached idetape_setup\n");
-#endif /* IDETAPE_DEBUG */	
+#endif /* IDETAPE_DEBUG_LOG */	
 	
 	drive->ready_stat = 0;			/* With an ATAPI device, we can issue packet commands */
 						/* regardless of the state of DRDY */
@@ -1117,22 +1221,18 @@
 	tape->busy=0;
 	tape->active_data_request=NULL;
 	tape->current_number_of_stages=0;
-	tape->first_stage=tape->last_stage=NULL;
-	tape->pipeline_was_full_once=0;
+	tape->first_stage=tape->next_stage=tape->last_stage=NULL;
 	tape->error_in_pipeline_stage=0;
-	tape->pipeline_locked=0;
-
 	tape->request_status=0;
-	tape->request_dsc_callback=0;
-	
+	tape->chrdev_direction=idetape_direction_none;
+
 #if IDETAPE_PIPELINE
 	tape->max_number_of_stages=IDETAPE_MIN_PIPELINE_STAGES;
-	printk ("ide-tape: Operating in pipelined (fast and tricky) write mode.\n");
+	printk ("ide-tape: Operating in pipelined (fast and tricky) operation mode.\n");
 #else
 	tape->max_number_of_stages=0;
-	printk ("ide-tape: Operating in non-pipelined (slow and safe) write mode.\n");
+	printk ("ide-tape: Operating in non-pipelined (slow and safe) operation mode.\n");
 #endif /* IDETAPE_PIPELINE */
-	printk ("ide-tape: Operating in non-pipelined (slow and safe) read mode.\n");
 
 	idetape_get_mode_sense_results (drive);
 
@@ -1152,6 +1252,58 @@
 		return;
 	}
 
+#if IDETAPE_ANTICIPATE_READ_WRITE_DSC
+
+	/*
+	 *	Cleverly select the DSC read/write polling frequency, based
+	 *	on the tape's speed, its recommended transfer unit, its
+	 *	internal buffer size and our operation mode.
+	 *
+	 *	In the pipelined operation mode we aim for "catching" the
+	 *	tape when its internal buffer is about 50% full. This will
+	 *	dramatically reduce our polling frequency and will also
+	 *	leave enough time for the ongoing request of the other device
+	 *	to complete before the buffer is completly empty. We will
+	 *	then completly refill the buffer with requests from our
+	 *	internal pipeline.
+	 *
+	 *	When operating in the non-pipelined operation mode, we
+	 *	can't allow ourself this luxury. Instead, we will try to take
+	 *	full advantage of the internal tape buffer by waiting only
+	 *	for one request to complete. This will increase our load
+	 *	on linux but will usually still fail to keep the tape
+	 *	constantly streaming.
+	 */
+
+	service_time=((double) tape->data_buffer_size/1024.0)/((double) tape->capabilities.speed*(1000.0/1024.0));
+	nr_units=(double) tape->capabilities.buffer_size*512.0/(double) tape->data_buffer_size;
+
+	if (tape->max_number_of_stages)	
+		tape->best_dsc_rw_frequency=(unsigned long) (0.5*nr_units*service_time*HZ);
+	else		
+		tape->best_dsc_rw_frequency=(unsigned long) (service_time*HZ);
+	
+	/*
+	 *	Ensure that the number we got makes sense.
+	 */
+
+	if (tape->best_dsc_rw_frequency > IDETAPE_DSC_READ_WRITE_LOWEST_FREQUENCY) {
+		printk ("ide-tape: Although the recommended polling period is %lu jiffies, \n",tape->best_dsc_rw_frequency);
+		printk ("ide-tape: we will use %u jiffies\n",IDETAPE_DSC_READ_WRITE_LOWEST_FREQUENCY);
+		printk ("ide-tape: (It may well be that we are wrong here)\n");
+		tape->best_dsc_rw_frequency = IDETAPE_DSC_READ_WRITE_LOWEST_FREQUENCY;
+	}
+
+	if (tape->best_dsc_rw_frequency < IDETAPE_DSC_READ_WRITE_FALLBACK_FREQUENCY) {
+		printk ("ide-tape: Although the recommended polling period is %lu jiffies, \n",tape->best_dsc_rw_frequency);
+		printk ("ide-tape: we will use %u jiffies\n",IDETAPE_DSC_READ_WRITE_FALLBACK_FREQUENCY);
+		tape->best_dsc_rw_frequency = IDETAPE_DSC_READ_WRITE_FALLBACK_FREQUENCY;
+	}
+
+#else
+	tape->best_dsc_rw_frequency=IDETAPE_DSC_READ_WRITE_FALLBACK_FREQUENCY;
+#endif /* IDETAPE_ANTICIPATE_READ_WRITE_DSC */
+
 	printk ("ide-tape: Tape speed - %d KBps. Recommended transfer unit - %d bytes.\n",tape->capabilities.speed,tape->data_buffer_size);
 
 	return;
@@ -1193,11 +1345,13 @@
 		printk ("ide-tape: Can't get tape parameters\n");
 		printk ("ide-tape: Assuming some default parameters\n");
 		tape->tape_block_size=512;
-		tape->capabilities.ctl=26*1024;
+		tape->capabilities.ctl=52;
+		tape->capabilities.speed=450;
+		tape->capabilities.buffer_size=6*52;
 		return;
 	}
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Dumping the results of the MODE SENSE packet command\n");
 	printk ("Mode Parameter Header:\n");
 	printk ("Mode Data Length - %d\n",header->mode_data_length);
@@ -1225,7 +1379,7 @@
 	printk ("Continuous transfer limits in blocks - %d\n",capabilities->ctl);
 	printk ("Current speed in KBps - %d\n",capabilities->speed);	
 	printk ("Buffer size - %d\n",capabilities->buffer_size*512);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 }
 
 /*
@@ -1280,36 +1434,43 @@
 
 	tape=&(drive->tape);
 	        
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_BUGS
 	if (tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
 		printk ("ide-tape: ide-tape.c bug - Two request sense in serial were issued\n");
-		/* ??? Need to rethink about that */		
 	}
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 
 	if (tape->failed_pc == NULL && pc->c[0] != IDETAPE_REQUEST_SENSE_CMD)
 		tape->failed_pc=pc;
 	tape->pc=pc;							/* Set the current packet command */
 
-	if (pc->retries > IDETAPE_MAX_PC_RETRIES) {
-		printk ("ide-tape: %s: I/O error, ",drive->name);
-		printk ("pc = %x, key = %x, asc = %x, ascq = %x\n",pc->c[0],tape->sense_key,tape->asc,tape->ascq);
-		printk ("ide-tape: Maximum retries reached - Giving up\n");
-		pc->error=1;					/* Giving up */
-		pc->active=0;
+	if (pc->retries > IDETAPE_MAX_PC_RETRIES || pc->abort) {
+
+		/*
+		 *	We will "abort" retrying a packet command in case
+		 *	a legitimate error code was received (crossing a
+		 *	filemark, for example). We will not log those errors.
+		 */
+
+		if (!pc->abort) {
+			printk ("ide-tape: %s: I/O error, ",drive->name);
+			printk ("pc = %x, key = %x, asc = %x, ascq = %x\n",pc->c[0],tape->sense_key,tape->asc,tape->ascq);
+			printk ("ide-tape: Maximum retries reached - Giving up\n");
+			pc->error=1;					/* Giving up */
+		}
 		tape->failed_pc=NULL;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_BUGS
 		if (pc->callback==NULL)
 			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
 		else
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 			(*pc->callback)(drive);
 		return;
 	}
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Retry number - %d\n",pc->retries);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	pc->retries++;
 
@@ -1319,7 +1480,6 @@
  */
  
 	pc->actually_transferred=0;					/* We haven't transferred any data yet */
-	pc->active=1;							/* Packet command started */
 	bcount.all=pc->request_transfer;				/* Request to transfer the entire buffer at once */
 
 									/* Initialize the task file registers */
@@ -1366,37 +1526,35 @@
 void idetape_pc_intr (ide_drive_t *drive)
 
 {
-	idetape_tape_t *tape;
+	idetape_tape_t *tape=&(drive->tape);
 	idetape_status_reg_t status;
 	idetape_bcount_reg_t bcount;
         idetape_ireason_reg_t ireason;
-	idetape_packet_command_t *pc;
-	
+	idetape_packet_command_t *pc=tape->pc;
 	unsigned long temp;
 
-	tape=&(drive->tape);
-	
 	status.all=IN_BYTE (IDETAPE_STATUS_REG);		/* Clear the interrupt */
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Reached idetape_pc_intr interrupt handler\n");
-#endif /* IDETAPE_DEBUG */	
+#endif /* IDETAPE_DEBUG_LOG */	
 
-	pc=tape->pc;						/* Current packet command */
-		
 	if (!status.b.drq) {					/* No more interrupts */
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("Packet command completed\n");
 		printk ("Total bytes transferred: %lu\n",pc->actually_transferred);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
+		
+		sti ();
+
 		if (status.b.check) {					/* Error detected */
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	/*
 	 *	Without debugging, we only log an error if we decided to
 	 *	give up retrying.
 	 */
 			printk ("ide-tape: %s: I/O error, ",drive->name);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 			idetape_retry_pc (drive);			/* Retry operation */
 			return;
 		}
@@ -1406,14 +1564,13 @@
 			idetape_postpone_request (drive);		/* Allow ide.c to handle other requests */
 			return;
 		}
-		pc->active=0;
 		if (tape->failed_pc == pc)
 			tape->failed_pc=NULL;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_BUGS
 		if (pc->callback==NULL)			
 			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
 		else
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 			(*pc->callback)(drive);			/* Command finished - Call the callback function */
 		return;
 	}
@@ -1446,7 +1603,7 @@
 				printk ("Allowing transfer\n");
 		}
 	}
-#if IDETAPE_DEBUG	
+#if IDETAPE_DEBUG_BUGS	
 	if (bcount.all && !pc->buffer) {	
 		printk ("ide-tape: ide-tape.c bug - Buffer not set in idetape_pc_intr. Discarding data.\n");
 		
@@ -1459,14 +1616,14 @@
 		else {	/* ??? */
 		}
 	}
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 	if (pc->writing)
 		idetape_output_data (drive,pc->current_position,bcount.all);	/* Write the current buffer */
 	else
 		idetape_input_data (drive,pc->current_position,bcount.all);	/* Read the current buffer */
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: %s %d bytes\n",pc->writing ? "Wrote":"Received",bcount.all);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	pc->actually_transferred+=bcount.all;					/* Update the current position */
 	pc->current_position+=bcount.all;
 
@@ -1482,21 +1639,17 @@
 void idetape_postpone_request (ide_drive_t *drive)
 
 {
-	idetape_tape_t *tape;
-	unsigned long flags;
+	idetape_tape_t *tape=&(drive->tape);
 	struct request *rq;
 	idetape_status_reg_t status;
 	
-	tape=&(drive->tape);
-	status.all=IN_BYTE (IDETAPE_STATUS_REG);
-
-	sti ();
-	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_postpone_request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
 	if (tape->postponed_rq != NULL)
 		printk ("ide-tape.c bug - postponed_rq not NULL in idetape_postpone_request\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 
 	tape->dsc_timer.expires=jiffies + tape->dsc_polling_frequency;	/* Set timer to poll for */
 	tape->dsc_timeout=jiffies+IDETAPE_DSC_TIMEOUT;			/* actual completion */
@@ -1510,14 +1663,16 @@
 
 	tape->postponed_rq = rq = HWGROUP(drive)->rq;
 	rq->rq_status = IDETAPE_RQ_POSTPONED;	
-	save_flags(flags);cli ();					/* Let ide.c handle another request */
 	blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
 	HWGROUP(drive)->rq = NULL;
-	restore_flags(flags);
-	
-	tape->request_status=0;
-	tape->request_dsc_callback=0;
+
+	/*
+	 *	Check the status again - Maybe we can save one polling period.
+	 */
+	 
+	status.all=IN_BYTE (IDETAPE_STATUS_REG);
 	tape->last_status=status.all;
+	tape->request_status=1;	
 	
 	tape->dsc_polling_start=jiffies;
 	add_timer(&(tape->dsc_timer));		/* Activate the polling timer */
@@ -1533,22 +1688,16 @@
 
 {
 	ide_drive_t *drive=(ide_drive_t *) data;
-	ide_hwgroup_t *hwgroup=HWGROUP (drive);
 	idetape_tape_t *tape=&(drive->tape);
 	idetape_status_reg_t status;
-	unsigned long flags;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("%s: idetape_poll_for_dsc_direct called\n",drive->name);
-#endif /* IDETAPE_DEBUG */	
+#endif /* IDETAPE_DEBUG_LOG */	
 
-	save_flags (flags);cli ();
 	OUT_BYTE(drive->select.all,IDE_SELECT_REG);
 	status.all=IN_BYTE (IDETAPE_STATUS_REG);
-	if (hwgroup->drive != NULL)
-		OUT_BYTE (hwgroup->drive->select.all,IDE_SELECT_REG);
-	restore_flags (flags);
-
+	
 	if (status.b.dsc) {					/* DSC received */
 		tape->dsc_received=1;
 		del_timer (&(tape->dsc_timer));			/* Stop polling and put back the postponed */
@@ -1579,17 +1728,15 @@
  *	by idetape_postpone_request) to poll for the DSC bit
  *	in the status register.
  *
- *	We take care not to perform any tape access and not to touch the
- *	device request queue if the driver is accessing the other device.
- *	We will instead ask ide.c to perform those functions on the next
- *	call to do_request, at the point in which the other device is idle.
- *
- *	However, in case the other device is already idle, we will read
- *	the status register from our timer handler.
- *
- *	I am also a bit paranoid with the use of cli (), all through the
- *	code. I still need to think harder about each one whether we can
- *	avoid it and still be free of race conditions ...
+ *	We take care not to perform any tape access if the driver is
+ *	accessing the other device. We will instead ask ide.c to sample
+ *	the tape status register on our behalf in the next call to do_request,
+ *	at the point in which the other device is idle, or assume that
+ *	DSC was received even though we won't verify it (but when we assume
+ *	that, it will usually have a solid basis).
+ *
+ *	The use of cli () below is a must, as we inspect and change
+ *	the device request list while another request is active.
  */
  
 void idetape_poll_for_dsc (unsigned long data)
@@ -1600,14 +1747,13 @@
 	idetape_tape_t *tape=&(drive->tape);
 	struct blk_dev_struct *bdev = &blk_dev[major];
 	unsigned long flags;
-	
 	idetape_status_reg_t status;
 
-	save_flags (flags);cli ();
-			
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("%s: idetape_poll_for_dsc called\n",drive->name);
-#endif /* IDETAPE_DEBUG */	
+#endif /* IDETAPE_DEBUG_LOG */	
+
+	save_flags (flags);cli ();
 
 	/*
 	 *	Check if the other device is idle. If there are no requests,
@@ -1615,63 +1761,159 @@
 	 */
 
 	if (bdev->current_request == NULL) {
-		restore_flags (flags);
+		sti ();
 		idetape_poll_for_dsc_direct (data);
 		return;
 	}
-	
-	if (bdev->current_request->next == NULL) {
-		/*
-		 *	There will not be another request after the currently
-		 *	ongoing request, so ide.c won't be able to sample
-		 *	the status register on our behalf in do_request. Just
-		 *	give up and poll again (in a faster frequency), until
-		 *	we are lucky.
-		 */
+
+	/*
+	 *	If DSC was received, re-insert our postponed request into
+	 *	the request queue (using ide_next).
+	 */
+
+	status.all=tape->last_status;
+
+	if (status.b.dsc) {					/* DSC received */
+		tape->dsc_received=1;
+		idetape_put_back_postponed_request (drive);
+		del_timer (&(tape->dsc_timer));
 		restore_flags (flags);
-		tape->dsc_timer.expires = jiffies + tape->dsc_polling_frequency/2;
-		add_timer(&(tape->dsc_timer));
 		return;
 	}
-	
+
 	/*
-	 *	We now know that:
+	 *	At this point, DSC may have been received, but we can't
+	 *	check it. We now have two options:
+	 *
+	 *		1.	The "simple" method - We can continue polling
+	 *			until we know the value of DSC.
+	 *
+	 *	but we also have a more clever option :-)
+	 *
+	 *		2.	We can sometimes more or less anticipate in
+	 *			advance how much time it will take for
+	 *			the tape to perform the request. This is the
+	 *			place to take advantage of this !
 	 *
-	 *		1.	The ide driver is potentially accessing
-	 *			the other device -- We can not touch it.
-	 *		2.	do_request will be called after the current
-	 *			request is finished.
+	 *			We can assume that DSC was received, put
+	 *			back our request, and hope that we will have
+	 *			a "cache hit". This will only work when
+	 *			we haven't initiated the packet command yet,
+	 *			but this is the common read/write case. As
+	 *			for the slower media access commands, fallback
+	 *			to method 1 above.
 	 *
-	 *	We will therefor ask ide.c to perform the tasks on our behalf.
+	 *	When using method 2, we can also take advantage of the
+	 *	knowledge of the tape's internal buffer size - We can
+	 *	precalculate the time it will take for the tape to complete
+	 *	servicing not only one request, but rather, say, 50% of its
+	 *	internal buffer. The polling period will then be much larger,
+	 *	decreasing our load on Linux, and we will also call
+	 *	idetape_postpone_request less often, as there will usually
+	 *	be more room in the internal tape buffer while we are in
+	 *	idetape_do_request.
+	 *
+	 *	For this method to work well, the ongoing request of the
+	 *	other device should be serviced by the time the tape is
+	 *	still working on its remaining 50% internal buffer. This
+	 *	will usually happen when the other device is much faster
+	 *	than the tape.
+	 */
+
+#if IDETAPE_ANTICIPATE_READ_WRITE_DSC
+
+	/*
+	 *	Method 2.
+	 *
+	 *	There is a high chance that DSC was received, even though
+	 *	we couldn't verify it. Let's hope that it's a "cache hit"
+	 *	rather than a "cache miss". Someday I will probably add a
+	 *	feedback loop around the number of "cache hits" which will
+	 *	fine-tune the polling period.
 	 */
 	 
-	status.all=tape->last_status;
-	
-	if (status.b.dsc) {					/* DSC received */
-		tape->dsc_received=1;
-		del_timer (&(tape->dsc_timer));			/* Stop polling and request ide.c to call */
-		tape->request_dsc_callback=1;			/* our idetape_put_back_postponed_request later */
-		restore_flags (flags);		
+	if (tape->postponed_rq->cmd != IDETAPE_PACKET_COMMAND_REQUEST_TYPE1) {
+
+		/*
+		 *	We can use this method only when the packet command
+		 *	was still not initiated.
+		 */
+		 
+		idetape_put_back_postponed_request (drive);
+		del_timer (&(tape->dsc_timer));
+		restore_flags (flags);
 		return;
 	}
+#endif /* IDETAPE_ANTICIPATE_READ_WRITE_DSC */
 
-	if (jiffies > tape->dsc_timeout) 	{ 		/* Timeout */
-		tape->dsc_received=1;
+	/*
+	 *	Fallback to method 1.
+	 */
+	 	
+	if (bdev->current_request->next == NULL) {
+		/*
+		 *	There will not be another request after the currently
+		 *	ongoing request, so ide.c won't be able to sample
+		 *	the status register on our behalf in do_request.
+		 *
+		 *	In case we are waiting for DSC before the packet
+		 *	command was initiated, we will put back our postponed
+		 *	request and have another look at the status register
+		 *	in idetape_do_request, as done in method 2 above.
+		 *
+		 *	In case we already initiated the command, we can't
+		 *	put it back, but it is anyway a slow media access
+		 *	command. We will just give up and poll again until
+		 *	we are lucky.
+		 */
+
+		if (tape->postponed_rq->cmd == IDETAPE_PACKET_COMMAND_REQUEST_TYPE1) {
+
+			/*
+			 *	Media access command - Poll again.
+			 *
+			 *	We set tape->request_status to 1, just in case
+			 *	other requests are added while we are waiting.
+			 */
+			 
+			tape->request_status=1;
+			restore_flags (flags);
+			tape->dsc_timer.expires = jiffies + tape->dsc_polling_frequency;
+			add_timer(&(tape->dsc_timer));
+			return;
+		}
+		
+		/*
+		 *	The packet command hasn't been sent to the tape yet -
+		 *	We can safely put back the request and have another
+		 *	look at the status register in idetape_do_request.
+		 */
+
+		idetape_put_back_postponed_request (drive);
 		del_timer (&(tape->dsc_timer));
-		/* ??? */
-		tape->request_dsc_callback=1;
 		restore_flags (flags);
 		return;
 	}
 
 	/*
-	 *	Request ide.c to sample for us the tape's status register on
-	 *	the next time in which it can be safely done.
+	 *	There will be another request after the current request.
+	 *
+	 *	Request ide.c to sample for us the tape's status register
+	 *	before the next request.
 	 */
 
 	tape->request_status=1;
 	restore_flags (flags);
-		 
+
+	if (jiffies > tape->dsc_timeout) 	{ 		/* Timeout */
+		tape->dsc_received=0;
+		/* ??? */
+		idetape_put_back_postponed_request (drive);
+		del_timer (&(tape->dsc_timer));
+		restore_flags (flags);
+		return;
+	}
+
 	/* Poll again */
 	
 	if (jiffies - tape->dsc_polling_start > IDETAPE_FAST_SLOW_THRESHOLD)
@@ -1683,9 +1925,8 @@
 }
 
 /*
- *	idetape_put_back_postponed_request gets called by do_request
- *	in ide.c when we decided to stop polling for DSC and continue
- *	servicing our postponed request.
+ *	idetape_put_back_postponed_request gets called when we decided to
+ *	stop polling for DSC and continue servicing our postponed request.
  */
 
 void idetape_put_back_postponed_request (ide_drive_t *drive)
@@ -1693,10 +1934,15 @@
 {
 	idetape_tape_t *tape = &(drive->tape);
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Putting back postponed request\n");
-#endif /* IDETAPE_DEBUG */
-
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+	if (tape->postponed_rq == NULL) {
+		printk ("tape->postponed_rq is NULL in put_back_postponed_request\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
 	(void) ide_do_drive_cmd (drive, tape->postponed_rq, ide_next);
 
 	/*
@@ -1723,10 +1969,9 @@
 	status.all=IN_BYTE (IDETAPE_STATUS_REG);
 
 	if (tape->dsc_received) {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("DSC received\n");
-#endif /* IDETAPE_DEBUG */
-		pc->active=0;
+#endif /* IDETAPE_DEBUG_LOG */
 		if (status.b.check) {					/* Error detected */
 			printk ("ide-tape: %s: I/O error, ",drive->name);
 			idetape_retry_pc (drive);			/* Retry operation */
@@ -1735,26 +1980,25 @@
 		pc->error=0;
 		if (tape->failed_pc == pc)
 			tape->failed_pc=NULL;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_BUGS
 		if (pc->callback==NULL)
 			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
 		else
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 			(*pc->callback)(drive);
 
 		return;
 	}
 	else {
-		pc->active=0;
 		printk ("ide-tape: %s: DSC timeout.\n",drive->name);
 		/* ??? */
 		pc->error=1;
 		tape->failed_pc=NULL;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_BUGS
 		if (pc->callback==NULL)
 			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
 		else
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 			(*pc->callback)(drive);
 		return;
 	}
@@ -1797,17 +2041,16 @@
 	tape=&(drive->tape);
 	rq=HWGROUP(drive)->rq;
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Reached idetape_pc_callback\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	if (!tape->pc->error) {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("Request completed\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		idetape_end_request (1,HWGROUP (drive));
 	}
 	else {
-		printk ("Aborting request\n");
 		idetape_end_request (0,HWGROUP (drive));
 	}
 	return;
@@ -1822,21 +2065,26 @@
 
 	tape=&(drive->tape);	
 	rq=HWGROUP(drive)->rq;
-#if IDETAPE_DEBUG	
+#if IDETAPE_DEBUG_LOG	
 	printk ("ide-tape: Reached idetape_read_callback\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	tape->block_address+=tape->pc->actually_transferred/tape->tape_block_size;
 	if (!tape->pc->error) {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("Request completed\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		rq->sector+=rq->current_nr_sectors;
 		rq->nr_sectors-=rq->current_nr_sectors;
 		rq->current_nr_sectors=0;
 		idetape_end_request (1,HWGROUP (drive));
 	}
 	else {
-		printk ("Aborting request\n");
+		rq->errors=tape->pc->error;
+		switch (rq->errors) {
+			case IDETAPE_RQ_ERROR_FILEMARK:
+			case IDETAPE_RQ_ERROR_EOD:
+				break;
+		}
 		idetape_end_request (0,HWGROUP (drive));
 	}
 	return;
@@ -1850,21 +2098,20 @@
 	
 	tape=&(drive->tape);
 	rq=HWGROUP(drive)->rq;
-#if IDETAPE_DEBUG	
+#if IDETAPE_DEBUG_LOG	
 	printk ("ide-tape: Reached idetape_write_callback\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	tape->block_address+=tape->pc->actually_transferred/tape->tape_block_size;
 	if (!tape->pc->error) {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("Request completed\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		rq->sector+=rq->current_nr_sectors;
 		rq->nr_sectors-=rq->current_nr_sectors;
 		rq->current_nr_sectors=0;
 		idetape_end_request (1,HWGROUP (drive));
 	}
 	else {
-		printk ("Aborting request\n");
 		idetape_end_request (0,HWGROUP (drive));
 	}
 	return;
@@ -1948,9 +2195,9 @@
 void idetape_create_inquiry_cmd (idetape_packet_command_t *pc)
 
 {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating INQUIRY packet command\n");
-#endif /* IDETAPE_DEBUG */	
+#endif /* IDETAPE_DEBUG_LOG */	
 	pc->request_transfer=36;
 	pc->callback=&idetape_inquiry_callback;
 	pc->writing=0;
@@ -1979,7 +2226,7 @@
 		printk ("Assuming QIC-157C format.\n");
 	}
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Dumping INQUIRY command results:\n");
 	printk ("Response Data Format: %d - ",result->response_format);
 	switch (result->response_format) {
@@ -2031,7 +2278,7 @@
 	printk ("Vendor Identification: %s\n",result->vendor_id);
 	printk ("Product Identification: %s\n",result->product_id);
 	printk ("Product Revision Level: %s\n",result->revision_level);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	if (result->device_type != 1)
 		printk ("Device type is not set to tape\n");
@@ -2048,9 +2295,9 @@
 void idetape_create_request_sense_cmd (idetape_packet_command_t *pc)
 
 {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating REQUEST SENSE packet command\n");
-#endif /* IDETAPE_DEBUG */	
+#endif /* IDETAPE_DEBUG_LOG */	
 	pc->request_transfer=18;
 	pc->callback=&idetape_request_sense_callback;
 	pc->writing=0;
@@ -2063,19 +2310,15 @@
 void idetape_request_sense_callback (ide_drive_t *drive)
 
 {
-	idetape_tape_t *tape;
-	struct request *rq;
+	idetape_tape_t *tape=&(drive->tape);
 
-	tape=&(drive->tape);	
-	rq=HWGROUP(drive)->rq;
-	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Reached idetape_request_sense_callback\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	if (!tape->pc->error) {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("Request completed\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		idetape_analyze_error (drive,(idetape_request_sense_result_t *) tape->pc->buffer);
 		idetape_end_request (1,HWGROUP (drive));
 	}
@@ -2095,28 +2338,41 @@
 void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result)
 
 {
-	idetape_tape_t *tape;
-	
-	tape=&(drive->tape);
+	idetape_tape_t *tape=&(drive->tape);
+	idetape_packet_command_t *pc=tape->failed_pc;
+		
 	tape->sense_key=result->sense_key;
 	tape->asc=result->asc;
 	tape->ascq=result->ascq;
-#if IDETAPE_DEBUG	
+	
+#if IDETAPE_DEBUG_LOG	
 	/*
 	 *	Without debugging, we only log an error if we decided to
 	 *	give up retrying.
 	 */
-	printk ("ide-tape: sense key = %x, asc = %x, ascq = %x\n",result->sense_key,result->asc,result->ascq);
-#endif /* IDETAPE_DEBUG */	
-	return;	
+	printk ("ide-tape: pc = %x, sense key = %x, asc = %x, ascq = %x\n",pc->c[0],result->sense_key,result->asc,result->ascq);
+#endif /* IDETAPE_DEBUG_LOG */
+
+	if (pc->c[0] == IDETAPE_READ_CMD) {
+		if (result->filemark) {
+			pc->error=IDETAPE_RQ_ERROR_FILEMARK;
+			pc->abort=1;
+			return;
+		}
+		if (result->sense_key == 8) {
+			pc->error=IDETAPE_RQ_ERROR_EOD;
+			pc->abort=1;
+			return;
+		}
+	}
 }
 
 void idetape_create_test_unit_ready_cmd (idetape_packet_command_t *pc)
 
 {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating TEST UNIT READY packet command\n");
-#endif /* IDETAPE_DEBUG */	
+#endif /* IDETAPE_DEBUG_LOG */	
 	pc->request_transfer=0;
 	pc->buffer=NULL;
 	pc->current_position=NULL;
@@ -2132,9 +2388,9 @@
 {
 	unsigned long *ptr;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating LOCATE packet command\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	pc->request_transfer=0;
 	pc->buffer=NULL;
 	pc->current_position=NULL;
@@ -2154,9 +2410,9 @@
 void idetape_create_rewind_cmd (idetape_packet_command_t *pc)
 
 {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating REWIND packet command\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	pc->request_transfer=0;
 	pc->buffer=NULL;
 	pc->current_position=NULL;
@@ -2176,9 +2432,9 @@
 void idetape_create_mode_sense_cmd (idetape_packet_command_t *pc,byte page_code)
 
 {
-#if IDETAPE_DEBUG
-		printk ("ide-tape: Creating MODE SENSE packet command - Page %d\n",page_code);
-#endif /* IDETAPE_DEBUG */
+#if IDETAPE_DEBUG_LOG
+	printk ("ide-tape: Creating MODE SENSE packet command - Page %d\n",page_code);
+#endif /* IDETAPE_DEBUG_LOG */
 
 	pc->wait_for_dsc=0;
 	pc->callback=&idetape_pc_callback;
@@ -2209,11 +2465,11 @@
 void idetape_create_write_filemark_cmd (idetape_packet_command_t *pc,int write_filemark)
 
 {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Creating WRITE FILEMARK packet command\n");
 	if (!write_filemark)
 		printk ("which will only flush buffered data\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	pc->request_transfer=0;
 	pc->buffer=NULL;
 	pc->current_position=NULL;
@@ -2228,13 +2484,32 @@
 		pc->c [4]=1;
 }
 
+void idetape_create_load_unload_cmd (idetape_packet_command_t *pc,int cmd)
+
+{
+#if IDETAPE_DEBUG_LOG
+	printk ("Creating LOAD UNLOAD packet command, cmd=%d\n",cmd);
+#endif /* IDETAPE_DEBUG_LOG */
+	pc->request_transfer=0;
+	pc->buffer=NULL;
+	pc->current_position=NULL;
+	pc->buffer_size=0;
+	pc->wait_for_dsc=1;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+		
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_LOAD_UNLOAD_CMD;
+	pc->c [4]=cmd;
+}
+
 void idetape_create_erase_cmd (idetape_packet_command_t *pc)
 
 {
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Creating ERASE command\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	pc->request_transfer=0;
 	pc->buffer=NULL;
@@ -2262,9 +2537,9 @@
 		} b;
 	} original;
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating READ packet command\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	original.all=length;
 
@@ -2295,9 +2570,9 @@
 		} b;
 	} original;
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating SPACE packet command\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	original.all=count;
 
@@ -2332,9 +2607,9 @@
 		} b;
 	} original;
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating WRITE packet command\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	original.all=length;
 
@@ -2355,9 +2630,9 @@
 void idetape_create_read_position_cmd (idetape_packet_command_t *pc)
 
 {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating READ POSITION packet command\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	pc->request_transfer=20;
 	pc->wait_for_dsc=0;
@@ -2378,20 +2653,20 @@
 	
 	tape=&(drive->tape);
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Reached idetape_read_position_callback\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	rq=HWGROUP(drive)->rq;
 	
 	if (!tape->pc->error) {
 		result=(idetape_read_position_result_t *) tape->pc->buffer;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("Request completed\n");
 		printk ("Dumping the results of the READ POSITION command\n");
 		printk ("BOP - %s\n",result->bop ? "Yes":"No");
 		printk ("EOP - %s\n",result->eop ? "Yes":"No");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		if (result->bpu) {
 			printk ("ide-tape: Block location is unknown to the tape\n");
 			printk ("Aborting request\n");
@@ -2399,9 +2674,9 @@
 			idetape_end_request (0,HWGROUP (drive));
 		}
 		else {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 			printk ("Block Location - %lu\n",idetape_swap_long (result->first_block));
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 			tape->block_address=idetape_swap_long (result->first_block);
 			tape->block_address_valid=1;
 			idetape_end_request (1,HWGROUP (drive));
@@ -2432,23 +2707,23 @@
 	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
 	pc.current_position=pc.temp_buffer;
 
-#if IDETAPE_DEBUG	
+#if IDETAPE_DEBUG_LOG	
 	printk ("ide-tape: Reached idetape_blkdev_ioctl\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	switch (cmd) {
 		case IDETAPE_INQUIRY_IOCTL:
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 			printk ("Adding INQUIRY packet command to the tail of the request queue\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 			idetape_create_inquiry_cmd (&pc);
 			pc.buffer=pc.temp_buffer;
 			pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
 			pc.current_position=pc.temp_buffer;
 			return (idetape_queue_pc_tail (drive,&pc));
 		case IDETAPE_LOCATE_IOCTL:
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 			printk ("Adding LOCATE packet command to the tail of the request queue\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 			idetape_create_locate_cmd (&pc,arg,0);
 			retval=idetape_queue_pc_tail (drive,&pc);
 			if (retval!=0) return (retval);
@@ -2474,10 +2749,6 @@
 
 /*
  *	idetape_end_request is used to end a request.
- *
- *	It is very similiar to ide_end_request, with a major difference - If
- *	we are handling our own requests rather than requests which originate
- *	in the buffer cache, we set rq->errors to 1 if the request failed.
  */
 
 void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
@@ -2486,51 +2757,58 @@
 	ide_drive_t *drive = hwgroup->drive;
 	struct request *rq = hwgroup->rq;
 	idetape_tape_t *tape = &(drive->tape);
-	
-							/* Our own originated request */
-	rq->errors=!uptodate;				/* rq->errors will tell us if the request was successfull */
-	
-	if (tape->active_data_request == rq) {		/* The request was a data transfer request */
-#if IDETAPE_DEBUG
-		if (!tape->max_number_of_stages)
-			printk ("ide-tape: non pipelined mode bug\n");
-		printk ("Finished our active data request\n");
-		printk ("Requests in pipeline: %d\n",tape->current_number_of_stages);
-#endif /* IDETAPE_DEBUG */
-		if (rq->errors)
-			tape->error_in_pipeline_stage=1;
+	unsigned int major = HWIF(drive)->major;
+	struct blk_dev_struct *bdev = &blk_dev[major];
 
-		/*
-		 *	Pass to the next stage, but avoid a possible
-		 *	race condition which could occur since
-		 *	active_data_request is set to NULL until
-		 *	idetape_active_next_stage returns, and in that
-		 *	time, the higher level of the driver can get
-		 *	an inaccurate sampling of this variable.
-		 */
-		 
-		tape->pipeline_locked=1;
-		tape->active_data_request = NULL;
-		if (tape->first_stage == NULL) {
-			tape->pipeline_locked=0;
+#if IDETAPE_DEBUG_LOG
+	printk ("Reached idetape_end_request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+	bdev->current_request=rq;			/* Since we may have taken it out */
+
+	if (!rq->errors)				/* In case rq->errors is already set, */
+		rq->errors=!uptodate;			/* we won't change it. */
+	
+	if (tape->active_data_request == rq) {		/* The request was a pipelined data transfer request */
+
+		if (rq->cmd == IDETAPE_READ_REQUEST) {
+#if IDETAPE_DEBUG_BUGS
+			if (tape->active_stage == NULL)
+				printk ("ide-tape: bug: active_stage is NULL in idetape_end_request\n");
+			else				
+#endif /* IDETAPE_DEBUG_BUGS */
+			idetape_copy_buffer_to_stage (tape->active_stage,tape->data_buffer);
+		}
+
+		tape->active_stage=NULL;
+		tape->active_data_request=NULL;
+
+		if (rq->cmd == IDETAPE_WRITE_REQUEST) {
+			if (rq->errors)
+				tape->error_in_pipeline_stage=1;
+			idetape_remove_stage_head (drive);
+		}
+		
+		if (tape->next_stage == NULL) {
 			idetape_increase_max_pipeline_stages (drive);
+			ide_end_drive_cmd (drive, 0, 0);
 			return;
 		}
+
 		idetape_active_next_stage (drive);
-		tape->pipeline_locked=0;
-#if IDETAPE_DEBUG
-		printk ("Using ide_end\n");
-#endif /* IDETAPE_DEBUG */
 
 		/*
 		 *	Insert the next request into the request queue.
 		 *
-		 *	We currently give higher priority to the other devie
-		 *	by using ide_end. ide_next can be used to give us
-		 *	a higher priority.
+		 *	The choice of using ide_next or ide_end is now left
+		 *	to the user.
 		 */
 		 
-		ide_do_drive_cmd (drive,tape->active_data_request,ide_end);
+#if IDETAPE_LOW_TAPE_PRIORITY
+		(void) ide_do_drive_cmd (drive,tape->active_data_request,ide_end);
+#else
+		(void) ide_do_drive_cmd (drive,tape->active_data_request,ide_next);
+#endif /* IDETAPE_LOW_TAPE_PRIORITY */
 	}
 	ide_end_drive_cmd (drive, 0, 0);
 }
@@ -2544,13 +2822,15 @@
 {
 	idetape_tape_t *tape=&(drive->tape);
 	idetape_packet_command_t *pc;
+	unsigned int major = HWIF(drive)->major;
+	struct blk_dev_struct *bdev = &blk_dev[major];
 	idetape_status_reg_t status;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Current request:\n");
 	printk ("rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
 	printk ("sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	if (!IDETAPE_REQUEST_CMD (rq->cmd)) {
 
@@ -2569,6 +2849,29 @@
 		return;
 	}
 
+	/*
+	 *	This is an important point. We will try to remove our request
+	 *	from the block device request queue while we service the
+	 *	request. Note that the request must be returned to
+	 *	bdev->current_request before the next call to
+	 *	ide_end_drive_cmd or ide_do_drive_cmd to conform with the
+	 *	normal behavior of the IDE driver, which leaves the active
+	 *	request in bdev->current_request during I/O.
+	 *
+	 *	This will eliminate fragmentation of disk/cdrom requests
+	 *	around a tape request, now that we are using ide_next to
+	 *	insert pending pipeline requests, since we have only one
+	 *	ide-tape.c data request in the device request queue, and
+	 *	thus once removed, ll_rw_blk.c will only see requests from
+	 *	the other device.
+	 *
+	 *	The potential fragmentation inefficiency was pointed to me
+	 *	by Mark Lord.
+	 */
+	 
+	if (rq->next != NULL && rq->rq_dev != rq->next->rq_dev)
+		bdev->current_request=rq->next;
+
 	/* Retry a failed packet command */
 
 	if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
@@ -2579,13 +2882,13 @@
 	/* Check if we have a postponed request */
 	
 	if (tape->postponed_rq != NULL) {
- #if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_BUGS
 		if (tape->postponed_rq->rq_status != RQ_ACTIVE || rq != tape->postponed_rq) {
 			printk ("ide-tape: ide-tape.c bug - Two DSC requests were queued\n");
 			idetape_end_request (0,HWGROUP (drive));
 			return;
 		}
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 		if (rq->cmd == IDETAPE_PACKET_COMMAND_REQUEST_TYPE1) {
 	
 			/* Media access command */
@@ -2606,15 +2909,12 @@
 	
 	switch (rq->cmd) {
 		case IDETAPE_READ_REQUEST:
-#if IDETAPE_DEBUG
-			printk ("ide-tape: Handling our own (not buffer cache originated) READ request\n");
-#endif /* IDETAPE_DEBUG */			
 			status.all=IN_BYTE (IDETAPE_STATUS_REG);
 			if (!status.b.dsc) {				/* Tape buffer not ready to accept r/w command */
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 				printk ("ide-tape: DSC != 1 - Postponing read request\n");
-#endif /* IDETAPE_DEBUG */	
-				tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+#endif /* IDETAPE_DEBUG_LOG */	
+				tape->dsc_polling_frequency=tape->best_dsc_rw_frequency;
 				idetape_postpone_request (drive);	/* Allow ide.c to process requests from */
 				return;
 			}			
@@ -2632,16 +2932,12 @@
 			return;
 		
 		case IDETAPE_WRITE_REQUEST:
-#if IDETAPE_DEBUG
-			printk ("ide-tape: Handling our own (not buffer cache originated) WRITE request\n");
-#endif /* IDETAPE_DEBUG */			
-
 			status.all=IN_BYTE (IDETAPE_STATUS_REG);
 			if (!status.b.dsc) {				/* Tape buffer not ready to accept r/w command */
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 				printk ("ide-tape: DSC != 1 - Postponing write request\n");
-#endif /* IDETAPE_DEBUG */	
-				tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+#endif /* IDETAPE_DEBUG_LOG */	
+				tape->dsc_polling_frequency=tape->best_dsc_rw_frequency;
 				idetape_postpone_request (drive);	/* Allow ide.c to process requests from */
 				return;
 			}			
@@ -2667,12 +2963,12 @@
  */
 			status.all=IN_BYTE (IDETAPE_STATUS_REG);
 			if (!status.b.dsc) {				/* Tape buffers are still not ready */
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 				printk ("ide-tape: DSC != 1 - Postponing packet command request\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 				rq->cmd=IDETAPE_PACKET_COMMAND_REQUEST_TYPE2;	/* Note that we are waiting for DSC *before* we */
 										/* even issued the command */
-				tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+				tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FALLBACK_FREQUENCY;
 				idetape_postpone_request (drive);	/* Allow ide.c to process requests from */
 				return;
 			}
@@ -2680,11 +2976,11 @@
 			pc=(idetape_packet_command_t *) rq->buffer;
 			idetape_issue_packet_command (drive,pc,&idetape_pc_intr);
 			return;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_BUGS
 		default:
 			printk ("ide-tape: bug in IDETAPE_REQUEST_CMD macro\n");
 			idetape_end_request (0,HWGROUP (drive));
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 	}	
 }
 
@@ -2742,22 +3038,38 @@
 void idetape_queue_pc_head (ide_drive_t *drive,idetape_packet_command_t *pc,struct request *rq)
 
 {
+	unsigned int major = HWIF(drive)->major;
+	struct blk_dev_struct *bdev = &blk_dev[major];
+
+	bdev->current_request=HWGROUP (drive)->rq;		/* Since we may have taken it out */
+
 	ide_init_drive_cmd (rq);
 	rq->buffer = (char *) pc;
 	rq->cmd = IDETAPE_PACKET_COMMAND_REQUEST_TYPE1;
 	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
 }
 
+/*
+ *	idetape_wait_for_request installs a semaphore in a pending request
+ *	and sleeps until it is serviced.
+ *
+ *	The caller should ensure that the request will not be serviced
+ *	before we install the semaphore (usually by disabling interrupts).
+ */
+ 
 void idetape_wait_for_request (struct request *rq)
 
 {
-	unsigned long flags;
 	struct semaphore sem = MUTEX_LOCKED;
 
-	save_flags (flags);cli ();
-	rq->sem=&sem;
-	restore_flags (flags);
+#if IDETAPE_DEBUG_BUGS
+	if (rq == NULL || !IDETAPE_REQUEST_CMD (rq->cmd)) {
+		printk ("ide-tape: bug: Trying to sleep on non-valid request\n");
+		return;		
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
 
+	rq->sem=&sem;
 	down (&sem);
 }
 
@@ -2777,9 +3089,9 @@
 	idetape_tape_t *tape = &(drive->tape);
 	struct request rq;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("idetape_queue_rw_tail: cmd=%d\n",cmd);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	/* build up a special read request, and add it to the queue */
 	
 	ide_init_drive_cmd (&rq);
@@ -2789,35 +3101,92 @@
 	rq.nr_sectors = blocks;
 	rq.current_nr_sectors = blocks;
 	tape->active_data_request=NULL;	/* Non-pipelined mode */
-#if IDETAPE_DEBUG
-		printk ("Using ide_tail\n");
-#endif /* IDETAPE_DEBUG */
 	return ide_do_drive_cmd (drive, &rq, ide_wait);
 }
 
- /*
- *	idetape_add_chrdev_write_request tries to add a character device
- *	originated write request to our pipeline. In case we don't succeed,
- *	we revert to non-piplined operation mode for this request.
- *
- *	1.	Try to allocate a new pipeline stage.
- *	2.	If we can't, wait for more and more requests to be serviced
- *		and try again each time.
- *	3.	If we still can't allocate a stage, fallback to
- *		non-pipelined operation mode for this request.
+/*
+ *	idetape_add_chrdev_read_request handles character device read requests
+ *	when operating in the pipelined operation mode.
+ */
+ 
+int idetape_add_chrdev_read_request (ide_drive_t *drive,int blocks,char *buffer)
+
+{
+	idetape_tape_t *tape = &(drive->tape);
+	idetape_pipeline_stage_t *new_stage;
+	unsigned long flags;
+	struct request rq;
+	int errors;
+	
+#if IDETAPE_DEBUG_LOG
+	printk ("Reached idetape_add_chrdev_read_request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+	ide_init_drive_cmd (&rq);
+	rq.cmd = IDETAPE_READ_REQUEST;
+	rq.sector = tape->block_address;
+	rq.nr_sectors = blocks;
+	rq.current_nr_sectors = blocks;
+
+	if (tape->current_number_of_stages < 0.5*tape->max_number_of_stages) {
+		new_stage=idetape_kmalloc_stage (drive);
+		while (new_stage != NULL) {
+			new_stage->rq=rq;
+			save_flags (flags);cli ();
+			idetape_add_stage_tail (drive,new_stage);
+			restore_flags (flags);
+			new_stage=idetape_kmalloc_stage (drive);
+		}
+		if (tape->active_data_request == NULL)
+			idetape_insert_pipeline_into_queue (drive);
+	}
+
+	if (tape->first_stage == NULL) {
+
+		/*
+		 *	Linux is short on memory. Revert to non-pipelined
+		 *	operation mode for this request.
+		 */
+		 
+		return (idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,blocks,buffer));
+	}		
+	
+	save_flags (flags);cli ();
+	if (tape->active_data_request == &(tape->first_stage->rq))
+		idetape_wait_for_request (tape->active_data_request);
+	restore_flags (flags);
+
+	errors=tape->first_stage->rq.errors;
+	if (!errors)		
+		idetape_copy_buffer_from_stage (tape->first_stage,buffer);
+
+	idetape_remove_stage_head (drive);
+	return (errors ? -EIO:0);
+}
+
+/*
+ *	idetape_add_chrdev_write_request tries to add a character device
+ *	originated write request to our pipeline. In case we don't succeed,
+ *	we revert to non-piplined operation mode for this request.
+ *
+ *	1.	Try to allocate a new pipeline stage.
+ *	2.	If we can't, wait for more and more requests to be serviced
+ *		and try again each time.
+ *	3.	If we still can't allocate a stage, fallback to
+ *		non-pipelined operation mode for this request.
  */
 
-int idetape_add_chrdev_write_request (ide_drive_t *drive,int cmd,int blocks,char *buffer)
+int idetape_add_chrdev_write_request (ide_drive_t *drive,int blocks,char *buffer)
 
 {
 	idetape_tape_t *tape = &(drive->tape);
 	idetape_pipeline_stage_t *new_stage;
+	unsigned long flags;
 	struct request *rq;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_add_chrdev_write_request\n");
-	printk ("Trying to allocate stage - ");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	
 	if (tape->error_in_pipeline_stage)		/* Return a deferred error */
 		return (-EIO);
@@ -2832,66 +3201,109 @@
 	 */
 
 	while (new_stage == NULL) {
-
-		/*
-		 *	Wait for the time in which we can safely inspect
-		 *	tape->first_stage.
-		 */
-		 
-		while (tape->pipeline_locked);
-
-		if (tape->first_stage != NULL) {
-			idetape_wait_for_request (&(tape->first_stage->rq));
+		save_flags (flags);cli ();
+		if (tape->active_data_request != NULL) {
+			idetape_wait_for_request (tape->active_data_request);
+			restore_flags (flags);
 			new_stage=idetape_kmalloc_stage (drive);
 		}
-		else
-			break;					/* Linux is short on memory */
-	}
-	
-	/*
-	 *	If we don't have a new_stage, fallback to non-pipelined
-	 *	operation mode for this request.
-	 */
+		else {
+			/*
+			 *	Linux is short on memory. Fallback to
+			 *	non-pipelined operation mode for this request.
+			 */
 	 	 	
-	if (new_stage == NULL) {
-		if (tape->active_data_request != NULL)
-			idetape_wait_for_request (tape->active_data_request);
-		return (idetape_queue_rw_tail (drive,cmd,blocks,buffer));
+			restore_flags (flags);
+			return (idetape_queue_rw_tail (drive,IDETAPE_WRITE_REQUEST,blocks,buffer));
+		}
 	}
 
 	rq=&(new_stage->rq);
 
 	ide_init_drive_cmd (rq);
-	rq->buffer = NULL;			/* We will correct this when we will actually service the request */
-	rq->cmd = cmd;
+	rq->cmd = IDETAPE_WRITE_REQUEST;
 	rq->sector = tape->block_address;	/* Doesn't actually matter - We always assume sequential access */
 	rq->nr_sectors = blocks;
 	rq->current_nr_sectors = blocks;
 
 	idetape_copy_buffer_to_stage (new_stage,buffer);
+
+	save_flags (flags);cli ();
 	idetape_add_stage_tail (drive,new_stage);
+	restore_flags (flags);
+
+	/*
+	 *	Check if we are currently servicing requests in the bottom
+	 *	part of the driver.
+	 *
+	 *	If not, wait for the pipeline to be full enough (75%) before
+	 *	starting to service requests, so that we will be able to
+	 *	keep up with the higher speeds of the tape.
+	 */
+
+	if (tape->active_data_request == NULL && tape->current_number_of_stages >= 0.75*tape->max_number_of_stages)
+		idetape_insert_pipeline_into_queue (drive);		
 
 	return (0);		
 }
 
-void idetape_empty_pipeline (ide_drive_t *drive)
+void idetape_empty_read_pipeline (ide_drive_t *drive)
 
 {
 	idetape_tape_t *tape = &(drive->tape);
+	unsigned long flags;
 
-	tape->pipeline_was_full_once=0;
+#if IDETAPE_DEBUG_BUGS
+	if (tape->chrdev_direction != idetape_direction_read) {
+		printk ("ide-tape: bug: Trying to empty read pipeline, but we are not reading.\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
+
+	if (tape->first_stage == NULL)
+		return;
+		
+	save_flags (flags);cli ();
+	tape->next_stage=NULL;
+	if (tape->active_data_request != NULL)
+		idetape_wait_for_request (tape->active_data_request);
+	restore_flags (flags);
+
+	while (tape->first_stage != NULL)
+		idetape_remove_stage_head (drive);
+
+#if IDETAPE_PIPELINE
+	tape->max_number_of_stages=IDETAPE_MIN_PIPELINE_STAGES;
+#else
+	tape->max_number_of_stages=0;
+#endif /* IDETAPE_PIPELINE */
+}
+
+
+void idetape_empty_write_pipeline (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape = &(drive->tape);
+	unsigned long flags;
+
+#if IDETAPE_DEBUG_BUGS
+	if (tape->chrdev_direction != idetape_direction_write) {
+		printk ("ide-tape: bug: Trying to empty write pipeline, but we are not writing.\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
 
-	while (tape->pipeline_locked);
-	
 	if (tape->active_data_request == NULL)
 		idetape_insert_pipeline_into_queue (drive);		
 
+	save_flags (flags);cli ();
 	if (tape->last_stage != NULL)
 		idetape_wait_for_request (&(tape->last_stage->rq));
 
 	else if (tape->active_data_request != NULL)
 		idetape_wait_for_request (tape->active_data_request);
-
+	restore_flags (flags);
+	
 	tape->error_in_pipeline_stage=0;
 
 	/*
@@ -2907,11 +3319,11 @@
 	tape->max_number_of_stages=0;
 #endif /* IDETAPE_PIPELINE */
 
-#if IDETAPE_DEBUG
-	if (tape->first_stage != NULL || tape->last_stage != NULL || tape->current_number_of_stages != 0) {
+#if IDETAPE_DEBUG_BUGS
+	if (tape->first_stage != NULL || tape->next_stage != NULL || tape->last_stage != NULL || tape->current_number_of_stages != 0) {
 		printk ("ide-tape: ide-tape pipeline bug\n");		
 	}
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 }
 
 
@@ -2928,6 +3340,7 @@
 	for (i=0;i<12;i++)
 		pc->c[i]=0;
 	pc->retries=0;
+	pc->abort=0;
 }
 
 /*
@@ -2991,9 +3404,9 @@
 	idetape_tape_t *tape;
 	
 	tape=&(drive->tape);
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: pc_stack_index=%d\n",tape->pc_stack_index);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	if (tape->pc_stack_index==IDETAPE_PC_STACK)
 		tape->pc_stack_index=0;
 	return (&(tape->pc_stack [tape->pc_stack_index++]));
@@ -3019,9 +3432,9 @@
 	
 	tape=&(drive->tape);
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: rq_stack_index=%d\n",tape->rq_stack_index);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	if (tape->rq_stack_index==IDETAPE_PC_STACK)
 		tape->rq_stack_index=0;
 	return (&(tape->rq_stack [tape->rq_stack_index++]));
@@ -3041,11 +3454,11 @@
 	idetape_tape_t *tape=&(drive->tape);
 	unsigned long flags;
 			
-	save_flags (flags);cli();
+	save_flags (flags);cli ();
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_blkdev_open\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	if (tape->busy) {
 		restore_flags (flags);		/* Allowing access only through one */
@@ -3064,11 +3477,11 @@
 	idetape_tape_t *tape=&(drive->tape);
 	unsigned long flags;
 			
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_blkdev_release\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
-	save_flags (flags);cli();
+	save_flags (flags);cli ();
 	tape->busy=0;
 	restore_flags (flags);
 
@@ -3080,28 +3493,6 @@
  */
 
 /*
- *	lseek is currently not installed.
- */
- 
-int idetape_chrdev_lseek (struct inode *inode, struct file *file, off_t offset, int origin)
-
-{
-	ide_drive_t *drive;
-	
-#if IDETAPE_DEBUG
-	printk ("Reached idetape_chrdev_lseek\n");
-#endif /* IDETAPE_DEBUG */
-
-	drive=idetape_chrdev.drive;
-	if (idetape_position_tape (drive,offset) != 0) {
-		printk ("ide-tape: Rewinding tape failed\n");
-		return (-1);
-	}
-	
-	return (0);
-}
-
-/*
  *	Our character device read / write functions.
  *
  *	The tape is optimized to maximize throughput when it is transfering
@@ -3120,8 +3511,8 @@
  *	after this request.
  *
  *	Again, for best results use an integral number of the tape's parameter
- *	(which is displayed in the driver installation stage). I will soon
- *	add an ioctl to get this important parameter.
+ *	(which is displayed in the driver installation stage and is returned
+ *	 by the MTIOCGET ioctl).
  */
 
 /*
@@ -3131,47 +3522,59 @@
 int idetape_chrdev_read (struct inode *inode, struct file *file, char *buf, int count)
 
 {
-	ide_drive_t *drive;
-	idetape_tape_t *tape;
-	int blocks,remainder,retval,ctl_bytes;
+	ide_drive_t *drive=idetape_chrdev.drive;
+	idetape_tape_t *tape=&(drive->tape);
+	int blocks,remainder,retval;
 	char *buf_ptr;
 	unsigned long previous_block_address,actually_read;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_read\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
-	drive=idetape_chrdev.drive;
-	tape=&(drive->tape);
-	tape->last_dt_was_write=0;
+	if (tape->chrdev_direction != idetape_direction_read) {		/* Initialize read operation */
+		if (tape->chrdev_direction == idetape_direction_write) {
+			idetape_empty_write_pipeline (drive);
+			idetape_flush_tape_buffers (drive);
+		}
+		
+		/*
+		 *	Issue a read 0 command to ensure that DSC handshake
+		 *	is switched from completion mode to buffer available
+		 *	mode.
+		 */
+		 
+		retval=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,0,tape->temp_data_buffer);
+		if (retval)
+			return (retval);
 
+		tape->chrdev_direction=idetape_direction_read;
+	}
+	
 	if (count==0)
 		return (0);
 
 	actually_read=0;
 	buf_ptr=buf;
-	ctl_bytes=tape->capabilities.ctl*tape->tape_block_size;
-	blocks=count/ctl_bytes;
-	remainder=count%ctl_bytes;
+	blocks=count/tape->data_buffer_size;
+	remainder=count%tape->data_buffer_size;
 	
 	while (blocks) {
-#if IDETAPE_DEBUG
-		printk ("Adding a READ request to the block device request queue\n");
-#endif /* IDETAPE_DEBUG */
 		previous_block_address=tape->block_address;
-		retval=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,tape->capabilities.ctl,tape->data_buffer);
-		actually_read+=tape->tape_block_size*(tape->block_address-previous_block_address);
+		retval=idetape_add_chrdev_read_request (drive,tape->capabilities.ctl,tape->temp_data_buffer);
+		if (tape->max_number_of_stages)
+			actually_read+=tape->data_buffer_size;
+		else
+			actually_read+=tape->tape_block_size*(tape->block_address-previous_block_address);
 
 		if (retval) {
-			printk ("ide-tape: Error occured while reading\n");
-			return (actually_read);
+			if (tape->max_number_of_stages)
+				return (0);
+			else
+				return (actually_read);
 		}
-#if IDETAPE_DEBUG
-	printk ("Copying %d bytes to the user space memory\n",ctl_bytes);
-#endif /* IDETAPE_DEBUG */
-
-		memcpy_tofs (buf_ptr,tape->data_buffer,ctl_bytes);
-		buf_ptr+=ctl_bytes;
+		memcpy_tofs (buf_ptr,tape->temp_data_buffer,tape->data_buffer_size);
+		buf_ptr+=tape->data_buffer_size;
 		blocks--;
 	}
 	if (remainder)
@@ -3183,49 +3586,43 @@
 int idetape_chrdev_read_remainder (struct inode *inode, struct file *file, char *buf, int count)
 
 {
-	ide_drive_t *drive;
-	idetape_tape_t *tape;
+	ide_drive_t *drive=idetape_chrdev.drive;
+	idetape_tape_t *tape=&(drive->tape);
 	int blocks,remainder,retval;
 	unsigned long previous_block_address,actually_read;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_read_remainder\n");
-#endif /* IDETAPE_DEBUG */
-
-	drive=idetape_chrdev.drive;
-	tape=&(drive->tape);
-
-	tape->last_dt_was_write=0;
-
-	if (count==0)
-		return (0);
+#endif /* IDETAPE_DEBUG_LOG */
 
 	blocks=count/tape->tape_block_size;
 	remainder=count%tape->tape_block_size;
 	if (remainder) {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Padding read to block boundary\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		blocks++;
 	}
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Adding a READ request to the block device request queue\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	previous_block_address=tape->block_address;
-	retval=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,blocks,tape->data_buffer);
+	retval=idetape_add_chrdev_read_request (drive,blocks,tape->temp_data_buffer);
 	if (retval) {
-		printk ("ide-tape: Error occured while reading\n");
-		actually_read=tape->tape_block_size*(tape->block_address-previous_block_address);
+		if (tape->max_number_of_stages)
+			actually_read=0;
+		else
+			actually_read=tape->tape_block_size*(tape->block_address-previous_block_address);
 		if (actually_read > count)
 			actually_read=count;
 		if (actually_read != 0)
-			memcpy_tofs (buf,tape->data_buffer,actually_read);
+			memcpy_tofs (buf,tape->temp_data_buffer,actually_read);
 		return (actually_read);
 	}
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Copying %d bytes to the user space memory\n",count);
-#endif /* IDETAPE_DEBUG */
-	memcpy_tofs (buf,tape->data_buffer,count);
+#endif /* IDETAPE_DEBUG_LOG */
+	memcpy_tofs (buf,tape->temp_data_buffer,count);
 	return (count);
 }
 
@@ -3238,14 +3635,29 @@
 	const char *buf_ptr;
 	unsigned long previous_block_address,actually_written;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_write\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	drive=idetape_chrdev.drive;
 	tape=&(drive->tape);
 
-	tape->last_dt_was_write=1;
+	if (tape->chrdev_direction != idetape_direction_write) {	/* Initialize write operation */
+		if (tape->chrdev_direction == idetape_direction_read)
+			idetape_empty_read_pipeline (drive);
+
+		/*
+		 *	Issue a write 0 command to ensure that DSC handshake
+		 *	is switched from completion mode to buffer available
+		 *	mode.
+		 */
+
+		retval=idetape_queue_rw_tail (drive,IDETAPE_WRITE_REQUEST,0,tape->temp_data_buffer);
+		if (retval)
+			return (retval);		
+	
+		tape->chrdev_direction=idetape_direction_write;
+	}
 
 	if (count==0)
 		return (0);
@@ -3259,14 +3671,13 @@
 		memcpy_fromfs (tape->temp_data_buffer,buf_ptr,tape->data_buffer_size);
 		buf_ptr+=tape->data_buffer_size;
 		previous_block_address=tape->block_address;
-		retval=idetape_add_chrdev_write_request (drive,IDETAPE_WRITE_REQUEST,tape->capabilities.ctl,tape->temp_data_buffer);
+		retval=idetape_add_chrdev_write_request (drive,tape->capabilities.ctl,tape->temp_data_buffer);
 		if (tape->max_number_of_stages)
 			actually_written+=tape->data_buffer_size;	/* Pipelined mode - Cheat :-) */
 		else
 			actually_written+=tape->tape_block_size*(tape->block_address-previous_block_address);
 
 		if (retval) {
-			printk ("ide-tape: Error occured while writing\n");
 			if (tape->max_number_of_stages)
 				return (0);
 			else
@@ -3289,9 +3700,9 @@
 	char *ptr;
 	unsigned long previous_block_address,actually_written;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_write_remainder\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		
 	drive=idetape_chrdev.drive;
 	tape=&(drive->tape);
@@ -3300,26 +3711,25 @@
 	remainder=count%tape->tape_block_size;
 	if (remainder)
 		blocks++;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Copying %d bytes from the user space memory\n",count);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	memcpy_fromfs (tape->temp_data_buffer,buf,count);
 	if (remainder) {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Padding written data to block boundary\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		ptr=tape->temp_data_buffer+(blocks-1)*tape->tape_block_size;
 		memset (ptr,0,remainder);
 	}
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Adding a WRITE request to the block device request queue\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	previous_block_address=tape->block_address;
-	retval=idetape_add_chrdev_write_request (drive,IDETAPE_WRITE_REQUEST,blocks,tape->temp_data_buffer);
+	retval=idetape_add_chrdev_write_request (drive,blocks,tape->temp_data_buffer);
 	if (retval) {
-		printk ("ide-tape: Error occured while writing\n");
 		if (tape->max_number_of_stages)
 			actually_written=0;
 		else
@@ -3337,21 +3747,61 @@
  *	General mtio.h magnetic io commands are supported here, and not in
  *	the correspoding block interface.
  *
+ *	The following ioctls are supported:
+ *
+ *	MTIOCTOP -	Refer to idetape_mtioctop for detailed description.
+ *
+ *	MTIOCGET - 	The mt_dsreg field in the returned mtget structure
+ *			will be set to (recommended block size <<
+ *			MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK, which
+ *			is currently equal to the size itself.
+ *			The other mtget fields are not supported.
+ *
+ *			Note that we do not actually return the tape's
+ *			block size. Rather, we provide the recommended
+ *			number of bytes which sould be used as a "user
+ *			block size" with the character device read/write
+ *			functions to maximize throughput.
+ *
+ *	MTIOCPOS -	The current tape "position" is returned.
+ *			(A unique number which can be used with the MTSEEK
+ *			 operation to return to this position in some
+ *			 future time, provided this place was not overwritten
+ *			 meanwhile).
+ *
  *	Our own ide-tape ioctls are supported on both interfaces.
  */
 
 int idetape_chrdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 
 {
+	ide_drive_t *drive=idetape_chrdev.drive;
+	idetape_tape_t *tape=&(drive->tape);
+	idetape_packet_command_t pc;
 	struct mtop mtop;
-	ide_drive_t *drive;
+	struct mtget mtget;
+	struct mtpos mtpos;
 	int retval;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_ioctl, cmd=%u\n",cmd);
-#endif
+#endif /* IDETAPE_DEBUG_LOG */
 
-	drive=idetape_chrdev.drive;
+	if (tape->chrdev_direction == idetape_direction_write) {
+		idetape_empty_write_pipeline (drive);
+		idetape_flush_tape_buffers (drive);
+		tape->chrdev_direction=idetape_direction_none;
+	}
+
+	if (tape->chrdev_direction == idetape_direction_read)
+		if (cmd != MTIOCTOP) {
+			idetape_empty_read_pipeline (drive);
+			tape->chrdev_direction=idetape_direction_none;
+		}
+	
+	pc.buffer=pc.temp_buffer;
+	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+	pc.current_position=pc.temp_buffer;
 
 	switch (cmd) {
 		case MTIOCTOP:
@@ -3359,6 +3809,21 @@
 			if (retval) return (retval);
 			memcpy_fromfs ((char *) &mtop, (char *) arg, sizeof (struct mtop));
 			return (idetape_mtioctop (drive,mtop.mt_op,mtop.mt_count));
+		case MTIOCGET:
+			mtget.mt_dsreg=(tape->data_buffer_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK;
+			retval=verify_area (VERIFY_WRITE,(char *) arg,sizeof (struct mtget));
+			if (retval) return (retval);
+			memcpy_tofs ((char *) arg,(char *) &mtget, sizeof (struct mtget));
+			return (0);
+		case MTIOCPOS:
+			idetape_create_read_position_cmd (&pc);
+			retval=idetape_queue_pc_tail (drive,&pc);
+			if (retval) return (retval);
+			mtpos.mt_blkno=tape->block_address;
+			retval=verify_area (VERIFY_WRITE,(char *) arg,sizeof (struct mtpos));
+			if (retval) return (retval);
+			memcpy_tofs ((char *) arg,(char *) &mtpos, sizeof (struct mtpos));
+			return (0);
 		default:
 			return (idetape_blkdev_ioctl (drive,inode,file,cmd,arg));
 	}
@@ -3381,70 +3846,82 @@
  *
  *	MTBSFM	-	Like MTBSF, only tape is positioned after the last filemark.
  *
+ *
+ *	Note:
+ *
+ *		MTBSF and MTBSFM are not supported when the tape doesn't
+ *		supports spacing over filemarks in the reverse direction.
+ *		In this case, MTFSFM is also usually not supported (it is
+ *		supported in the rare case in which we crossed the filemark
+ *		during our read-ahead pipelined operation mode).
+ *		
  *	MTWEOF	-	Writes mt_count filemarks. Tape is positioned after
  *			the last written filemark.
  *
  *	MTREW	-	Rewindes tape.
  *
+ *	MTOFFL	-	Puts the tape drive "Offline": Rewinds the tape and
+ *			prevents further access until the media is replaced.
+ *
  *	MTNOP	-	Flushes tape buffers.
  *
+ *	MTRETEN	-	Retension media. This typically consists of one end
+ *			to end pass on the media.
+ *
  *	MTEOM	-	Moves to the end of recorded data.
  *
  *	MTERASE	-	Erases tape.
  *
+ *	MTSEEK	-	Positions the tape in a specific block number, which
+ *			was previously received using the MTIOCPOS ioctl,
+ *			assuming this place was not overwritten meanwhile.
+ *
  *	The following commands are currently not supported:
  *
- *	MTFSR, MTBSR, MTFSS, MTBSS, MTWSM, MTOFFL, MTRETEN, MTSEEK, MTSETBLK,
- *	MTSETDENSITY, MTSETDRVBUFFER, MT_ST_BOOLEANS, MT_ST_WRITE_THRESHOLD.
+ *	MTFSR, MTBSR, MTFSS, MTBSS, MTWSM, MTSETBLK, MTSETDENSITY,
+ *	MTSETDRVBUFFER, MT_ST_BOOLEANS, MT_ST_WRITE_THRESHOLD.
  */
  
 int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count)
 
 {
-	int i,retval;
-	
+	idetape_tape_t *tape=&(drive->tape);
 	idetape_packet_command_t pc;
+	int i,retval;
 
 	pc.buffer=pc.temp_buffer;
 	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
 	pc.current_position=pc.temp_buffer;
 
-	idetape_create_write_filemark_cmd (&pc,0);	/* Flush buffers */
-	retval=idetape_queue_pc_tail (drive,&pc);
-	if (retval) return (retval);
+#if IDETAPE_DEBUG_LOG
+	printk ("Handling MTIOCTOP ioctl: mt_op=%d, mt_count=%d\n",mt_op,mt_count);
+#endif /* IDETAPE_DEBUG_LOG */
+
+	/*
+	 *	Commands which need our pipelined read-ahead stages.
+	 */
 
 	switch (mt_op) {
 		case MTFSF:
-#if IDETAPE_DEBUG
-			printk ("Handling MTFSF command\n");
-#endif /* IDETAPE_DEBUG */
-			idetape_create_space_cmd (&pc,mt_count,IDETAPE_SPACE_OVER_FILEMARK);
-			return (idetape_queue_pc_tail (drive,&pc));
 		case MTFSFM:
-#if IDETAPE_DEBUG
-			printk ("Handling MTFSFM command\n");
-#endif /* IDETAPE_DEBUG */
-			retval=idetape_mtioctop (drive,MTFSF,mt_count);
-			if (retval) return (retval);
-			return (idetape_mtioctop (drive,MTBSF,1));
 		case MTBSF:
-#if IDETAPE_DEBUG
-			printk ("Handling MTBSF command\n");
-#endif /* IDETAPE_DEBUG */
-			idetape_create_space_cmd (&pc,-mt_count,IDETAPE_SPACE_OVER_FILEMARK);
-			return (idetape_queue_pc_tail (drive,&pc));
 		case MTBSFM:
-#if IDETAPE_DEBUG
-			printk ("Handling MTBSFM command\n");
-#endif /* IDETAPE_DEBUG */
-			retval=idetape_mtioctop (drive,MTBSF,mt_count);
-			if (retval) return (retval);
-			return (idetape_mtioctop (drive,MTFSF,1));
+			return (idetape_space_over_filemarks (drive,mt_op,mt_count));
+		default:
+			break;
+	}
+
+	/*
+	 *	Empty the pipeline.
+	 */
+
+	if (tape->chrdev_direction == idetape_direction_read) {
+		idetape_empty_read_pipeline (drive);
+		tape->chrdev_direction=idetape_direction_none;
+	}
+
+	switch (mt_op) {
 		case MTWEOF:
-#if IDETAPE_DEBUG
-			printk ("Handling MTWEOF command\n");
-#endif /* IDETAPE_DEBUG */
-		
 			for (i=0;i<mt_count;i++) {
 				idetape_create_write_filemark_cmd (&pc,1);
 				retval=idetape_queue_pc_tail (drive,&pc);
@@ -3452,31 +3929,115 @@
 			}
 			return (0);
 		case MTREW:
-#if IDETAPE_DEBUG
-			printk ("Handling MTREW command\n");
-#endif /* IDETAPE_DEBUG */
 			return (idetape_rewind_tape (drive));
+		case MTOFFL:
+			idetape_create_load_unload_cmd (&pc,!IDETAPE_LU_LOAD_MASK);
+			return (idetape_queue_pc_tail (drive,&pc));
 		case MTNOP:
-#if IDETAPE_DEBUG
-			printk ("Handling MTNOP command\n");
-#endif /* IDETAPE_DEBUG */
-			idetape_create_write_filemark_cmd (&pc,0);
+			return (idetape_flush_tape_buffers (drive));
+		case MTRETEN:
+			idetape_create_load_unload_cmd (&pc,IDETAPE_LU_RETENSION_MASK | IDETAPE_LU_LOAD_MASK);
 			return (idetape_queue_pc_tail (drive,&pc));
 		case MTEOM:
-#if IDETAPE_DEBUG
-			printk ("Handling MTEOM command\n");
-#endif /* IDETAPE_DEBUG */
-		
 			idetape_create_space_cmd (&pc,0,IDETAPE_SPACE_TO_EOD);
 			return (idetape_queue_pc_tail (drive,&pc));
 		case MTERASE:
-#if IDETAPE_DEBUG
-			printk ("Handling MTERASE command\n");
-#endif /* IDETAPE_DEBUG */
-			retval=idetape_position_tape (drive,0);
+			retval=idetape_rewind_tape (drive);
 			if (retval) return (retval);
 			idetape_create_erase_cmd (&pc);
 			return (idetape_queue_pc_tail (drive,&pc));
+		case MTSEEK:
+			return (idetape_position_tape (drive,mt_count));
+		default:
+			printk ("ide-tape: MTIO operation %d not supported\n",mt_op);
+			return (-EIO);
+	}
+}
+
+/*
+ *	idetape_space_over_filemarks is now a bit more complicated than just
+ *	passing the command to the tape since we may have crossed some
+ *	filemarks during our pipelined read-ahead mode.
+ *
+ *	As a minor side effect, the pipeline enables us to support MTFSFM when
+ *	the filemark is in our internal pipeline even if the tape doesn't
+ *	support spacing over filemarks in the reverse direction.
+ */
+ 
+int idetape_space_over_filemarks (ide_drive_t *drive,short mt_op,int mt_count)
+
+{
+	idetape_tape_t *tape=&(drive->tape);
+	idetape_packet_command_t pc;
+	unsigned long flags;
+	int retval,count=0,errors;
+
+	if (tape->chrdev_direction == idetape_direction_read) {
+
+		/*
+		 *	We have a read-ahead buffer. Scan it for crossed
+		 *	filemarks.
+		 */
+		 
+		while (tape->first_stage != NULL) {
+			
+			/*
+			 *	Wait until the first read-ahead request
+			 *	is serviced.
+			 */
+		
+			save_flags (flags);cli ();
+			if (tape->active_data_request == &(tape->first_stage->rq))
+				idetape_wait_for_request (tape->active_data_request);
+			restore_flags (flags);
+
+			errors=tape->first_stage->rq.errors;
+			if (errors == IDETAPE_RQ_ERROR_FILEMARK)
+				count++;
+
+			if (count == mt_count) {
+				switch (mt_op) {
+					case MTFSF:
+						idetape_remove_stage_head (drive);
+					case MTFSFM:
+						return (0);
+				}
+			}
+			idetape_remove_stage_head (drive);
+		}
+		tape->chrdev_direction = idetape_direction_none;
+	}
+
+	/*
+	 *	The filemark was not found in our internal pipeline.
+	 *	Now we can issue the space command.
+	 */
+
+	pc.buffer=pc.temp_buffer;
+	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+	pc.current_position=pc.temp_buffer;
+
+	switch (mt_op) {
+		case MTFSF:
+			idetape_create_space_cmd (&pc,mt_count-count,IDETAPE_SPACE_OVER_FILEMARK);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTFSFM:
+			if (!tape->capabilities.sprev)
+				return (-EIO);
+			retval=idetape_mtioctop (drive,MTFSF,mt_count-count);
+			if (retval) return (retval);
+			return (idetape_mtioctop (drive,MTBSF,1));
+		case MTBSF:
+			if (!tape->capabilities.sprev)
+				return (-EIO);
+			idetape_create_space_cmd (&pc,-(mt_count+count),IDETAPE_SPACE_OVER_FILEMARK);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTBSFM:
+			if (!tape->capabilities.sprev)
+				return (-EIO);
+			retval=idetape_mtioctop (drive,MTBSF,mt_count+count);
+			if (retval) return (retval);
+			return (idetape_mtioctop (drive,MTFSF,1));
 		default:
 			printk ("ide-tape: MTIO operation %d not supported\n",mt_op);
 			return (-EIO);
@@ -3495,11 +4056,11 @@
 	unsigned long flags;
 	unsigned int minor;
 			
-	save_flags (flags);cli();
+	save_flags (flags);cli ();
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_open\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 
 	drive=idetape_chrdev.drive;
@@ -3527,8 +4088,6 @@
 		}
 	}
 
-	tape->last_dt_was_write=0;
-
 	return (0);
 }
 
@@ -3546,39 +4105,34 @@
 	idetape_packet_command_t pc;
 	unsigned long flags;
 			
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_release\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	drive=idetape_chrdev.drive;
 	tape=&(drive->tape);
 	minor=MINOR (inode->i_rdev);
 
-	idetape_empty_pipeline (drive);
-
-	if (tape->last_dt_was_write) {
+	if (tape->chrdev_direction == idetape_direction_write) {
+		idetape_empty_write_pipeline (drive);
+		tape->chrdev_direction = idetape_direction_none;
 		idetape_create_write_filemark_cmd (&pc,1);	/* Write a filemark */
 		if (idetape_queue_pc_tail (drive,&pc)) {
 			printk ("ide-tape: Couldn't write a filemark\n");
 			/* ??? */
 		}
 	}
-	else {
-		idetape_create_write_filemark_cmd (&pc,0);	/* Flush buffers */
-		if (idetape_queue_pc_tail (drive,&pc)) {
-			printk ("ide-tape: Couldn't flush buffers\n");
-			/* ??? */
-		}
-	}
 
 	if (minor < 128) {
+		if (tape->chrdev_direction == idetape_direction_read)
+			idetape_empty_read_pipeline (drive);
 		if (idetape_rewind_tape (drive)) {
 			printk ("ide-tape: Rewinding tape failed\n");
 			/* ??? */
 		}
 	}
 
-	save_flags (flags);cli();
+	save_flags (flags);cli ();
 	tape->busy=0;
 	restore_flags (flags);
 
@@ -3623,13 +4177,9 @@
 {
 	int retval;
 	idetape_packet_command_t pc;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_rewind_tape\n");
-#endif /* IDETAPE_DEBUG */	
-
-	idetape_create_write_filemark_cmd (&pc,0);	/* Flush buffers */
-	retval=idetape_queue_pc_tail (drive,&pc);
-	if (retval) return (retval);
+#endif /* IDETAPE_DEBUG_LOG */	
 	
 	idetape_create_rewind_cmd (&pc);
 	retval=idetape_queue_pc_tail (drive,&pc);
@@ -3642,6 +4192,15 @@
 	return (idetape_queue_pc_tail (drive,&pc));
 }
 
+int idetape_flush_tape_buffers (ide_drive_t *drive)
+
+{
+	idetape_packet_command_t pc;
+
+	idetape_create_write_filemark_cmd (&pc,0);
+	return (idetape_queue_pc_tail (drive,&pc));
+}
+
 /*
  *	Pipeline related functions
  */
@@ -3668,12 +4227,11 @@
 	idetape_buffer_head_t *prev_bh,*bh;
 	int buffers_num,i;
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_kmalloc_stage\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
-	if (tape->current_number_of_stages==tape->max_number_of_stages) {
-		tape->pipeline_was_full_once=1;
+	if (tape->current_number_of_stages>=tape->max_number_of_stages) {
 		return (NULL);
 	}
 		
@@ -3731,9 +4289,9 @@
 	if (stage == NULL)
 		return;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_kfree_stage\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	
 	bh=stage->bh;
 	
@@ -3760,14 +4318,26 @@
 	idetape_buffer_head_t *bh;
 	char *ptr;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_copy_buffer_from_stage\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+	if (buffer == NULL) {
+		printk ("ide-tape: bug: buffer is null in copy_buffer_from_stage\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
 	
 	ptr=buffer;
 	bh=stage->bh;
 	
 	while (bh != NULL) {
+#if IDETAPE_DEBUG_BUGS
+		if (bh->data == NULL) {
+			printk ("ide-tape: bug: bh->data is null\n");
+			return;
+		}
+#endif /* IDETAPE_DEBUG_BUGS */
 		memcpy (ptr,bh->data,IDETAPE_ALLOCATION_BLOCK);
 		bh=bh->next;
 		ptr=ptr+IDETAPE_ALLOCATION_BLOCK;
@@ -3786,14 +4356,26 @@
 	idetape_buffer_head_t *bh;
 	char *ptr;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_copy_buffer_to_stage\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+	if (buffer == NULL) {
+		printk ("ide-tape: bug: buffer is null in copy_buffer_to_stage\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
 
 	ptr=buffer;
 	bh=stage->bh;
 	
 	while (bh != NULL) {
+#if IDETAPE_DEBUG_BUGS
+		if (bh->data == NULL) {
+			printk ("ide-tape: bug: bh->data is null\n");
+			return;
+		}
+#endif /* IDETAPE_DEBUG_BUGS */
 		memcpy (bh->data,ptr,IDETAPE_ALLOCATION_BLOCK);
 		bh=bh->next;
 		ptr=ptr+IDETAPE_ALLOCATION_BLOCK;
@@ -3814,12 +4396,9 @@
 {
 	idetape_tape_t *tape=&(drive->tape);
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_increase_max_pipeline_stages\n");
-#endif /* IDETAPE_DEBUG */
-
-	if (!tape->pipeline_was_full_once)
-		return;
+#endif /* IDETAPE_DEBUG_LOG */
 
 	tape->max_number_of_stages+=IDETAPE_INCREASE_STAGES_RATE*
 					(IDETAPE_MAX_PIPELINE_STAGES-IDETAPE_MIN_PIPELINE_STAGES);
@@ -3827,72 +4406,83 @@
 	if (tape->max_number_of_stages >= IDETAPE_MAX_PIPELINE_STAGES)
 		tape->max_number_of_stages = IDETAPE_MAX_PIPELINE_STAGES;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Maximum number of stages: %d\n",tape->max_number_of_stages);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	return;
 }
 
 /*
  *	idetape_add_stage_tail adds a new stage at the end of the pipeline.
+ *
+ *	Caller should disable interrupts, if necessary.
  */
  
 void idetape_add_stage_tail (ide_drive_t *drive,idetape_pipeline_stage_t *stage)
 
 {
 	idetape_tape_t *tape=&(drive->tape);
-	unsigned long flags;
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("Reached idetape_add_stage_tail\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	stage->next=NULL;
-	
-#if IDETAPE_DEBUG
-		printk ("Adding to the tail of the pipeline\n");
-#endif /* IDETAPE_DEBUG */
-
-	/*
-	 *	Avoid a race condition - pipeline_locked is set whenever
-	 *	we modify active_data_request from the lower level of
-	 *	the driver. We wait until active_data_request is valid.
-	 */
-	 
-	while (tape->pipeline_locked);
-
-	save_flags (flags);cli ();
 	stage->prev=tape->last_stage;
-	if (tape->first_stage != NULL)
+	if (tape->last_stage != NULL)
 		tape->last_stage->next=stage;
 	else
-		tape->first_stage=stage;
+		tape->first_stage=tape->next_stage=stage;
 	tape->last_stage=stage;
+	if (tape->next_stage == NULL)
+		tape->next_stage=tape->last_stage;
 	tape->current_number_of_stages++;
+}
 
-	/*
-	 *	Check if we are currently servicing requests in the bottom
-	 *	part of the driver.
-	 *
-	 *	If not, wait for the pipeline to be full enough (75%) before
-	 *	starting to service requests, so that we will be able to
-	 *	keep up with the higher speeds of the tape.
-	 */
+/*
+ *	idetape_remove_stage_head removes tape->first_stage from the pipeline.
+ *
+ *	Again, caller should avoid race conditions.
+ */
+ 
+void idetape_remove_stage_head (ide_drive_t *drive)
 
-	if (tape->active_data_request == NULL &&
-		tape->current_number_of_stages >= 0.75*tape->max_number_of_stages) {
-		
-		restore_flags (flags);
-		idetape_insert_pipeline_into_queue (drive);		
+{
+	idetape_tape_t *tape=&(drive->tape);
+	idetape_pipeline_stage_t *stage;
+	
+#if IDETAPE_DEBUG_LOG
+		printk ("Reached idetape_remove_stage_head\n");
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+	if (tape->first_stage == NULL) {
+		printk ("ide-tape: bug: tape->first_stage is NULL\n");
+		return;		
+	}
+	if (tape->active_stage == tape->first_stage) {
+		printk ("ide-tape: bug: Trying to free our active pipeline stage\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
+	stage=tape->first_stage;
+	tape->first_stage=stage->next;
+	idetape_kfree_stage (stage);
+	tape->current_number_of_stages--;
+	if (tape->first_stage == NULL) {
+		tape->last_stage=NULL;
+#if IDETAPE_DEBUG_BUGS
+		if (tape->next_stage != NULL)
+			printk ("ide-tape: bug: tape->next_stage != NULL\n");
+		if (tape->current_number_of_stages)
+			printk ("ide-tape: bug: current_number_of_stages should be 0 now\n");
+#endif /* IDETAPE_DEBUG_BUGS */
 	}
-	else
-		restore_flags (flags);
 }
 
 /*
  *	idetape_insert_pipeline_into_queue is used to start servicing the
- *	pipeline stages.
+ *	pipeline stages, starting from tape->next_stage.
  */
  
 void idetape_insert_pipeline_into_queue (ide_drive_t *drive)
@@ -3900,55 +4490,41 @@
 {
 	idetape_tape_t *tape=&(drive->tape);
 
-	if (tape->first_stage == NULL)
+	if (tape->next_stage == NULL)
 		return;
 
 	if (tape->active_data_request == NULL) {
 		idetape_active_next_stage (drive);
-#if IDETAPE_DEBUG
-		printk ("Using ide_end\n");
-#endif /* IDETAPE_DEBUG */
 		(void) (ide_do_drive_cmd (drive,tape->active_data_request,ide_end));
 		return;
 	}
 }
 
 /*
- *	idetape_active_next_stage will "move the pipeline" one stage -
- *	Inserting the first stage into the "active place", and using kfree
- *	to free the stage.
+ *	idetape_active_next_stage will declare the next stage as "active".
  */
  
 void idetape_active_next_stage (ide_drive_t *drive)
 
 {
 	idetape_tape_t *tape=&(drive->tape);
-	idetape_pipeline_stage_t *stage;
-	struct request *rq;
-	unsigned long flags;
+	idetape_pipeline_stage_t *stage=tape->next_stage;
+	struct request *rq=&(stage->rq);
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_active_next_stage\n");
-	if (tape->first_stage == NULL) {
-		printk ("ide-tape: ide-tape.c bug - tape->first_stage is NULL\n");
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+	if (stage == NULL) {
+		printk ("ide-tape: bug: Trying to activate a non existing stage\n");
 		return;
 	}
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */	
+	if (rq->cmd == IDETAPE_WRITE_REQUEST)
+		idetape_copy_buffer_from_stage (stage,tape->data_buffer);
 	
-	stage=tape->first_stage;
-
-	idetape_copy_buffer_from_stage (stage,tape->data_buffer);
-	rq=idetape_next_rq_storage (drive);
-	*rq=stage->rq;
 	rq->buffer=tape->data_buffer;
-
-	save_flags (flags);cli ();
-	tape->first_stage=stage->next;
-	if (tape->first_stage == NULL)
-		tape->last_stage=NULL;
-	tape->current_number_of_stages--;
 	tape->active_data_request=rq;
-	restore_flags (flags);
-	
-	idetape_kfree_stage (stage);
+	tape->active_stage=stage;
+	tape->next_stage=stage->next;
 }
diff -u --recursive --new-file v1.3.53/linux/drivers/block/ide-tape.h linux/drivers/block/ide-tape.h
--- v1.3.53/linux/drivers/block/ide-tape.h	Sun Dec 17 11:43:12 1995
+++ linux/drivers/block/ide-tape.h	Thu Jan  4 07:43:40 1996
@@ -1,7 +1,7 @@
 /*
- * linux/drivers/block/ide-tape.h	Version 1.1 - ALPHA	Dec  14, 1995
+ * linux/drivers/block/ide-tape.h	Version 1.2 - ALPHA	Jan   1, 1996
  *
- * Copyright (C) 1995 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
+ * Copyright (C) 1995, 1996 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
  */
 
 /*
@@ -23,8 +23,7 @@
  *
  *	Pipelined operation mode has the potential to maximize the
  *	performance of the driver and thus to saturate the throughput
- *	to the maximum value supported by the tape. Currently, pipelined
- *	mode is supported only on writes.
+ *	to the maximum value supported by the tape.
  *
  *	In pipelined mode we are servicing requests without blocking the
  *	user backup program. For example, on a write request, we will add it
@@ -32,9 +31,8 @@
  *	user program will then have enough time to prepare the next blocks
  *	while the tape is still busy working on the previous requests.
  *
- *	Pipelined (write) operation mode is enabled by default, but since
- *	it has a few downfalls as well (Use of additional memory and deferred
- *      error code to the application), you may wish to disable it.
+ *	Pipelined operation mode is enabled by default, but since it has a
+ *	few downfalls as well, you may wish to disable it.
  *	Further explanation of pipelined mode is available in ide-tape.c .
  */
 
@@ -58,6 +56,29 @@
 #define	IDETAPE_INCREASE_STAGES_RATE		0.2
 
 /*
+ *	Assuming the tape shares an interface with another device, the default
+ *	behavior is to service our pending pipeline requests as soon as
+ *	possible, but to gracefully postpone them in favor of the other device
+ *	when the tape is busy. This has the potential to maximize our
+ *	throughput and in the same time, to make efficient use of the IDE bus.
+ *
+ *	Note that when we transfer data to / from the tape, we co-operate with
+ *	the relatively fast tape buffers and the tape will perform the
+ *	actual media access in the background, without blocking the IDE
+ *	bus. This means that as long as the maximum IDE bus throughput is much
+ *	higher than the sum of our maximum throughput and the maximum
+ *	throughput of the other device, we should probably leave the default
+ *	behavior.
+ *
+ *	However, if it is still desired to give the other device a share even
+ *	in our own (small) bus bandwidth, you can set IDETAPE_LOW_TAPE_PRIORITY
+ *	to 1. This will let the other device finish *all* its pending requests
+ *	before we even check if we can service our next pending request.
+ */
+		 
+#define IDETAPE_LOW_TAPE_PRIORITY		0
+
+/*
  *	It seems that dynamically allocating buffers of about 32KB
  *	each is doomed to fail, unless we are in or very near the
  *	initialization stage. Take care when changing this value, as it
@@ -76,21 +97,25 @@
 #endif
 
 /*
- *	Setting IDETAPE_DEBUG to 1 will:
- *
- *		1.	Generally log all driver actions.
- *		2.	Enable self-sanity checks in some places.
+ *	The following are used to debug the driver:
  *
- *	Use IDETAPE_DEBUG when encountering a problem with the driver.
+ *	Setting IDETAPE_DEBUG_LOG to 1 will log driver flow control.
+ *	Setting IDETAPE_DEBUG_BUGS to 1 will enable self-sanity checks in
+ *	some places.
  *
- *	Setting IDETAPE_DEBUG to 0 will restore normal operation mode:
+ *	Setting them to 0 will restore normal operation mode:
  *
  *		1.	Disable logging normal successful operations.
  *		2.	Disable self-sanity checks.
  *		3.	Errors will still be logged, of course.
+ *
+ *	All the #if DEBUG code will be removed some day, when the driver
+ *	is verified to be stable enough. This will make it much more
+ *	esthetic.
  */
  
-#define	IDETAPE_DEBUG		0
+#define	IDETAPE_DEBUG_LOG		0
+#define	IDETAPE_DEBUG_BUGS		1
 
 /*
  *	After each failed packet command we issue a request sense command
@@ -147,9 +172,12 @@
  *		request queue, so that ide.c will service requests from
  *		the other device on the same interface meanwhile.
  *
- *		The polling frequency is 1/IDETAPE_DSC_READ_WRITE_FREQUENCY,
- *		and it should be relatively fast. The default is a period
- *		of 50 msec.
+ *		We can now automatically select the "best" polling frequency.
+ *		Have a look at IDETAPE_ANTICIPATE_READ_WRITE_DSC below.
+ *
+ *		In case you don't want to use the automatic selection,
+ *		choose it to be relatively fast. The default fallback
+ *		frequency is 1/50 msec.
  *
  *	2.	After the successful initialization of a "media access
  *		packet command", which is a command which can take a long
@@ -173,7 +201,30 @@
  *
  */
 
-#define	IDETAPE_DSC_READ_WRITE_FREQUENCY	5*HZ/100	/* 50 msec */
+/*
+ *	Setting IDETAPE_ANTICIPATE_READ_WRITE_DSC to 1 will allow ide-tape
+ *	to cleverly select the lowest possible frequency which will
+ *	not affect performance, based on the tape parameters and our operation
+ *	mode. This has potential to dramatically decrease our polling load
+ *	on Linux.
+ *
+ *	However, for the cases in which our calculation fails, setting
+ *	the following option to 0 will force the use of the "fallback"
+ *	polling period defined below (defaults to 50 msec).
+ *
+ *	In any case, the frequency will be between the "lowest" value
+ *	to the "fallback" value, to ensure that our selected "best" frequency
+ *	is reasonable.
+ */
+
+#define IDETAPE_ANTICIPATE_READ_WRITE_DSC	1
+
+/*
+ *	DSC timings.
+ */
+ 
+#define	IDETAPE_DSC_READ_WRITE_FALLBACK_FREQUENCY   5*HZ/100	/* 50 msec */
+#define IDETAPE_DSC_READ_WRITE_LOWEST_FREQUENCY	30*HZ/100	/* 300 msec */
 #define	IDETAPE_DSC_FAST_MEDIA_ACCESS_FREQUENCY	1*HZ		/* 1 second */
 #define	IDETAPE_FAST_SLOW_THRESHOLD		5*60*HZ		/* 5 minutes */
 #define IDETAPE_DSC_SLOW_MEDIA_ACCESS_FREQUENCY	60*HZ		/* 1 minute */
@@ -184,14 +235,13 @@
 /*
  *	Definitions which are already needed in ide.h
  */
- 
+
 /*
- *	The following is currently not used.
+ *	Current character device data transfer direction.
  */
+  
+typedef enum {idetape_direction_none,idetape_direction_read,idetape_direction_write} chrdev_direction_t;
 
-typedef enum {no_excess_data,excess_data_read,excess_data_write} excess_data_status_t;
-
- 
 struct ide_drive_s;				/* Forward declaration - Will be defined later in ide.h */
 typedef void (idetape_pc_completed_t)(struct ide_drive_s *);
 
@@ -203,10 +253,9 @@
 	byte c [12];				/* Actual packet bytes */
 	
 	byte retries;				/* On each retry, we increment retries */
-	byte error;				/* Set when an error occured */
-	byte active;				/* Set when a packet command is in progress */
-	byte wait_for_dsc;			/* 1 When polling for DSC */
-	byte dsc_count;		
+	byte error;				/* Error code */
+	byte abort;				/* Set when an error is considered normal - We won't retry */
+	byte wait_for_dsc;			/* 1 When polling for DSC on a media access command */
 	unsigned long request_transfer;		/* Bytes to transfer */
 	unsigned long actually_transferred; 	/* Bytes actually transferred */
 	unsigned long buffer_size;		/* Size of our data buffer */
@@ -353,12 +402,20 @@
 	byte dsc_count;				/* We received DSC dsc_count times in a row */
 	unsigned long dsc_polling_start;	/* The time in which we started polling for DSC */
 	struct timer_list dsc_timer;		/* Timer used to poll for dsc */
+
+	/*
+	 *	We can now be much more clever in our selection of the
+	 *	read/write polling frequency. This is used along with
+	 *	the compile time option IDETAPE_ANTICIPATE_DSC.
+	 */
+ 
+	unsigned long best_dsc_rw_frequency;	/* Read/Write dsc polling frequency */
+
 	unsigned long dsc_polling_frequency;	/* The current polling frequency */
 	unsigned long dsc_timeout;		/* Maximum waiting time */
 	byte dsc_received;			/* Set when we receive DSC */
 
 	byte request_status;
-	byte request_dsc_callback;
 	byte last_status;			/* Contents of the tape status register */
 						/* before the current request (saved for us */
 						/* by ide.c) */
@@ -374,7 +431,7 @@
 
 	/* Character device operation */
 
-	unsigned char last_dt_was_write;	/* Last character device data transfer was a write */
+	chrdev_direction_t chrdev_direction;	/* Current character device data transfer direction */
 	byte busy;				/* Device already opened */
 
 	/* Device information */
@@ -410,24 +467,25 @@
 		
 	int current_number_of_stages;		/* Number of currently used stages */
 	int max_number_of_stages;		/* We will not allocate more than this number of stages */
-	idetape_pipeline_stage_t *first_stage;	/* Will be serviced after the currently active request */
-	idetape_pipeline_stage_t *last_stage;	/* New write requests will be added to the pipeline here */
-	int pipeline_was_full_once;		/* Set at the first time we fill the pipeline since the tape was opened */
+	idetape_pipeline_stage_t *first_stage;	/* The first stage which will be removed from the pipeline */
+	idetape_pipeline_stage_t *active_stage;	/* The currently active stage */
+	idetape_pipeline_stage_t *next_stage;	/* Will be serviced after the currently active request */
+	idetape_pipeline_stage_t *last_stage;	/* New requests will be added to the pipeline here */
 	int error_in_pipeline_stage;		/* Set when an error was detected in one of the pipeline stages */	
-	int pipeline_locked;			/* Against race conditions ... */
 	
 } idetape_tape_t;
 
+/*
+ *	The following is used to have a quick look at the tape's status
+ *	register between requests of the other device.
+ */
+ 
 #define POLL_HWIF_TAPE_DRIVE							\
 	if (hwif->tape_drive != NULL) {						\
 		if (hwif->tape_drive->tape.request_status) {			\
 			OUT_BYTE(hwif->tape_drive->select.all,IDE_SELECT_REG);	\
 			hwif->tape_drive->tape.last_status=GET_STAT();		\
 			hwif->tape_drive->tape.request_status=0;		\
-		}								\
-		if (hwif->tape_drive->tape.request_dsc_callback) {		\
-			hwif->tape_drive->tape.request_dsc_callback=0;		\
-			idetape_put_back_postponed_request(hwif->tape_drive);	\
 		}								\
 	}
 
diff -u --recursive --new-file v1.3.53/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v1.3.53/linux/drivers/block/ide.c	Sun Dec 17 11:43:12 1995
+++ linux/drivers/block/ide.c	Thu Jan  4 07:43:41 1996
@@ -1,7 +1,7 @@
 /*
- *  linux/drivers/block/ide.c	Version 5.23  Dec 15, 1995
+ *  linux/drivers/block/ide.c	Version 5.24  Jan 4, 1996
  *
- *  Copyright (C) 1994, 1995  Linus Torvalds & authors (see below)
+ *  Copyright (C) 1994-1996  Linus Torvalds & authors (see below)
  */
 
 /*
@@ -179,6 +179,7 @@
  *  Version 5.21	fix nasty cdrom/tape bug (ide_preempt was messed up)
  *  Version 5.22	fix ide_xlate_1024() to work with/without drive->id
  *  Version 5.23	miscellaneous touch-ups
+ *  Version 5.24	fix #if's for SUPPORT_CMD640
  *
  *  Driver compile-time options are in ide.h
  *
@@ -221,7 +222,7 @@
 
 #include "ide.h"
 
-#ifdef SUPPORT_CMD640
+#if SUPPORT_CMD640
 void cmd640_tune_drive(ide_drive_t *);
 static int cmd640_vlb = 0;
 #endif
@@ -2209,7 +2210,7 @@
 			printk(", DMA");
 	}
 	printk("\n");
-#ifdef SUPPORT_CMD640
+#if SUPPORT_CMD640
 	cmd640_tune_drive(drive);	/* but can we tune a fish? */
 #endif
 }
@@ -2526,7 +2527,7 @@
 #include "umc8672.c"	/* until we tidy up the interface some more */
 #endif
 
-#ifdef SUPPORT_CMD640
+#if SUPPORT_CMD640
 #include "cmd640.c"	/* until we tidy up the interface some more */
 #endif
 
@@ -3037,7 +3038,7 @@
 	if (pcibios_present())
 		ide_init_pci ();
 #endif /* CONFIG_PCI */
-#ifdef SUPPORT_CMD640
+#if SUPPORT_CMD640
 	ide_probe_for_cmd640x();
 #endif
 
diff -u --recursive --new-file v1.3.53/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v1.3.53/linux/drivers/block/ide.h	Sat Dec 30 15:50:53 1995
+++ linux/drivers/block/ide.h	Thu Jan  4 15:27:43 1996
@@ -598,13 +598,6 @@
  
 void idetape_register_chrdev (void);
 
-/*
- *	The following function is called to re-insert a postponed tape
- *	request into the request queue.
- */
- 
-void idetape_put_back_postponed_request (ide_drive_t *drive);
-
 #endif /* CONFIG_BLK_DEV_IDETAPE */
 
 #ifdef CONFIG_BLK_DEV_TRITON
diff -u --recursive --new-file v1.3.53/linux/drivers/block/rd.c linux/drivers/block/rd.c
--- v1.3.53/linux/drivers/block/rd.c	Tue Jan  2 16:46:24 1996
+++ linux/drivers/block/rd.c	Wed Jan  3 14:04:52 1996
@@ -25,6 +25,11 @@
  * been completely removed.
  *
  * Loadable module support added by Tom Dyas.
+ *
+ * Further cleanups by Chad Page (page0588@sundance.sjsu.edu):
+ *	Cosmetic changes in #ifdef MODULE, code movement, etc...
+ * 	When the ramdisk is rmmod'ed, free the protected buffers
+ * 	Default ramdisk size changed to 2.88MB
  */
 
 #include <linux/sched.h>
@@ -52,10 +57,15 @@
 #define MAJOR_NR RAMDISK_MAJOR
 #include <linux/blk.h>
 
-#define BUILD_CRAMDISK
+/* These *should* be defined as parameters */
 #define NUM_RAMDISKS 8
+#define RD_DEFAULTSIZE	2880	/* 2.88 MB */
 
 #ifndef MODULE
+/* We don't have to load ramdisks or gunzip them in a module... */
+#define RD_LOADER
+#define BUILD_CRAMDISK
+
 void rd_load(void);
 static int crd_load(struct file *fp, struct file *outfp);
 #endif
@@ -106,10 +116,17 @@
 		goto repeat;
 	}
 
-	if (CURRENT->cmd == READ) {	
+	/*
+	 * If we're reading, fill the buffer with 0's.  This is okay since
+         * we're using protected buffers which should never get freed...
+	 *
+	 * If we're writing, we protect the buffer.
+  	 */
+
+	if (CURRENT->cmd == READ) 
 		memset(CURRENT->buffer, 0, len); 
-	}
-	set_bit(BH_Protected, &CURRENT->bh->b_state);
+	else	
+		set_bit(BH_Protected, &CURRENT->bh->b_state);
 
 	end_request(1);
 	goto repeat;
@@ -146,7 +163,6 @@
 
 static int rd_open(struct inode * inode, struct file * filp)
 {
-
 	if (DEVICE_NR(inode->i_rdev) >= NUM_RAMDISKS)
 		return -ENODEV;
 
@@ -192,7 +208,7 @@
 	blk_dev[MAJOR_NR].request_fn = &rd_request;
 
 	for (i = 0; i < NUM_RAMDISKS; i++) {
-		rd_length[i] = (16384 * 1024);
+		rd_length[i] = (RD_DEFAULTSIZE * 1024);
 		rd_blocksizes[i] = 1024;
 	}
 
@@ -201,7 +217,35 @@
 	return 0;
 }
 
-#ifndef MODULE
+/* loadable module support */
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	int error = rd_init();
+	if (!error)
+		printk(KERN_INFO "RAMDISK: Loaded as module.\n");
+	return error;
+}
+
+/* Before freeing the module, invalidate all of the protected buffers! */
+void cleanup_module(void)
+{
+	int i;
+
+	for (i = 0 ; i < NUM_RAMDISKS; i++)
+		invalidate_buffers(MKDEV(MAJOR_NR, i));
+
+	unregister_blkdev( MAJOR_NR, "ramdisk" );
+	blk_dev[MAJOR_NR].request_fn = 0;
+}
+
+#endif  /* MODULE */
+
+/* End of non-loading portions of the ramdisk driver */
+
+#ifdef RD_LOADER 
 /*
  * This routine tries to a ramdisk image to load, and returns the
  * number of blocks to read for a non-compressed image, 0 if the image
@@ -403,6 +447,7 @@
 		infile.f_op->release(&inode, &infile);
 	set_fs(fs);
 }
+#endif /* RD_LOADER */
 
 #ifdef BUILD_CRAMDISK
 
@@ -546,25 +591,3 @@
 
 #endif  /* BUILD_CRAMDISK */
 
-#endif  /* MODULE */
-
-
-/* loadable module support */
-
-#ifdef MODULE
-
-int init_module(void)
-{
-	int error = rd_init();
-	if (!error)
-		printk(KERN_INFO "RAMDISK: Loaded as module.\n");
-	return error;
-}
-
-void cleanup_module(void)
-{
-	unregister_blkdev( MAJOR_NR, "ramdisk" );
-	blk_dev[MAJOR_NR].request_fn = 0;
-}
-
-#endif  /* MODULE */
diff -u --recursive --new-file v1.3.53/linux/drivers/char/random.c linux/drivers/char/random.c
--- v1.3.53/linux/drivers/char/random.c	Thu Nov  9 11:23:48 1995
+++ linux/drivers/char/random.c	Wed Jan  3 14:04:53 1996
@@ -1,7 +1,7 @@
 /*
  * random.c -- A strong random number generator
  *
- * Version 0.95, last modified 4-Nov-95
+ * Version 0.96, last modified 29-Dec-95
  * 
  * Copyright Theodore Ts'o, 1994, 1995.  All rights reserved.
  *
@@ -276,7 +276,7 @@
 	}
 }
 
-void rand_initialize_blkdev(int major)
+void rand_initialize_blkdev(int major, int mode)
 {
 	struct timer_rand_state *state;
 	
@@ -287,7 +287,7 @@
 	 * If kamlloc returns null, we just won't use that entropy
 	 * source.
 	 */
-	state = kmalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
+	state = kmalloc(sizeof(struct timer_rand_state), mode);
 	if (state) {
 		blkdev_timer_state[major] = state;
 		memset(state, 0, sizeof(struct timer_rand_state));
@@ -437,9 +437,15 @@
 
 void add_blkdev_randomness(int major)
 {
-	if (major >= MAX_BLKDEV || blkdev_timer_state[major] == 0)
+	if (major >= MAX_BLKDEV)
 		return;
 
+	if (blkdev_timer_state[major] == 0) {
+		rand_initialize_blkdev(major, GFP_ATOMIC);
+		if (blkdev_timer_state[major] == 0)
+			return;
+	}
+		
 	add_timer_randomness(&random_state, blkdev_timer_state[major],
 			     0x200+major);
 }
diff -u --recursive --new-file v1.3.53/linux/drivers/net/lance.c linux/drivers/net/lance.c
--- v1.3.53/linux/drivers/net/lance.c	Tue Jan  2 16:46:25 1996
+++ linux/drivers/net/lance.c	Wed Jan  3 20:36:23 1996
@@ -976,36 +976,47 @@
 			if (status & 0x08) lp->stats.rx_crc_errors++;
 			if (status & 0x04) lp->stats.rx_fifo_errors++;
 			lp->rx_ring[entry].base &= 0x03ffffff;
-		} else {
+		}
+		else 
+		{
 			/* Malloc up new buffer, compatible with net-2e. */
 			short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4;
 			struct sk_buff *skb;
+			
+			if(pkt_len<60)
+			{
+				printk("%s: Runt packet!\n",dev->name);
+				lp->stats.rx_errors++;
+			}
+			else
+			{
+				skb = dev_alloc_skb(pkt_len+2);
+				if (skb == NULL) 
+				{
+					printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+					for (i=0; i < RX_RING_SIZE; i++)
+						if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].base < 0)
+							break;
 
-			skb = dev_alloc_skb(pkt_len+2);
-			if (skb == NULL) {
-				printk("%s: Memory squeeze, deferring packet.\n", dev->name);
-				for (i=0; i < RX_RING_SIZE; i++)
-				  if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].base < 0)
+					if (i > RX_RING_SIZE -2) 
+					{
+						lp->stats.rx_dropped++;
+						lp->rx_ring[entry].base |= 0x80000000;
+						lp->cur_rx++;
+					}
 					break;
-
-				if (i > RX_RING_SIZE -2) {
-				  lp->stats.rx_dropped++;
-				  lp->rx_ring[entry].base |= 0x80000000;
-				  lp->cur_rx++;
 				}
-				break;
+				skb->dev = dev;
+				skb_reserve(skb,2);	/* 16 byte align */
+				skb_put(skb,pkt_len);	/* Make room */
+				eth_copy_and_sum(skb,
+					(unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff),
+					pkt_len,0);
+				skb->protocol=eth_type_trans(skb,dev);
+				netif_rx(skb);
+				lp->stats.rx_packets++;
 			}
-			skb->dev = dev;
-			skb_reserve(skb,2);	/* 16 byte align */
-			skb_put(skb,pkt_len);	/* Make room */
-			eth_copy_and_sum(skb,
-				   (unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff),
-				   pkt_len,0);
-			skb->protocol=eth_type_trans(skb,dev);
-			netif_rx(skb);
-			lp->stats.rx_packets++;
 		}
-
 		/* The docs say that the buffer length isn't touched, but Andrew Boyd
 		   of QNX reports that some revs of the 79C965 clear it. */
 		lp->rx_ring[entry].buf_length = -PKT_BUF_SZ;
diff -u --recursive --new-file v1.3.53/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c
--- v1.3.53/linux/drivers/scsi/BusLogic.c	Fri Nov 17 08:42:26 1995
+++ linux/drivers/scsi/BusLogic.c	Mon Jan  1 09:26:10 1996
@@ -1,6 +1,6 @@
 /*
 
-  Linux Driver for BusLogic SCSI Host Adapters
+  Linux Driver for BusLogic MultiMaster SCSI Host Adapters
 
   Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>
 
@@ -18,15 +18,17 @@
   sent directly to him for evaluation and testing.
 
   Special thanks to Alex T. Win of BusLogic, whose advice has been invaluable,
-  and to David B. Gentzel, for writing the original Linux BusLogic driver.
+  to David B. Gentzel, for writing the original Linux BusLogic driver, and to
+  Paul Gortmaker, for being such a dedicated test site.
 
 */
 
 
-#define BusLogic_DriverVersion		"1.3.0"
-#define BusLogic_DriverDate		"13 November 1995"
+#define BusLogic_DriverVersion		"1.3.1"
+#define BusLogic_DriverDate		"31 December 1995"
 
 
+#include <linux/module.h>
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/blkdev.h>
@@ -67,12 +69,12 @@
 
 
 /*
-  BusLogic_TracingOptions is a bit mask of Tracing Options to be applied
+  BusLogic_GlobalOptions is a bit mask of Global Options to be applied
   across all Host Adapters.
 */
 
 static int
-  BusLogic_TracingOptions =		0;
+  BusLogic_GlobalOptions =		0;
 
 
 /*
@@ -146,8 +148,7 @@
   if (DriverAnnouncementPrinted) return;
   printk("scsi: ***** BusLogic SCSI Driver Version "
 	 BusLogic_DriverVersion " of " BusLogic_DriverDate " *****\n");
-  printk("scsi: Copyright 1995 by Leonard N. Zubkoff "
-	 "<lnz@dandelion.com>\n");
+  printk("scsi: Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>\n");
   DriverAnnouncementPrinted = true;
 }
 
@@ -315,8 +316,9 @@
   CCB = HostAdapter->Free_CCBs;
   if (CCB != NULL)
     {
-      CCB->SerialNumber = SerialNumber++;
+      CCB->SerialNumber = ++SerialNumber;
       HostAdapter->Free_CCBs = CCB->Next;
+      CCB->Next = NULL;
       BusLogic_UnlockHostAdapter(HostAdapter);
       return CCB;
     }
@@ -334,7 +336,7 @@
   CCB->HostAdapter = HostAdapter;
   CCB->Status = BusLogic_CCB_Free;
   BusLogic_LockHostAdapter(HostAdapter);
-  CCB->SerialNumber = SerialNumber++;
+  CCB->SerialNumber = ++SerialNumber;
   CCB->NextAll = HostAdapter->All_CCBs;
   HostAdapter->All_CCBs = CCB;
   BusLogic_UnlockHostAdapter(HostAdapter);
@@ -353,7 +355,6 @@
   BusLogic_LockHostAdapter(HostAdapter);
   CCB->Command = NULL;
   CCB->Status = BusLogic_CCB_Free;
-  CCB->SerialNumber = 0;
   CCB->Next = HostAdapter->Free_CCBs;
   HostAdapter->Free_CCBs = CCB;
   BusLogic_UnlockHostAdapter(HostAdapter);
@@ -396,24 +397,10 @@
   if (ReplyLength > 0)
     memset(ReplyData, 0, ReplyLength);
   /*
-    Select an appropriate timeout value.
-  */
-  switch (OperationCode)
-    {
-    case BusLogic_InquireInstalledDevicesID0to7:
-    case BusLogic_InquireInstalledDevicesID8to15:
-      /* Approximately 60 seconds. */
-      TimeoutCounter = loops_per_sec << 2;
-      break;
-    default:
-      /* Approximately 1 second. */
-      TimeoutCounter = loops_per_sec >> 4;
-      break;
-    }
-  /*
     Wait for the Host Adapter Ready bit to be set and the Command/Parameter
     Register Busy bit to be reset in the Status Register.
   */
+  TimeoutCounter = loops_per_sec >> 3;
   while (--TimeoutCounter >= 0)
     {
       StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
@@ -426,28 +413,38 @@
   /*
     Write the OperationCode to the Command/Parameter Register.
   */
+  HostAdapter->HostAdapterCommandCompleted = false;
   BusLogic_WriteCommandParameterRegister(HostAdapter, OperationCode);
   /*
     Write any additional Parameter Bytes.
   */
-  HostAdapter->HostAdapterCommandCompleted = false;
-  while (--ParameterLength >= 0)
+  TimeoutCounter = 10000;
+  while (ParameterLength > 0 && --TimeoutCounter >= 0)
     {
+      /*
+	Wait 100 microseconds to give the Host Adapter enough time to determine
+	whether the last value written to the Command/Parameter Register was
+	valid or not.  If the Command Complete bit is set in the Interrupt
+	Register, then the Command Invalid bit in the Status Register will be
+	reset if the Operation Code or Parameter was valid and the command
+	has completed, or set if the Operation Code or Parameter was invalid.
+	If the Data In Register Ready bit is set in the Status Register, then
+	the Operation Code was valid, and data is waiting to be read back
+	from the Host Adapter.  Otherwise, wait for the Command/Parameter
+	Register Busy bit in the Status Register to be reset.
+      */
+      udelay(100);
       InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
       if (InterruptRegister & BusLogic_CommandComplete) break;
       if (HostAdapter->HostAdapterCommandCompleted) break;
-      while (--TimeoutCounter >= 0)
-	{
-	  StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
-	  if (!(StatusRegister & BusLogic_CommandParameterRegisterBusy)) break;
-	}
-      BusLogic_CommandFailureReason =
-	"Timeout waiting for Parameter Acceptance";
-      if (TimeoutCounter < 0) return -2;
+      if (StatusRegister & BusLogic_DataInRegisterReady) break;
+      if (StatusRegister & BusLogic_CommandParameterRegisterBusy) continue;
       BusLogic_WriteCommandParameterRegister(HostAdapter, *ParameterPointer++);
+      ParameterLength--;
     }
-  BusLogic_CommandFailureReason = "Excess Parameters Supplied";
-  if (ParameterLength >= 0) return -1;
+  BusLogic_CommandFailureReason = "Timeout waiting for Parameter Acceptance";
+  if (TimeoutCounter < 0) return -2;
   /*
     The Modify I/O Address command does not cause a Command Complete Interrupt.
   */
@@ -455,14 +452,30 @@
     {
       StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
       BusLogic_CommandFailureReason = "Modify I/O Address Invalid";
-      return ((StatusRegister & BusLogic_CommandInvalid) ? -1 : 0);
+      if (StatusRegister & BusLogic_CommandInvalid) return -1;
+      BusLogic_CommandFailureReason = NULL;
+      return 0;
+    }
+  /*
+    Select an appropriate timeout value for awaiting command completion.
+  */
+  switch (OperationCode)
+    {
+    case BusLogic_InquireInstalledDevicesID0to7:
+    case BusLogic_InquireInstalledDevicesID8to15:
+      /* Approximately 60 seconds. */
+      TimeoutCounter = loops_per_sec << 2;
+      break;
+    default:
+      /* Approximately 1 second. */
+      TimeoutCounter = loops_per_sec >> 4;
+      break;
     }
   /*
     Receive any Reply Bytes, waiting for either the Command Complete bit to
     be set in the Interrupt Register, or for the Interrupt Handler to set the
-    HostAdapterCommandCompleted bit in the Host Adapter structure.
+    Host Adapter Command Completed bit in the Host Adapter structure.
   */
-  HostAdapter->HostAdapterCommandCompleted = false;
   while (--TimeoutCounter >= 0)
     {
       InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
@@ -473,36 +486,72 @@
 	if (++ReplyBytes <= ReplyLength)
 	  *ReplyPointer++ = BusLogic_ReadDataInRegister(HostAdapter);
 	else BusLogic_ReadDataInRegister(HostAdapter);
-  }
+    }
   BusLogic_CommandFailureReason = "Timeout waiting for Command Complete";
   if (TimeoutCounter < 0) return -2;
   /*
-    Clear any pending Command Complete Interrupt, unless this is a
-    Test Command Complete Interrupt command.
+    If testing Command Complete Interrupts, wait a short while in case the
+    loop immediately above terminated due to the Command Complete bit being
+    set in the Interrupt Register, but the interrupt hasn't actually been
+    processed yet.  Otherwise, acknowledging the interrupt here could prevent
+    the interrupt test from succeeding.
+  */
+  if (OperationCode == BusLogic_TestCommandCompleteInterrupt)
+    udelay(10000);
+  /*
+    Clear any pending Command Complete Interrupt.
   */
-  if (OperationCode != BusLogic_TestCommandCompleteInterrupt)
-    BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset);
-  if (BusLogic_TracingOptions & BusLogic_TraceConfiguration)
+  BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset);
+  if (BusLogic_GlobalOptions & BusLogic_TraceConfiguration)
     if (OperationCode != BusLogic_TestCommandCompleteInterrupt)
       {
 	int i;
 	printk("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:",
 	       OperationCode, StatusRegister, ReplyLength, ReplyBytes);
-	for (i = 0; i < ReplyBytes; i++)
+	if (ReplyLength > ReplyBytes) ReplyLength = ReplyBytes;
+	for (i = 0; i < ReplyLength; i++)
 	  printk(" %02X", ((unsigned char *) ReplyData)[i]);
 	printk("\n");
       }
   /*
-    Return count of Reply Bytes, or -1 if the command was invalid.
+    Process Command Invalid conditions.
   */
-  BusLogic_CommandFailureReason = "Command Invalid";
-  return ((StatusRegister & BusLogic_CommandInvalid) ? -1 : ReplyBytes);
+  if (StatusRegister & BusLogic_CommandInvalid)
+    {
+      /*
+	Some early BusLogic Host Adapters may not recover properly from
+	a Command Invalid condition, so if this appears to be the case,
+	a Soft Reset is issued to the Host Adapter.  Potentially invalid
+	commands are never attempted after Mailbox Initialization is
+	performed, so there should be no Host Adapter state lost by a
+	Soft Reset in response to a Command Invalid condition.
+      */
+      udelay(1000);
+      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+      if (StatusRegister != (BusLogic_HostAdapterReady |
+			     BusLogic_InitializationRequired))
+	{
+	  BusLogic_WriteControlRegister(HostAdapter, BusLogic_SoftReset);
+	  udelay(1000);
+	}
+      BusLogic_CommandFailureReason = "Command Invalid";
+      return -1;
+    }
+  /*
+    Handle Excess Parameters Supplied conditions.
+  */
+  BusLogic_CommandFailureReason = "Excess Parameters Supplied";
+  if (ParameterLength > 0) return -1;
+  /*
+    Indicate the command completed successfully.
+  */
+  BusLogic_CommandFailureReason = NULL;
+  return ReplyBytes;
 }
 
 
 /*
-  BusLogic_Failure prints a standardized error message for tests that are
-  executed before the SCSI Host is registered, and then returns false.
+  BusLogic_Failure prints a standardized error message, and then returns false.
 */
 
 static boolean BusLogic_Failure(BusLogic_HostAdapter_T *HostAdapter,
@@ -524,7 +573,7 @@
 
 static boolean BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
 {
-  boolean TraceProbe = (BusLogic_TracingOptions & BusLogic_TraceProbe);
+  boolean TraceProbe = (BusLogic_GlobalOptions & BusLogic_TraceProbe);
   unsigned char StatusRegister, GeometryRegister;
   /*
     Read the Status Register to test if there is an I/O port that responds.  A
@@ -570,7 +619,7 @@
 static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T
 					     *HostAdapter)
 {
-  boolean TraceHardReset = (BusLogic_TracingOptions & BusLogic_TraceHardReset);
+  boolean TraceHardReset = (BusLogic_GlobalOptions & BusLogic_TraceHardReset);
   long TimeoutCounter = loops_per_sec >> 2;
   unsigned char StatusRegister = 0;
   /*
@@ -591,6 +640,12 @@
 	   HostAdapter->IO_Address, StatusRegister);
   if (TimeoutCounter < 0) return false;
   /*
+    Wait 100 microseconds to allow completion of any initial diagnostic
+    activity which might leave the contents of the Status Register
+    unpredictable.
+  */
+  udelay(100);
+  /*
     Wait until Diagnostic Active is reset in the Status Register.
   */
   while (--TimeoutCounter >= 0)
@@ -655,13 +710,13 @@
   unsigned long ProcessorFlags;
   int Result;
   /*
-    Issue the Inquire Setup Information command.  Only genuine BusLogic Host
-    Adapters and true clones support this command.  Adaptec 1542C series Host
-    Adapters that respond to the Geometry Register I/O port will fail this
-    command.  Interrupts must be disabled around the call to BusLogic_Command
-    since a Command Complete interrupt could occur if the IRQ Channel was
-    previously enabled for another BusLogic Host Adapter sharing the same IRQ
-    Channel.
+    Issue the Inquire Extended Setup Information command.  Only genuine
+    BusLogic Host Adapters and true clones support this command.  Adaptec 1542C
+    series Host Adapters that respond to the Geometry Register I/O port will
+    fail this command.  Interrupts must be disabled around the call to
+    BusLogic_Command since a Command Complete interrupt could occur if the IRQ
+    Channel was previously enabled for another BusLogic Host Adapter sharing
+    the same IRQ Channel.
   */
   save_flags(ProcessorFlags);
   cli();
@@ -672,7 +727,7 @@
 			    &ExtendedSetupInformation,
 			    sizeof(ExtendedSetupInformation));
   restore_flags(ProcessorFlags);
-  if (BusLogic_TracingOptions & BusLogic_TraceProbe)
+  if (BusLogic_GlobalOptions & BusLogic_TraceProbe)
     printk("BusLogic_Check(0x%X): Result %d\n",
 	   HostAdapter->IO_Address, Result);
   return (Result == sizeof(ExtendedSetupInformation));
@@ -691,7 +746,7 @@
   BusLogic_Configuration_T Configuration;
   BusLogic_SetupInformation_T SetupInformation;
   BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation;
-  BusLogic_ModelAndRevision_T ModelAndRevision;
+  BusLogic_BoardModelNumber_T BoardModelNumber;
   BusLogic_FirmwareVersion3rdDigit_T FirmwareVersion3rdDigit;
   BusLogic_FirmwareVersionLetter_T FirmwareVersionLetter;
   BusLogic_RequestedReplyLength_T RequestedReplyLength;
@@ -733,14 +788,19 @@
       != sizeof(ExtendedSetupInformation))
     return BusLogic_Failure(HostAdapter, "INQUIRE EXTENDED SETUP INFORMATION");
   /*
-    Issue the Inquire Board Model and Revision command.
+    Issue the Inquire Board Model Number command.
   */
-  RequestedReplyLength = sizeof(ModelAndRevision);
-  if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardModelAndRevision,
-		       &RequestedReplyLength, sizeof(RequestedReplyLength),
-		       &ModelAndRevision, sizeof(ModelAndRevision))
-      != sizeof(ModelAndRevision))
-    return BusLogic_Failure(HostAdapter, "INQUIRE BOARD MODEL AND REVISION");
+  if (!(BoardID.FirmwareVersion1stDigit == '2' &&
+	ExtendedSetupInformation.BusType == 'A'))
+    {
+      RequestedReplyLength = sizeof(BoardModelNumber);
+      if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardModelNumber,
+			   &RequestedReplyLength, sizeof(RequestedReplyLength),
+			   &BoardModelNumber, sizeof(BoardModelNumber))
+	  != sizeof(BoardModelNumber))
+	return BusLogic_Failure(HostAdapter, "INQUIRE BOARD MODEL NUMBER");
+    }
+  else strcpy(BoardModelNumber, "542B");
   /*
     Issue the Inquire Firmware Version 3rd Digit command.
   */
@@ -753,7 +813,9 @@
     Issue the Inquire Firmware Version Letter command.
   */
   FirmwareVersionLetter = '\0';
-  if (BoardID.FirmwareVersion1stDigit >= '3')
+  if (BoardID.FirmwareVersion1stDigit > '3' ||
+      (BoardID.FirmwareVersion1stDigit == '3' &&
+       BoardID.FirmwareVersion2ndDigit >= '3'))
     if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersionLetter,
 			 NULL, 0, &FirmwareVersionLetter,
 			 sizeof(FirmwareVersionLetter))
@@ -764,21 +826,24 @@
     the major version number of their firmware as follows:
 
     4.xx	BusLogic "C" Series Host Adapters:
-		  946C/956C/956CD/747C/757C/757CD/445C/545C/540CF
+		  BT-946C/956C/956CD/747C/757C/757CD/445C/545C/540CF
     3.xx	BusLogic "S" Series Host Adapters:
-		  747S/747D/757S/757D/445S/545S/542D
-		  542B/742A (revision H)
+		  BT-747S/747D/757S/757D/445S/545S/542D
+		  BT-542B/742A (revision H)
     2.xx	BusLogic "A" Series Host Adapters:
-		  542B/742A (revision G and below)
-    0.xx	AMI FastDisk VLB BusLogic Clone Host Adapter
+		  BT-542B/742A (revision G and below)
+    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;
-  for (i = 0; i < sizeof(ModelAndRevision.Model); i++)
+  *TargetPointer++ = 'B';
+  *TargetPointer++ = 'T';
+  *TargetPointer++ = '-';
+  for (i = 0; i < sizeof(BoardModelNumber); i++)
     {
-      Character = ModelAndRevision.Model[i];
+      Character = BoardModelNumber[i];
       if (Character == ' ' || Character == '\0') break;
       *TargetPointer++ = Character;
     }
@@ -840,7 +905,7 @@
   if (ExtendedSetupInformation.BusType == 'A')
     HostAdapter->BusType = BusLogic_ISA_Bus;
   else
-    switch (HostAdapter->ModelName[0])
+    switch (HostAdapter->ModelName[3])
       {
       case '4':
 	HostAdapter->BusType = BusLogic_VESA_Bus;
@@ -881,7 +946,7 @@
       | SetupInformation.DisconnectPermittedID0to7;
   else HostAdapter->DisconnectPermitted = 0xFF;
   /*
-    Save the Scatter Gather Limits, Level Triggered Interrupts flag,
+    Save the Scatter Gather Limits, Level Sensitive Interrupts flag,
     Wide SCSI flag, and Differential SCSI flag in the Host Adapter structure.
   */
   HostAdapter->HostAdapterScatterGatherLimit =
@@ -890,8 +955,8 @@
     HostAdapter->HostAdapterScatterGatherLimit;
   if (HostAdapter->HostAdapterScatterGatherLimit > BusLogic_ScatterGatherLimit)
     HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit;
-  if (ExtendedSetupInformation.Misc.LevelTriggeredInterrupts)
-    HostAdapter->LevelTriggeredInterrupts = true;
+  if (ExtendedSetupInformation.Misc.LevelSensitiveInterrupts)
+    HostAdapter->LevelSensitiveInterrupts = true;
   if (ExtendedSetupInformation.HostWideSCSI)
     {
       HostAdapter->HostWideSCSI = true;
@@ -913,15 +978,28 @@
   */
   HostAdapter->BIOS_Address = ExtendedSetupInformation.BIOS_Address << 12;
   /*
+    BusLogic BT-445S Host Adapters prior to board revision D have a hardware
+    bug whereby when the BIOS is enabled, transfers to/from the same address
+    range the BIOS occupies modulo 16MB are handled incorrectly.  Only properly
+    functioning BT-445S boards have firmware version 3.37, so we require that
+    ISA bounce buffers be used for the buggy BT-445S models as well as for all
+    ISA models.
+  */
+  if (HostAdapter->BusType == BusLogic_ISA_Bus ||
+      (HostAdapter->BIOS_Address > 0 &&
+       strcmp(HostAdapter->ModelName, "BT-445S") == 0 &&
+       strcmp(HostAdapter->FirmwareVersion, "3.37") < 0))
+    HostAdapter->BounceBuffersRequired = true;
+  /*
     Select an appropriate value for Concurrency (Commands per Logical Unit)
-    either from a Command Line Entry, or based on whether this is an ISA
-    or non-ISA Host Adapter.
+    either from a Command Line Entry, or based on whether this Host Adapter
+    requires that ISA bounce buffers be used.
   */
   if (HostAdapter->CommandLineEntry != NULL &&
       HostAdapter->CommandLineEntry->Concurrency > 0)
     HostAdapter->Concurrency = HostAdapter->CommandLineEntry->Concurrency;
-  else if (HostAdapter->BusType == BusLogic_ISA_Bus)
-    HostAdapter->Concurrency = BusLogic_Concurrency_ISA;
+  else if (HostAdapter->BounceBuffersRequired)
+    HostAdapter->Concurrency = BusLogic_Concurrency_BB;
   else HostAdapter->Concurrency = BusLogic_Concurrency;
   /*
     Select an appropriate value for Bus Settle Time either from a Command
@@ -932,6 +1010,11 @@
     HostAdapter->BusSettleTime = HostAdapter->CommandLineEntry->BusSettleTime;
   else HostAdapter->BusSettleTime = BusLogic_DefaultBusSettleTime;
   /*
+    Select an appropriate value for Local Options from a Command Line Entry.
+  */
+  if (HostAdapter->CommandLineEntry != NULL)
+    HostAdapter->LocalOptions = HostAdapter->CommandLineEntry->LocalOptions;
+  /*
     Select appropriate values for the Error Recovery Option array either from
     a Command Line Entry, or using BusLogic_ErrorRecoveryDefault.
   */
@@ -953,23 +1036,27 @@
   if (HostAdapter->Concurrency > 1)
     switch (HostAdapter->FirmwareVersion[0])
       {
+      case '5':
+	TaggedQueuingPermittedDefault = 0xFFFF;
+	break;
       case '4':
-	if (HostAdapter->FirmwareVersion[2] > '2' ||
-	    (HostAdapter->FirmwareVersion[2] == '2' &&
-	     HostAdapter->FirmwareVersion[3] >= '2'))
+	if (strcmp(HostAdapter->FirmwareVersion, "4.22") >= 0)
 	  TaggedQueuingPermittedDefault = 0xFFFF;
 	break;
       case '3':
-	if (HostAdapter->FirmwareVersion[2] > '3' ||
-	    (HostAdapter->FirmwareVersion[2] == '3' &&
-	     HostAdapter->FirmwareVersion[3] >= '5'))
+	if (strcmp(HostAdapter->FirmwareVersion, "3.35") >= 0)
 	  TaggedQueuingPermittedDefault = 0xFFFF;
 	break;
       }
   /*
-    Combine the default Tagged Queuing permission based on the Host Adapter
-    firmware version and Concurrency with any Command Line Entry Tagged
-    Queuing specification.
+    Tagged Queuing is only useful if Disconnect/Reconnect is permitted.
+    Therefore, mask the Tagged Queuing Permitted Default bits with the
+    Disconnect/Reconnect Permitted bits.
+  */
+  TaggedQueuingPermittedDefault &= HostAdapter->DisconnectPermitted;
+  /*
+    Combine the default Tagged Queuing Permitted Default bits with any
+    Command Line Entry Tagged Queuing specification.
   */
   if (HostAdapter->CommandLineEntry != NULL)
     HostAdapter->TaggedQueuingPermitted =
@@ -990,7 +1077,7 @@
 	 "IRQ Channel: %d/%s\n",
 	 HostAdapter->HostNumber, HostAdapter->FirmwareVersion,
 	 HostAdapter->IO_Address, HostAdapter->IRQ_Channel,
-	 (HostAdapter->LevelTriggeredInterrupts ? "Level" : "Edge"));
+	 (HostAdapter->LevelSensitiveInterrupts ? "Level" : "Edge"));
   printk("scsi%d:   DMA Channel: ", HostAdapter->HostNumber);
   if (HostAdapter->DMA_Channel > 0)
     printk("%d, ", HostAdapter->DMA_Channel);
@@ -1091,7 +1178,7 @@
 	{
 	  if (FirstHostAdapter->IRQ_Channel == HostAdapter->IRQ_Channel)
 	    {
-	      if (strlen(FirstHostAdapter->InterruptLabel) + 8
+	      if (strlen(FirstHostAdapter->InterruptLabel) + 11
 		  < sizeof(FirstHostAdapter->InterruptLabel))
 		{
 		  strcat(FirstHostAdapter->InterruptLabel, " + ");
@@ -1129,21 +1216,7 @@
   Host->can_queue = BusLogic_MailboxCount;
   Host->cmd_per_lun = HostAdapter->Concurrency;
   Host->sg_tablesize = HostAdapter->DriverScatterGatherLimit;
-  Host->unchecked_isa_dma = (HostAdapter->BusType == BusLogic_ISA_Bus);
-  /*
-    BusLogic 445S Host Adapters prior to board revision D have a hardware bug
-    whereby when the BIOS is enabled, transfers to/from the same address range
-    the BIOS occupies modulo 16MB are handled incorrectly.  Since 16KB out of
-    each 16MB after the first is such a small amount of memory, this memory
-    can be marked as reserved without a significant loss of performance; this
-    is a much cheaper solution than requiring that ISA bounce buffers be used.
-  */
-  if (HostAdapter->BIOS_Address > 0 &&
-      strcmp(HostAdapter->ModelName, "445S") == 0)
-    {
-      Host->forbidden_addr = HostAdapter->BIOS_Address;
-      Host->forbidden_size = 16*1024;
-    }
+  Host->unchecked_isa_dma = HostAdapter->BounceBuffersRequired;
   /*
     Indicate the System Resource Acquisition completed successfully,
   */
@@ -1200,26 +1273,17 @@
   FinalInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel];
   if (FinalInterruptCount < InitialInterruptCount + TestCount)
     {
-      printk("scsi%d: HOST ADAPTER INTERRUPT TEST FAILED - DETACHING\n",
-	     HostAdapter->HostNumber);
-      printk("scsi%d:  Interrupts are not getting through "
-	     "from the Host Adapter to the\n", HostAdapter->HostNumber);
-      printk("scsi%d:  BusLogic Driver Interrupt Handler.  "
-	     "The most likely cause is that\n", HostAdapter->HostNumber);
-      printk("scsi%d:  either the Host Adapter or Motherboard "
-	     "is configured incorrectly.\n", HostAdapter->HostNumber);
-      printk("scsi%d:  Please check the Host Adapter configuration "
-	     "with AutoSCSI or by\n", HostAdapter->HostNumber);
-      printk("scsi%d:  examining any dip switch and jumper settings "
-	     "on the Host Adapter, and\n", HostAdapter->HostNumber);
-      printk("scsi%d:  verify that no other device is attempting to "
-	     "use the same IRQ Channel.\n", HostAdapter->HostNumber);
-      printk("scsi%d:  For PCI Host Adapters, it may also be necessary "
-	     "to investigate and\n", HostAdapter->HostNumber);
-      printk("scsi%d:  manually set the PCI interrupt assignments "
-	     "and edge/level interrupt\n", HostAdapter->HostNumber);
-      printk("scsi%d:  type selection in the BIOS Setup Program or "
-	     "with Motherboard jumpers.\n", HostAdapter->HostNumber);
+      BusLogic_Failure(HostAdapter, "HOST ADAPTER INTERRUPT TEST");
+      printk("\n\
+Interrupts are not getting through from the Host Adapter to the BusLogic\n\
+Driver Interrupt Handler. The most likely cause is that either the Host\n\
+Adapter or Motherboard is configured incorrectly.  Please check the Host\n\
+Adapter configuration with AutoSCSI or by examining any dip switch and\n\
+jumper settings on the Host Adapter, and verify that no other device is\n\
+attempting to use the same IRQ Channel.  For PCI Host Adapters, it may also\n\
+be necessary to investigate and manually set the PCI interrupt assignments\n\
+and edge/level interrupt type selection in the BIOS Setup Program or with\n\
+Motherboard jumpers.\n\n");
       return false;
     }
   /*
@@ -1243,13 +1307,15 @@
   BusLogic_WideModeCCBRequest_T WideModeCCBRequest;
   BusLogic_ModifyIOAddressRequest_T ModifyIOAddressRequest;
   /*
-    Initialize Read/Write Operation Count and Command Successful Flag
-    for each Target.
+    Initialize the Command Successful Flag, Read/Write Operation Count,
+    and Queued Operation Count for each Target.
   */
-  memset(HostAdapter->ReadWriteOperationCount, 0,
-	 sizeof(HostAdapter->ReadWriteOperationCount));
   memset(HostAdapter->CommandSuccessfulFlag, false,
 	 sizeof(HostAdapter->CommandSuccessfulFlag));
+  memset(HostAdapter->ReadWriteOperationCount, 0,
+	 sizeof(HostAdapter->ReadWriteOperationCount));
+  memset(HostAdapter->QueuedOperationCount, 0,
+	 sizeof(HostAdapter->QueuedOperationCount));
   /*
     Initialize the Outgoing and Incoming Mailbox structures.
   */
@@ -1276,11 +1342,7 @@
   if (BusLogic_Command(HostAdapter, BusLogic_InitializeExtendedMailbox,
 		       &ExtendedMailboxRequest,
 		       sizeof(ExtendedMailboxRequest), NULL, 0) < 0)
-    {
-      printk("scsi%d: MAILBOX INITIALIZATION FAILED - DETACHING\n",
-	     HostAdapter->HostNumber);
-      return false;
-    }
+    return BusLogic_Failure(HostAdapter, "MAILBOX INITIALIZATION");
   /*
     Enable Strict Round Robin Mode if supported by the Host Adapter.  In Strict
     Round Robin Mode, the Host Adapter only looks at the next Outgoing Mailbox
@@ -1288,10 +1350,14 @@
     Mailboxes to find any that have new commands in them.  BusLogic indicates
     that Strict Round Robin Mode is significantly more efficient.
   */
-  RoundRobinModeRequest = BusLogic_StrictRoundRobinMode;
-  BusLogic_Command(HostAdapter, BusLogic_EnableStrictRoundRobinMode,
-		   &RoundRobinModeRequest,
-		   sizeof(RoundRobinModeRequest), NULL, 0);
+  if (strcmp(HostAdapter->FirmwareVersion, "3.31") >= 0)
+    {
+      RoundRobinModeRequest = BusLogic_StrictRoundRobinMode;
+      if (BusLogic_Command(HostAdapter, BusLogic_EnableStrictRoundRobinMode,
+			   &RoundRobinModeRequest,
+			   sizeof(RoundRobinModeRequest), NULL, 0) < 0)
+	return BusLogic_Failure(HostAdapter, "ENABLE STRICT ROUND ROBIN MODE");
+    }
   /*
     For Wide SCSI Host Adapters, issue the Enable Wide Mode CCB command to
     allow more than 8 Logical Units per Target to be supported.
@@ -1302,11 +1368,7 @@
       if (BusLogic_Command(HostAdapter, BusLogic_EnableWideModeCCB,
 			   &WideModeCCBRequest,
 			   sizeof(WideModeCCBRequest), NULL, 0) < 0)
-	{
-	  printk("scsi%d: ENABLE WIDE MODE CCB FAILED - DETACHING\n",
-		 HostAdapter->HostNumber);
-	  return false;
-	}
+	return BusLogic_Failure(HostAdapter, "ENABLE WIDE MODE CCB");
     }
   /*
     For PCI Host Adapters being accessed through the PCI compliant I/O
@@ -1325,11 +1387,7 @@
 	  if (BusLogic_Command(HostAdapter, BusLogic_ModifyIOAddress,
 			       &ModifyIOAddressRequest,
 			       sizeof(ModifyIOAddressRequest), NULL, 0) < 0)
-	    {
-	      printk("scsi%d: MODIFY I/O ADDRESS FAILED - DETACHING\n",
-		     HostAdapter->HostNumber);
-	      return false;
-	    }
+	    return BusLogic_Failure(HostAdapter, "MODIFY I/O ADDRESS");
 	}
     }
   /*
@@ -1365,6 +1423,15 @@
   */
   BusLogic_Delay(HostAdapter->BusSettleTime);
   /*
+    Inhibit the Target Devices Inquiry if requested.
+  */
+  if (HostAdapter->LocalOptions & BusLogic_InhibitTargetInquiry)
+    {
+      printk("scsi%d:   Target Device Inquiry Inhibited\n",
+	     HostAdapter->HostNumber);
+      return true;
+    }
+  /*
     Issue the Inquire Installed Devices ID 0 to 7 command, and for Wide SCSI
     Host Adapters the Inquire Installed Devices ID 8 to 15 command.  This is
     necessary to force Synchronous Transfer Negotiation so that the Inquire
@@ -1395,12 +1462,22 @@
   /*
     Issue the Inquire Synchronous Period command.
   */
-  RequestedReplyLength = sizeof(SynchronousPeriod);
-  if (BusLogic_Command(HostAdapter, BusLogic_InquireSynchronousPeriod,
-		       &RequestedReplyLength, 1,
-		       &SynchronousPeriod, sizeof(SynchronousPeriod))
-      != sizeof(SynchronousPeriod))
-    return BusLogic_Failure(HostAdapter, "INQUIRE SYNCHRONOUS PERIOD");
+  if (HostAdapter->FirmwareVersion[0] >= '3')
+    {
+      RequestedReplyLength = sizeof(SynchronousPeriod);
+      if (BusLogic_Command(HostAdapter, BusLogic_InquireSynchronousPeriod,
+			   &RequestedReplyLength, sizeof(RequestedReplyLength),
+			   &SynchronousPeriod, sizeof(SynchronousPeriod))
+	  != sizeof(SynchronousPeriod))
+	return BusLogic_Failure(HostAdapter, "INQUIRE SYNCHRONOUS PERIOD");
+    }
+  else
+    for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+      if (SetupInformation.SynchronousValuesID0to7[TargetID].Offset > 0)
+	SynchronousPeriod[TargetID] =
+	  20 + 5 * SetupInformation.SynchronousValuesID0to7[TargetID]
+				   .TransferPeriod;
+      else SynchronousPeriod[TargetID] = 0;
   /*
     Save the Installed Devices, Synchronous Values, and Synchronous Period
     information in the Host Adapter structure.
@@ -1430,7 +1507,7 @@
 	    int RoundedSynchronousTransferRate =
 	      (SynchronousTransferRate + 5000) / 10000;
 	    printk("scsi%d:   Target %d: Synchronous at "
-		   "%d.%02d mega-transfers/sec, offset %d\n",
+		   "%d.%02d mega-transfers/second, offset %d\n",
 		   HostAdapter->HostNumber, TargetID,
 		   RoundedSynchronousTransferRate / 100,
 		   RoundedSynchronousTransferRate % 100,
@@ -1442,7 +1519,7 @@
 	    int RoundedSynchronousTransferRate =
 	      (SynchronousTransferRate + 50000) / 100000;
 	    printk("scsi%d:   Target %d: Synchronous at "
-		   "%d.%01d mega-transfers/sec, offset %d\n",
+		   "%d.%01d mega-transfers/second, offset %d\n",
 		   HostAdapter->HostNumber, TargetID,
 		   RoundedSynchronousTransferRate / 10,
 		   RoundedSynchronousTransferRate % 10,
@@ -1555,8 +1632,8 @@
       /*
 	Read the Host Adapter Configuration, Acquire the System Resources
 	necessary to use Host Adapter and initialize the fields in the SCSI
-	Host structure, then Test Interrupts, Create the CCBs, and finally
-	Initialize the Host Adapter.
+	Host structure, then Test Interrupts, Create the CCBs, Initialize
+	the Host Adapter, and finally Inquire about the Target Devices.
       */
       if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) &&
 	  BusLogic_AcquireResources(HostAdapter, Host) &&
@@ -1579,10 +1656,11 @@
 	{
 	  /*
 	    An error occurred during Host Adapter Configuration Querying,
-	    Resource Acquisition, Interrupt Testing, CCB Creation, or Host
-	    Adapter Initialization, so remove Host Adapter from the list of
-	    registered BusLogic Host Adapters, destroy the CCBs, Release
-	    the System Resources, and Unregister the SCSI Host.
+	    Resource Acquisition, Interrupt Testing, CCB Creation, Host
+	    Adapter Initialization, or Target Device Inquiry, so remove
+	    Host Adapter from the list of registered BusLogic Host Adapters,
+	    destroy the CCBs, Release the System Resources, and Unregister
+	    the SCSI Host.
 	  */
 	  BusLogic_DestroyCCBs(HostAdapter);
 	  BusLogic_ReleaseResources(HostAdapter);
@@ -1735,46 +1813,60 @@
 		Scan through the Incoming Mailboxes in Strict Round Robin
 		fashion, saving any completed CCBs for further processing.
 		It is essential that for each CCB and SCSI Command issued,
-		completion processing is performed exactly once.  Therefore,
-		only Incoming Mailbox entries with completion code Command
-		Completed Without Error, Command Completed With Error, or
-		Command Aborted At Host Request are saved for completion
-		processing.  When an Incoming Mailbox entry has a completion
-		code of Aborted Command Not Found, the CCB had already
-		completed or been aborted before the current Abort request
-		was processed, and so completion processing has already
-		occurred and no further action should be taken.
+		command completion processing is performed exactly once.
+		Therefore, only Incoming Mailboxes with completion code
+		Command Completed Without Error, Command Completed With
+		Error, or Command Aborted At Host Request are saved for
+		completion processing.  When an Incoming Mailbox has a
+		completion code of Aborted Command Not Found, the CCB had
+		already completed or been aborted before the current Abort
+		request was processed, and so completion processing has
+		already occurred and no further action should be taken.
 	      */
 	      BusLogic_IncomingMailbox_T *NextIncomingMailbox =
 		HostAdapter->NextIncomingMailbox;
-	      while (NextIncomingMailbox->CompletionCode !=
+	      BusLogic_CompletionCode_T MailboxCompletionCode;
+	      while ((MailboxCompletionCode =
+		      NextIncomingMailbox->CompletionCode) !=
 		     BusLogic_IncomingMailboxFree)
 		{
 		  BusLogic_CCB_T *CCB = NextIncomingMailbox->CCB;
-		  BusLogic_CompletionCode_T MailboxCompletionCode =
-		    NextIncomingMailbox->CompletionCode;
 		  if (MailboxCompletionCode != BusLogic_AbortedCommandNotFound)
-		    {
-		      /*
-			Mark this CCB as completed and add it to the end
-			of the list of completed CCBs.
-		      */
-		      CCB->Status = BusLogic_CCB_Completed;
-		      CCB->MailboxCompletionCode = MailboxCompletionCode;
-		      CCB->Next = NULL;
-		      if (FirstCompletedCCB == NULL)
-			{
-			  FirstCompletedCCB = CCB;
-			  LastCompletedCCB = CCB;
-			}
-		      else
-			{
-			  LastCompletedCCB->Next = CCB;
-			  LastCompletedCCB = CCB;
-			}
-		    }
-		  else printk("scsi%d: Aborted CCB #%d Not Found\n",
-			      HostAdapter->HostNumber, CCB->SerialNumber);
+		    if (CCB->Status == BusLogic_CCB_Active)
+		      {
+			/*
+			  Mark this CCB as completed and add it to the end
+			  of the list of completed CCBs.
+			*/
+			CCB->Status = BusLogic_CCB_Completed;
+			CCB->MailboxCompletionCode = MailboxCompletionCode;
+			CCB->Next = NULL;
+			if (FirstCompletedCCB == NULL)
+			  {
+			    FirstCompletedCCB = CCB;
+			    LastCompletedCCB = CCB;
+			  }
+			else
+			  {
+			    LastCompletedCCB->Next = CCB;
+			    LastCompletedCCB = CCB;
+			  }
+			HostAdapter->QueuedOperationCount[CCB->TargetID]--;
+		      }
+		    else
+		      {
+			/*
+			  If a CCB ever appears in an Incoming Mailbox and
+			  is not marked as status Active, then there is
+			  most likely a bug in the Host Adapter firmware.
+			*/
+			printk("scsi%d: Illegal CCB #%d status %d in "
+			       "Incoming Mailbox\n", HostAdapter->HostNumber,
+			       CCB->SerialNumber, CCB->Status);
+		      }
+		  else printk("scsi%d: Aborted CCB #%d to Target %d "
+			      "Not Found\n", HostAdapter->HostNumber,
+			      CCB->SerialNumber, CCB->TargetID);
 		  NextIncomingMailbox->CompletionCode =
 		    BusLogic_IncomingMailboxFree;
 		  if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox)
@@ -1838,23 +1930,23 @@
 	  {
 	  case BusLogic_IncomingMailboxFree:
 	  case BusLogic_AbortedCommandNotFound:
-	    printk("scsi%d: CCB #%d Impossible State\n",
-		   HostAdapter->HostNumber, CCB->SerialNumber);
+	    printk("scsi%d: CCB #%d to Target %d Impossible State\n",
+		   HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
 	    break;
 	  case BusLogic_CommandCompletedWithoutError:
 	    HostAdapter->CommandSuccessfulFlag[CCB->TargetID] = true;
 	    Command->result = DID_OK << 16;
 	    break;
 	  case BusLogic_CommandAbortedAtHostRequest:
-	    printk("scsi%d: CCB #%d Aborted\n",
-		   HostAdapter->HostNumber, CCB->SerialNumber);
+	    printk("scsi%d: CCB #%d to Target %d Aborted\n",
+		   HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
 	    Command->result = DID_ABORT << 16;
 	    break;
 	  case BusLogic_CommandCompletedWithError:
 	    Command->result =
 	      BusLogic_ComputeResultCode(CCB->HostAdapterStatus,
 					 CCB->TargetDeviceStatus);
-	    if (BusLogic_TracingOptions & BusLogic_TraceErrors)
+	    if (BusLogic_GlobalOptions & BusLogic_TraceErrors)
 	      if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout)
 		{
 		  int i;
@@ -1887,15 +1979,14 @@
 
 
 /*
-  BusLogic_WriteOutgoingMailboxEntry writes an Outgoing Mailbox entry
-  for Host Adapter with Action Code and CCB.
+  BusLogic_WriteOutgoingMailbox places CCB and Action Code into an Outgoing
+  Mailbox for execution by Host Adapter.
 */
 
-static boolean BusLogic_WriteOutgoingMailboxEntry(BusLogic_HostAdapter_T
-						    *HostAdapter,
-						  BusLogic_ActionCode_T
-						    ActionCode,
-						  BusLogic_CCB_T *CCB)
+static boolean BusLogic_WriteOutgoingMailbox(BusLogic_HostAdapter_T
+					       *HostAdapter,
+					     BusLogic_ActionCode_T ActionCode,
+					     BusLogic_CCB_T *CCB)
 {
   BusLogic_OutgoingMailbox_T *NextOutgoingMailbox;
   boolean Result = false;
@@ -1903,13 +1994,20 @@
   NextOutgoingMailbox = HostAdapter->NextOutgoingMailbox;
   if (NextOutgoingMailbox->ActionCode == BusLogic_OutgoingMailboxFree)
     {
-      NextOutgoingMailbox->ActionCode = ActionCode;
-      NextOutgoingMailbox->CCB = CCB;
       CCB->Status = BusLogic_CCB_Active;
+      /*
+	The CCB field must be written before the Action Code field since
+	the Host Adapter is operating asynchronously and the locking code
+	does not protect against simultaneous access by the Host Adapter.
+      */
+      NextOutgoingMailbox->CCB = CCB;
+      NextOutgoingMailbox->ActionCode = ActionCode;
       BusLogic_StartMailboxScan(HostAdapter);
       if (++NextOutgoingMailbox > HostAdapter->LastOutgoingMailbox)
 	NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
       HostAdapter->NextOutgoingMailbox = NextOutgoingMailbox;
+      if (ActionCode == BusLogic_MailboxStartCommand)
+	HostAdapter->QueuedOperationCount[CCB->TargetID]++;
       Result = true;
     }
   BusLogic_UnlockHostAdapter(HostAdapter);
@@ -1948,13 +2046,14 @@
       return 0;
     }
   /*
-    Allocate a CCB from the Host Adapter's free list, aborting the command
-    with an error if there are none available and memory allocation fails.
+    Allocate a CCB from the Host Adapter's free list.  If there are none
+    available and memory allocation fails, return a result code of Bus Busy
+    so that this Command will be retried.
   */
   CCB = BusLogic_AllocateCCB(HostAdapter);
   if (CCB == NULL)
     {
-      Command->result = DID_ERROR << 16;
+      Command->result = DID_BUS_BUSY << 16;
       CompletionRoutine(Command);
       return 0;
     }
@@ -2000,6 +2099,8 @@
     }
   CCB->CDB_Length = CDB_Length;
   CCB->SenseDataLength = sizeof(Command->sense_buffer);
+  CCB->HostAdapterStatus = 0;
+  CCB->TargetDeviceStatus = 0;
   CCB->TargetID = TargetID;
   CCB->LogicalUnit = LogicalUnit;
   /*
@@ -2017,24 +2118,48 @@
     BusLogic recommends that after a Reset the first couple of commands that
     are sent to a Target be sent in a non Tagged Queue fashion so that the Host
     Adapter and Target can establish Synchronous Transfer before Queue Tag
-    messages can interfere with the Synchronous Negotiation message.
+    messages can interfere with the Synchronous Negotiation message.  By
+    waiting to enable tagged Queuing until after the first 16 read/write
+    commands have been sent, it is assured that the Tagged Queuing message
+    will not occur while the partition table is printed.
   */
   if ((HostAdapter->TaggedQueuingPermitted & (1 << TargetID)) &&
       Command->device->tagged_supported &&
-      (EnableTQ = HostAdapter->ReadWriteOperationCount[TargetID] - 5) >= 0)
+      (EnableTQ = HostAdapter->ReadWriteOperationCount[TargetID] - 16) >= 0)
     {
+      BusLogic_QueueTag_T QueueTag = BusLogic_SimpleQueueTag;
+      unsigned long CurrentTime = jiffies;
       if (EnableTQ == 0)
 	printk("scsi%d: Tagged Queuing now active for Target %d\n",
 	       HostAdapter->HostNumber, TargetID);
+      /*
+	When using Tagged Queuing with Simple Queue Tags, it appears that disk
+	drive controllers do not guarantee that a queued command will not
+	remain in a disconnected state indefinitely if commands that read or
+	write nearer the head position continue to arrive without interruption.
+	Therefore, for each Target Device this driver keeps track of the last
+	time either the queue was empty or an Ordered Queue Tag was issued.  If
+	more than 2 seconds have elapsed since this last sequence point, this
+	command will be issued with an Ordered Queue Tag rather than a Simple
+	Queue Tag, which forces the Target Device to complete all previously
+	queued commands before this command may be executed.
+      */
+      if (HostAdapter->QueuedOperationCount[TargetID] == 0)
+	HostAdapter->LastSequencePoint[TargetID] = CurrentTime;
+      else if (CurrentTime - HostAdapter->LastSequencePoint[TargetID] > 2*HZ)
+	{
+	  HostAdapter->LastSequencePoint[TargetID] = CurrentTime;
+	  QueueTag = BusLogic_OrderedQueueTag;
+	}
       if (HostAdapter->HostWideSCSI)
 	{
 	  CCB->WideModeTagEnable = true;
-	  CCB->WideModeQueueTag = BusLogic_SimpleQueueTag;
+	  CCB->WideModeQueueTag = QueueTag;
 	}
       else
 	{
 	  CCB->TagEnable = true;
-	  CCB->QueueTag = BusLogic_SimpleQueueTag;
+	  CCB->QueueTag = QueueTag;
 	}
     }
   memcpy(CCB->CDB, CDB, CDB_Length);
@@ -2042,16 +2167,17 @@
   CCB->Command = Command;
   Command->scsi_done = CompletionRoutine;
   /*
-    Place the CCB in an Outgoing Mailbox, aborting the command with an
-    error if there are none available.
+    Place the CCB in an Outgoing Mailbox.  If there are no Outgoing
+    Mailboxes available, return a result code of Bus Busy so that this
+    Command will be retried.
   */
-  if (!(BusLogic_WriteOutgoingMailboxEntry(
-	  HostAdapter, BusLogic_MailboxStartCommand, CCB)))
+  if (!(BusLogic_WriteOutgoingMailbox(HostAdapter,
+				      BusLogic_MailboxStartCommand, CCB)))
     {
-      printk("scsi%d: cannot write Outgoing Mailbox Entry\n",
+      printk("scsi%d: cannot write Outgoing Mailbox\n",
 	     HostAdapter->HostNumber);
       BusLogic_DeallocateCCB(CCB);
-      Command->result = DID_ERROR << 16;
+      Command->result = DID_BUS_BUSY << 16;
       CompletionRoutine(Command);
     }
   return 0;
@@ -2066,6 +2192,7 @@
 {
   BusLogic_HostAdapter_T *HostAdapter =
     (BusLogic_HostAdapter_T *) Command->host->hostdata;
+  unsigned long CommandPID = Command->pid;
   unsigned char InterruptRegister;
   BusLogic_CCB_T *CCB;
   int Result;
@@ -2079,7 +2206,7 @@
   if (InterruptRegister & BusLogic_InterruptValid)
     {
       unsigned long ProcessorFlags;
-      printk("scsi%d: Recovering Lost Interrupt for IRQ Channel %d\n",
+      printk("scsi%d: Recovering Lost/Delayed Interrupt for IRQ Channel %d\n",
 	     HostAdapter->HostNumber, HostAdapter->IRQ_Channel);
       save_flags(ProcessorFlags);
       cli();
@@ -2088,41 +2215,54 @@
       return SCSI_ABORT_SNOOZE;
     }
   /*
-    Find the CCB to be aborted and determine how to proceed.
+    Find the CCB to be aborted if possible.
   */
   BusLogic_LockHostAdapter(HostAdapter);
-  Result = SCSI_ABORT_NOT_RUNNING;
   for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
-    if (CCB->Command == Command)
-      {
-	if (CCB->Status == BusLogic_CCB_Active)
-	  if ((HostAdapter->HostWideSCSI && CCB->WideModeTagEnable &&
-	       CCB->WideModeQueueTag != BusLogic_SimpleQueueTag) ||
-	      (!HostAdapter->HostWideSCSI && CCB->TagEnable &&
-	       CCB->QueueTag != BusLogic_SimpleQueueTag))
-	    {
-	      /*
-		CCBs using Tagged Queuing with other than Simple Queue Tag
-		should not be aborted.
-	      */
-	      Result = SCSI_ABORT_BUSY;
-	    }
-	  else
-	    {
-	      /*
-		Attempt to abort the CCB.
-	      */
-	      if (BusLogic_WriteOutgoingMailboxEntry(
-		    HostAdapter, BusLogic_MailboxAbortCommand, CCB))
-		{
-		  printk("scsi%d: Aborting CCB #%d\n",
-			 HostAdapter->HostNumber, CCB->SerialNumber);
-		  Result = SCSI_ABORT_PENDING;
-		}
-	      else Result = SCSI_ABORT_BUSY;
-	    }
-	break;
-      }
+    if (CCB->Command == Command) break;
+  BusLogic_UnlockHostAdapter(HostAdapter);
+  if (CCB == NULL)
+    {
+      printk("scsi%d: Unable to Abort Command to Target %d - No CCB Found\n",
+	     HostAdapter->HostNumber, Command->target);
+      return SCSI_ABORT_NOT_RUNNING;
+    }
+  /*
+    Briefly pause to see if this command will complete.
+  */
+  printk("scsi%d: Pausing briefly to see if CCB #%d "
+	 "to Target %d will complete\n",
+	 HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
+  BusLogic_Delay(2);
+  /*
+    If this CCB is still Active and still refers to the same Command, then
+    actually aborting this Command is necessary.
+  */
+  BusLogic_LockHostAdapter(HostAdapter);
+  Result = SCSI_ABORT_NOT_RUNNING;
+  if (CCB->Status == BusLogic_CCB_Active &&
+      CCB->Command == Command && Command->pid == CommandPID)
+    {
+      /*
+	Attempt to abort this CCB.
+      */
+      if (BusLogic_WriteOutgoingMailbox(HostAdapter,
+					BusLogic_MailboxAbortCommand, CCB))
+	{
+	  printk("scsi%d: Aborting CCB #%d to Target %d\n",
+		 HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
+	  Result = SCSI_ABORT_PENDING;
+	}
+      else
+	{
+	  printk("scsi%d: Unable to Abort CCB #%d to Target %d - "
+		 "No Outgoing Mailboxes\n", HostAdapter->HostNumber,
+		 CCB->SerialNumber, CCB->TargetID);
+	  Result = SCSI_ABORT_BUSY;
+	}
+    }
+  else printk("scsi%d: CCB #%d to Target %d completed\n",
+	      HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
   BusLogic_UnlockHostAdapter(HostAdapter);
   return Result;
 }
@@ -2165,23 +2305,50 @@
   /*
     Mark all currently executing CCBs as having been reset.
   */
+  BusLogic_LockHostAdapter(HostAdapter);
   for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
     if (CCB->Status == BusLogic_CCB_Active)
       {
-	SCSI_Command_T *ActiveCommand = CCB->Command;
-	if (ActiveCommand == Command) Command = NULL;
-	BusLogic_DeallocateCCB(CCB);
-	if (ActiveCommand != NULL)
+	CCB->Status = BusLogic_CCB_Reset;
+	if (CCB->Command == Command)
 	  {
-	    ActiveCommand->result = DID_RESET << 16;
-	    ActiveCommand->scsi_done(ActiveCommand);
+	    CCB->Command = NULL;
+	    /*
+	      Disable Tagged Queuing if it was active for this Target Device.
+	    */
+	    if (((HostAdapter->HostWideSCSI && CCB->WideModeTagEnable) ||
+		 (!HostAdapter->HostWideSCSI && CCB->TagEnable)) &&
+		(HostAdapter->TaggedQueuingPermitted & (1 << CCB->TargetID)))
+	      {
+		HostAdapter->TaggedQueuingPermitted &= ~(1 << CCB->TargetID);
+		printk("scsi%d: Tagged Queuing now disabled for Target %d\n",
+		       HostAdapter->HostNumber, CCB->TargetID);
+	      }
 	  }
       }
+  BusLogic_UnlockHostAdapter(HostAdapter);
+  /*
+    Perform completion processing for the Command being Reset.
+  */
   if (Command != NULL)
     {
       Command->result = DID_RESET << 16;
       Command->scsi_done(Command);
     }
+  /*
+    Perform completion processing for any other active CCBs.
+  */
+  for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+    if (CCB->Status == BusLogic_CCB_Reset)
+      {
+	Command = CCB->Command;
+	BusLogic_DeallocateCCB(CCB);
+	if (Command != NULL)
+	  {
+	    Command->result = DID_RESET << 16;
+	    Command->scsi_done(Command);
+	  }
+      }
   return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
 }
 
@@ -2210,16 +2377,27 @@
   /*
     If there is a currently executing CCB in the Host Adapter for this Command,
     then an Incoming Mailbox entry will be made with a completion code of
-    BusLogic_HostAdapterAssertedBusDeviceReset.  Otherwise, the CCB Command
+    BusLogic_HostAdapterAssertedBusDeviceReset.  Otherwise, the CCB's Command
     field will be left pointing to the Command so that the interrupt for the
     completion of the Bus Device Reset can call the Completion Routine for the
     Command.
   */
   BusLogic_LockHostAdapter(HostAdapter);
   for (XCCB = HostAdapter->All_CCBs; XCCB != NULL; XCCB = XCCB->NextAll)
-    if (XCCB->Status == BusLogic_CCB_Active && XCCB->Command == Command)
+    if (XCCB->Command == Command && XCCB->Status == BusLogic_CCB_Active)
       {
 	CCB->Command = NULL;
+	/*
+	  Disable Tagged Queuing if it was active for this Target Device.
+	*/
+	if (((HostAdapter->HostWideSCSI && XCCB->WideModeTagEnable) ||
+	    (!HostAdapter->HostWideSCSI && XCCB->TagEnable)) &&
+	    (HostAdapter->TaggedQueuingPermitted & (1 << TargetID)))
+	  {
+	    HostAdapter->TaggedQueuingPermitted &= ~(1 << TargetID);
+	    printk("scsi%d: Tagged Queuing now disabled for Target %d\n",
+		   HostAdapter->HostNumber, TargetID);
+	  }
 	break;
       }
   BusLogic_UnlockHostAdapter(HostAdapter);
@@ -2228,15 +2406,16 @@
     If sending a Bus Device Reset is impossible, attempt a full Host
     Adapter Hard Reset and SCSI Bus Reset.
   */
-  if (!(BusLogic_WriteOutgoingMailboxEntry(
-	  HostAdapter, BusLogic_MailboxStartCommand, CCB)))
+  if (!(BusLogic_WriteOutgoingMailbox(HostAdapter,
+				      BusLogic_MailboxStartCommand, CCB)))
     {
-      printk("scsi%d: cannot write Outgoing Mailbox Entry for "
-	     "Bus Device Reset\n", HostAdapter->HostNumber);
+      printk("scsi%d: cannot write Outgoing Mailbox for Bus Device Reset\n",
+	     HostAdapter->HostNumber);
       BusLogic_DeallocateCCB(CCB);
       return BusLogic_ResetHostAdapter(HostAdapter, Command);
     }
   HostAdapter->ReadWriteOperationCount[TargetID] = 0;
+  HostAdapter->QueuedOperationCount[TargetID] = 0;
   return SCSI_RESET_PENDING;
 }
 
@@ -2268,7 +2447,8 @@
 	}
       else return BusLogic_ResetHostAdapter(HostAdapter, Command);
     }
-  printk("scsi%d: Error Recovery Suppressed\n", HostAdapter->HostNumber);
+  printk("scsi%d: Error Recovery for Target %d Suppressed\n",
+	 HostAdapter->HostNumber, TargetID);
   return SCSI_RESET_PUNT;
 }
 
@@ -2345,9 +2525,13 @@
   a SCSI Bus Reset and issuing any SCSI commands.  If unspecified, it defaults
   to 0 which means to use the value of BusLogic_DefaultBusSettleTime.
 
-  The fourth integer specified is the Tracing Options.  If unspecified, it
-  defaults to 0 which means that no special tracing information is to be
-  printed.  Note that Tracing Options are applied across all Host Adapters.
+  The fourth integer specified is the Local Options.  If unspecified, it
+  defaults to 0.  Note that Local Options are only applied to a specific Host
+  Adapter.
+
+  The fifth integer specified is the Global Options.  If unspecified, it
+  defaults to 0.  Note that Global Options are applied across all Host
+  Adapters.
 
   The string options are used to provide control over Tagged Queuing and Error
   Recovery. If both Tagged Queuing and Error Recovery strings are provided, the
@@ -2428,12 +2612,13 @@
   CommandLineEntry->IO_Address = 0;
   CommandLineEntry->Concurrency = 0;
   CommandLineEntry->BusSettleTime = 0;
+  CommandLineEntry->LocalOptions = 0;
   CommandLineEntry->TaggedQueuingPermitted = 0;
   CommandLineEntry->TaggedQueuingPermittedMask = 0;
   memset(CommandLineEntry->ErrorRecoveryOption,
 	 BusLogic_ErrorRecoveryDefault,
 	 sizeof(CommandLineEntry->ErrorRecoveryOption));
-  if (IntegerCount > 4)
+  if (IntegerCount > 5)
     printk("BusLogic: Unexpected Command Line Integers ignored\n");
   if (IntegerCount >= 1)
     {
@@ -2454,7 +2639,8 @@
 		       "(duplicate I/O Address 0x%X)\n", IO_Address);
 		return;
 	      }
-	    else if (IO_Address == BusLogic_IO_StandardAddresses[i]) break;
+	    else if (IO_Address >= 0x1000 ||
+		     IO_Address == BusLogic_IO_StandardAddresses[i]) break;
 	  BusLogic_IO_AddressProbeList[ProbeListIndex++] = IO_Address;
 	  BusLogic_IO_AddressProbeList[ProbeListIndex] = 0;
 	}
@@ -2474,7 +2660,9 @@
   if (IntegerCount >= 3)
     CommandLineEntry->BusSettleTime = Integers[3];
   if (IntegerCount >= 4)
-    BusLogic_TracingOptions |= Integers[4];
+    CommandLineEntry->LocalOptions = Integers[4];
+  if (IntegerCount >= 5)
+    BusLogic_GlobalOptions |= Integers[5];
   if (!(BusLogic_CommandLineEntryCount == 0 || ProbeListIndex == 0 ||
 	BusLogic_CommandLineEntryCount == ProbeListIndex))
     {
@@ -2575,3 +2763,17 @@
   if (*Strings != '\0')
     printk("BusLogic: Unexpected Command Line String '%s' ignored\n", Strings);
 }
+
+
+/*
+  Include Module support if requested.
+*/
+
+
+#ifdef MODULE
+
+SCSI_Host_Template_T driver_template = BUSLOGIC;
+
+#include "scsi_module.c"
+
+#endif
diff -u --recursive --new-file v1.3.53/linux/drivers/scsi/BusLogic.h linux/drivers/scsi/BusLogic.h
--- v1.3.53/linux/drivers/scsi/BusLogic.h	Fri Nov 17 08:42:26 1995
+++ linux/drivers/scsi/BusLogic.h	Mon Jan  1 09:26:10 1996
@@ -1,6 +1,6 @@
 /*
 
-  Linux Driver for BusLogic SCSI Host Adapters
+  Linux Driver for BusLogic MultiMaster SCSI Host Adapters
 
   Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>
 
@@ -95,13 +95,11 @@
 
 /*
   Define the number of Incoming and Outgoing Mailboxes used by this driver.
-  Since BusLogic Host Adapters have room to buffer 32 commands internally,
-  there is significant virtue in setting BusLogic_MailboxCount to 32 or above.
   The maximum possible value is 255, since the MailboxCount parameter to the
   Initialize Extended Mailbox command is limited to a single byte.
 */
 
-#define BusLogic_MailboxCount			32
+#define BusLogic_MailboxCount			64
 
 
 /*
@@ -124,11 +122,12 @@
 
 /*
   Define the default number of Concurrent Commands per Logical Unit to allow
-  for Target Devices on non-ISA and ISA Host Adapters.
+  for Target Devices depending on whether or not ISA bounce buffers are
+  required.
 */
 
 #define BusLogic_Concurrency			7
-#define BusLogic_Concurrency_ISA		1
+#define BusLogic_Concurrency_BB			1
 
 
 /*
@@ -142,7 +141,14 @@
 
 
 /*
-  Define the possible Tracing Options.
+  Define the possible Local Options.
+*/
+
+#define BusLogic_InhibitTargetInquiry		1
+
+
+/*
+  Define the possible Global Options.
 */
 
 #define BusLogic_TraceProbe			1
@@ -267,7 +273,7 @@
   BusLogic_InitializeExtendedMailbox =		0x81,	/* documented */
   BusLogic_InquireFirmwareVersion3rdDigit =	0x84,	/* undocumented */
   BusLogic_InquireFirmwareVersionLetter =	0x85,	/* undocumented */
-  BusLogic_InquireBoardModelAndRevision =	0x8B,	/* undocumented */
+  BusLogic_InquireBoardModelNumber	 =	0x8B,	/* undocumented */
   BusLogic_InquireSynchronousPeriod =		0x8C,	/* undocumented */
   BusLogic_InquireExtendedSetupInformation =	0x8D,	/* documented */
   BusLogic_EnableStrictRoundRobinMode =		0x8F,	/* documented */
@@ -395,15 +401,10 @@
 
 
 /*
-  Define the Inquire Board Model and Revision reply structure.
+  Define the Inquire Board Model Number reply type.
 */
 
-typedef struct BusLogic_ModelAndRevision
-{
-  unsigned char Model[5];
-  unsigned char Revision;
-}
-BusLogic_ModelAndRevision_T;
+typedef unsigned char BusLogic_BoardModelNumber_T[5];
 
 
 /*
@@ -427,7 +428,7 @@
   unsigned char MailboxCount;				/* Byte 4 */
   void *BaseMailboxAddress __attribute__ ((packed));	/* Bytes 5-8 */
   struct { unsigned char :6;				/* Byte 9 */
-	   boolean LevelTriggeredInterrupts:1;
+	   boolean LevelSensitiveInterrupts:1;
 	   unsigned char :1; } Misc;
   unsigned char FirmwareRevision[3];			/* Bytes 10-12 */
   boolean HostWideSCSI:1;				/* Byte 13 Bit 0 */
@@ -479,8 +480,8 @@
 
 /*
   Define the Requested Reply Length type used by the Inquire Setup Information,
-  Inquire Board Model and Revision, Inquire Synchronous Period, and Inquire
-  Extended Setup Information commands.
+  Inquire Board Model Number, Inquire Synchronous Period, and Inquire Extended
+  Setup Information commands.
 */
 
 typedef unsigned char BusLogic_RequestedReplyLength_T;
@@ -676,7 +677,7 @@
   BusLogic_HostAdapterStatus_T HostAdapterStatus:8;	/* Byte 14 */
   BusLogic_TargetDeviceStatus_T TargetDeviceStatus:8;	/* Byte 15 */
   unsigned char TargetID;				/* Byte 16 */
-  unsigned char LogicalUnit:5;				/* Byte 17 Bits 0-2 */
+  unsigned char LogicalUnit:5;				/* Byte 17 Bits 0-4 */
   boolean TagEnable:1;					/* Byte 17 Bit 5 */
   BusLogic_QueueTag_T QueueTag:2;			/* Byte 17 Bits 6-7 */
   SCSI_CDB_T CDB;					/* Bytes 18-29 */
@@ -691,7 +692,8 @@
   SCSI_Command_T *Command;
   enum { BusLogic_CCB_Free =	    0,
 	 BusLogic_CCB_Active =	    1,
-	 BusLogic_CCB_Completed =   2 } Status;
+	 BusLogic_CCB_Completed =   2,
+	 BusLogic_CCB_Reset =	    3 } Status;
   BusLogic_CompletionCode_T MailboxCompletionCode;
   unsigned int SerialNumber;
   struct BusLogic_CCB *Next;
@@ -759,6 +761,7 @@
   unsigned short IO_Address;
   unsigned short Concurrency;
   unsigned short BusSettleTime;
+  unsigned short LocalOptions;
   unsigned short TaggedQueuingPermitted;
   unsigned short TaggedQueuingPermittedMask;
   unsigned char ErrorRecoveryOption[BusLogic_MaxTargetIDs];
@@ -774,10 +777,10 @@
 {
   SCSI_Host_T *SCSI_Host;
   unsigned char HostNumber;
-  unsigned char ModelName[6];
+  unsigned char ModelName[9];
   unsigned char FirmwareVersion[6];
-  unsigned char BoardName[15];
-  unsigned char InterruptLabel[63];
+  unsigned char BoardName[18];
+  unsigned char InterruptLabel[62];
   unsigned short IO_Address;
   unsigned char IRQ_Channel;
   unsigned char DMA_Channel;
@@ -788,10 +791,11 @@
   boolean SynchronousInitiation:1;
   boolean ParityChecking:1;
   boolean ExtendedTranslation:1;
-  boolean LevelTriggeredInterrupts:1;
+  boolean LevelSensitiveInterrupts:1;
   boolean HostWideSCSI:1;
   boolean HostDifferentialSCSI:1;
   boolean HostAdapterResetPending:1;
+  boolean BounceBuffersRequired:1;
   volatile boolean HostAdapterCommandCompleted:1;
   unsigned short HostAdapterScatterGatherLimit;
   unsigned short DriverScatterGatherLimit;
@@ -799,6 +803,7 @@
   unsigned short MaxLogicalUnits;
   unsigned short Concurrency;
   unsigned short BusSettleTime;
+  unsigned short LocalOptions;
   unsigned short DisconnectPermitted;
   unsigned short TaggedQueuingPermitted;
   unsigned long BIOS_Address;
@@ -813,6 +818,8 @@
   unsigned char ErrorRecoveryOption[BusLogic_MaxTargetIDs];
   unsigned char CommandSuccessfulFlag[BusLogic_MaxTargetIDs];
   unsigned long ReadWriteOperationCount[BusLogic_MaxTargetIDs];
+  unsigned char QueuedOperationCount[BusLogic_MaxTargetIDs];
+  unsigned long LastSequencePoint[BusLogic_MaxTargetIDs];
   BusLogic_OutgoingMailbox_T *FirstOutgoingMailbox;
   BusLogic_OutgoingMailbox_T *LastOutgoingMailbox;
   BusLogic_OutgoingMailbox_T *NextOutgoingMailbox;
@@ -943,14 +950,17 @@
 
 
 /*
-  BusLogic_Delay waits for Seconds to elapse.  It must be called with
-  interrupts enabled so that jiffies is updated.
+  BusLogic_Delay waits for Seconds to elapse.
 */
 
 static inline void BusLogic_Delay(int Seconds)
 {
   unsigned long TimeoutJiffies = jiffies + Seconds * HZ;
+  unsigned long ProcessorFlags;
+  save_flags(ProcessorFlags);
+  sti();
   while (jiffies < TimeoutJiffies) ;
+  restore_flags(ProcessorFlags);
 }
 
 
diff -u --recursive --new-file v1.3.53/linux/drivers/scsi/ChangeLog linux/drivers/scsi/ChangeLog
--- v1.3.53/linux/drivers/scsi/ChangeLog	Fri Nov 17 08:42:26 1995
+++ linux/drivers/scsi/ChangeLog	Thu Jan  4 07:35:47 1996
@@ -1,4 +1,8 @@
-Fri Nov 10 15:29:49 1995  Leonard N. Zubkoff (lnz@dandelion.com)
+Sun Dec 31 23:26:00 1995  Leonard N. Zubkoff <lnz@dandelion.com>
+
+	* BusLogic Driver Version 1.3.1 Released.
+
+Fri Nov 10 15:29:49 1995  Leonard N. Zubkoff <lnz@dandelion.com>
 
 	* Released new BusLogic driver.
 
diff -u --recursive --new-file v1.3.53/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v1.3.53/linux/drivers/scsi/Config.in	Sun Dec 17 11:43:18 1995
+++ linux/drivers/scsi/Config.in	Thu Jan  4 07:35:47 1996
@@ -18,7 +18,7 @@
 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
-bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC
+dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC
 dep_tristate 'EATA-DMA (DPT, NEC, ATT, Olivetti) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI
 dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI
 dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI
diff -u --recursive --new-file v1.3.53/linux/drivers/scsi/README.BusLogic linux/drivers/scsi/README.BusLogic
--- v1.3.53/linux/drivers/scsi/README.BusLogic	Fri Nov 17 08:42:26 1995
+++ linux/drivers/scsi/README.BusLogic	Mon Jan  1 09:26:10 1996
@@ -1,5 +1,5 @@
-		     BusLogic SCSI Driver for Linux 1.3.41
-		       Version 1.3.0 ~ 13 November 1995
+	       BusLogic MultiMaster SCSI Driver for Linux 1.3.51
+		       Version 1.3.1 ~ 31 December 1995
 
 			       Leonard N. Zubkoff
 			       Dandelion Digital
@@ -15,7 +15,7 @@
 of bus architectures by virtue of their MultiMaster ASIC technology.  This
 driver supports all present BusLogic MultiMaster Host Adapters, and should
 support any future MultiMaster designs with little or no modification.  Host
-adapters based on the new FlashPoint technology are not supported by this
+adapters based on the new FlashPoint architecture are not supported by this
 driver.
 
 My primary goals in writing this completely new BusLogic driver for Linux are
@@ -86,9 +86,10 @@
   number of concurrent commands per logical unit is available from the kernel
   command line.  In addition, tagged queuing is automatically disabled whenever
   the host adapter firmware version is known not to implement it correctly, or
-  whenever a concurrency value of 1 is selected.  In performance testing,
-  sustained disk writes of 7.3MB per second have been observed to a /dev/sd
-  device.
+  whenever a concurrency value of 1 is selected.  Tagged queuing is also
+  disabled for individual target devices if disconnect/reconnect is disabled
+  for that device.  In performance testing, sustained disk writes of 7.3MB per
+  second have been observed to a /dev/sd device.
 
 o Robustness Features
 
@@ -106,7 +107,9 @@
   correct operation, the next command that is reset will force a full host
   adapter hard reset and SCSI bus reset.  SCSI bus resets caused by other
   devices and detected by the host adapter are also handled by issuing a hard
-  reset to the host adapter and full reinitialization.  These error recovery
+  reset to the host adapter and full reinitialization.  Finally, if a command
+  using tagged queuing causes a bus device reset or SCSI bus reset, then tagged
+  queuing will be disabled for that target device.  These error recovery
   options should improve overall system robustness by preventing individual
   errant devices from causing the system as a whole to lock up or crash, and
   thereby allowing a clean shutdown and restart after the offending component
@@ -126,7 +129,9 @@
   On PCI systems running kernels compiled with PCI BIOS support enabled, this
   driver will interrogate the PCI configuration space and use the I/O port
   addresses assigned by the system BIOS, rather than the ISA compatible I/O
-  port addresses.
+  port addresses.  The ISA compatible I/O port address is then disabled by the
+  driver.  On PCI systems it is also recommended that the AutoSCSI utility be
+  used to disable the ISA compatible I/O port entirely as it is not necessary.
 
 o Shared Interrupts Support
 
@@ -156,34 +161,34 @@
 
 "C" Series Host Adapters:
 
-946C	PCI	Fast Single-ended SCSI-2
-956C	PCI	Fast/Wide Single-ended SCSI-2
-956CD	PCI	Fast/Wide Differential SCSI-2
-445C	VLB	Fast Single-ended SCSI-2
-747C	EISA	Fast Single-ended SCSI-2
-757C	EISA	Fast/Wide Single-ended SCSI-2
-757CD	EISA	Fast/Wide Differential SCSI-2
-545C	ISA	Fast Single-ended SCSI-2
-540CF	ISA	Fast Single-ended SCSI-2
+BT-946C	    PCI	    Fast Single-ended SCSI-2
+BT-956C	    PCI	    Fast/Wide Single-ended SCSI-2
+BT-956CD    PCI	    Fast/Wide Differential SCSI-2
+BT-445C	    VLB	    Fast Single-ended SCSI-2
+BT-747C	    EISA    Fast Single-ended SCSI-2
+BT-757C	    EISA    Fast/Wide Single-ended SCSI-2
+BT-757CD    EISA    Fast/Wide Differential SCSI-2
+BT-545C	    ISA	    Fast Single-ended SCSI-2
+BT-540CF    ISA	    Fast Single-ended SCSI-2
 
 "S" Series Host Adapters:
 
-445S	VLB	Fast Single-ended SCSI-2
-747S	EISA	Fast Single-ended SCSI-2
-747D	EISA	Fast Differential SCSI-2
-757S	EISA	Fast/Wide Single-ended SCSI-2
-757D	EISA	Fast/Wide Differential SCSI-2
-545S	ISA	Fast Single-ended SCSI-2
-542D	ISA	Fast Differential SCSI-2
-742A 	EISA	Single-ended SCSI-2 (742A revision H)
-542B	ISA	Single-ended SCSI-2 (542B revision H)
+BT-445S	    VLB	    Fast Single-ended SCSI-2
+BT-747S	    EISA    Fast Single-ended SCSI-2
+BT-747D	    EISA    Fast Differential SCSI-2
+BT-757S	    EISA    Fast/Wide Single-ended SCSI-2
+BT-757D	    EISA    Fast/Wide Differential SCSI-2
+BT-545S	    ISA	    Fast Single-ended SCSI-2
+BT-542D	    ISA	    Fast Differential SCSI-2
+BT-742A	    EISA    Single-ended SCSI-2 (742A revision H)
+BT-542B	    ISA	    Single-ended SCSI-2 (542B revision H)
 
 "A" Series Host Adapters:
 
-742A 	EISA	Single-ended SCSI-2 (742A revisions A - G)
-542B	ISA	Single-ended SCSI-2 (542B revisions A - G)
+BT-742A	    EISA    Single-ended SCSI-2 (742A revisions A - G)
+BT-542B	    ISA	    Single-ended SCSI-2 (542B revisions A - G)
 
-The FlashPoint LT, also known as the 930 Ultra, implements a different host
+The FlashPoint LT, also known as the BT-930 Ultra, implements a different host
 interface and is not supported by this driver.
 
 AMI FastDisk Host Adapters are true BusLogic clones and are supported by this
@@ -235,18 +240,16 @@
 
 				 INSTALLATION
 
-This distribution was prepared for Linux kernel version 1.3.41.  Installation
+This distribution was prepared for Linux kernel version 1.3.51.  Installation
 in later versions will probably be successful as well, though BusLogic.patch
-may not be required once this driver becomes part of the standard development
-kernel; installation in earlier versions should not be attempted as 1.3.41
-contains changes I made to the common code that are essential for correct error
-recovery.
+may not be required.  Installation in Linux 1.3.41 - 1.3.50 will probably be
+successful, but may require minor modifications.
 
 To install the BusLogic SCSI driver, you may use the following commands,
 replacing "/usr/src" with wherever you keep your Linux kernel source tree:
 
   cd /usr/src
-  tar -xvzf BusLogic-1.3.0.tar.gz
+  tar -xvzf BusLogic-1.3.1.tar.gz
   mv README.BusLogic BusLogic.[ch] linux/drivers/scsi
   patch -p < BusLogic.patch
   cd linux
diff -u --recursive --new-file v1.3.53/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v1.3.53/linux/drivers/scsi/hosts.c	Mon Dec 11 15:42:03 1995
+++ linux/drivers/scsi/hosts.c	Thu Jan  4 07:50:59 1996
@@ -290,8 +290,6 @@
 
     retval->unique_id = 0;
     retval->io_port = 0;
-    retval->forbidden_addr = 0;
-    retval->forbidden_size = 0;
     retval->hostt = tpnt;
     retval->next = NULL;
 #ifdef DEBUG
@@ -402,28 +400,6 @@
     max_scsi_hosts = next_scsi_host;
 #endif
     return 0;
-}
-
-
-void scsi_mem_init(unsigned long memory_end)
-{
-    struct Scsi_Host *Host;
-    long High8, Low24;
-    for (Host = scsi_hostlist; Host != NULL; Host = Host->next) {
-	if (Host->forbidden_addr > 0 && Host->forbidden_size > 0) {
-	    for (High8 = 1<<24; High8 < memory_end; High8 += 1<<24) {
-		for (Low24 = Host->forbidden_addr;
-		     Low24 < Host->forbidden_addr + Host->forbidden_size;
-		     Low24 += PAGE_SIZE) {
-		    unsigned long ForbiddenAddress = High8 + Low24;
-		    if (ForbiddenAddress >= memory_end) goto next_host;
-		    mem_map[MAP_NR(ForbiddenAddress)].reserved = 1;
-		}
-	    }
-	}
-    next_host:
-	continue;
-    }
 }
 
 /*
diff -u --recursive --new-file v1.3.53/linux/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h
--- v1.3.53/linux/drivers/scsi/hosts.h	Wed Sep 27 15:59:59 1995
+++ linux/drivers/scsi/hosts.h	Thu Jan  4 14:21:38 1996
@@ -282,14 +282,6 @@
      * initialized to 0 in scsi_register.
      */
     unsigned int unique_id;
-
-    /*
-     * Set these if there are conflicts between memory
-     * in the < 1mb region and regions at 16mb multiples.
-     * The address must be on a page boundary.
-     */
-    unsigned long forbidden_addr;
-    unsigned long forbidden_size;
     
     /*
      * The rest can be copied from the template, or specifically
@@ -313,7 +305,7 @@
      */
     unsigned suggest_bus_reset:1;
  
-    int hostdata[0];  /* Used for storage of host specific stuff */
+    unsigned long hostdata[0];  /* Used for storage of host specific stuff */
 };
 
 extern struct Scsi_Host * scsi_hostlist;
diff -u --recursive --new-file v1.3.53/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v1.3.53/linux/drivers/scsi/scsi.c	Tue Jan  2 16:46:27 1996
+++ linux/drivers/scsi/scsi.c	Thu Jan  4 07:43:41 1996
@@ -802,8 +802,8 @@
 	if (!scsi_abort (SCpnt, DID_TIME_OUT, pid))
 	    return;
     case IN_ABORT:
-	printk("SCSI host %d abort() timed out - resetting\n",
-	       SCpnt->host->host_no);
+	printk("SCSI host %d abort (pid %ld) timed out - resetting\n",
+	       SCpnt->host->host_no, SCpnt->pid);
 	if (!scsi_reset (SCpnt, FALSE))
 	    return;
     case IN_RESET:
diff -u --recursive --new-file v1.3.53/linux/drivers/scsi/ultrastor.c linux/drivers/scsi/ultrastor.c
--- v1.3.53/linux/drivers/scsi/ultrastor.c	Tue Jan  2 16:46:27 1996
+++ linux/drivers/scsi/ultrastor.c	Thu Jan  4 07:43:40 1996
@@ -63,7 +63,7 @@
  * the 24F, cause the SCSI bus to do odd things and generally lead to
  * kernel panics and machine hangs.  This is like the Adaptec code.
  *
- * Check I/O ports 14f, and 34f before allocating them to avoid conflicts.
+ * Use check/snarf_region for 14f, 34f to avoid I/O space address conflicts.
  */
 
 /* Changes from version 1.8 to version 1.9
diff -u --recursive --new-file v1.3.53/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.53/linux/fs/buffer.c	Sat Dec 30 15:50:54 1995
+++ linux/fs/buffer.c	Thu Jan  4 20:26:24 1996
@@ -25,7 +25,9 @@
 #include <linux/locks.h>
 #include <linux/errno.h>
 #include <linux/malloc.h>
+#include <linux/pagemap.h>
 #include <linux/swapctl.h>
+#include <linux/smp.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -278,6 +280,7 @@
 			if (bh->b_count)
 				continue;
 			bh->b_flushtime = 0;
+			clear_bit(BH_Protected, &bh->b_state);
 			clear_bit(BH_Uptodate, &bh->b_state);
 			clear_bit(BH_Dirty, &bh->b_state);
 			clear_bit(BH_Req, &bh->b_state);
@@ -837,6 +840,7 @@
 {
 	wait_on_buffer(buf);
 	mark_buffer_clean(buf);
+	clear_bit(BH_Protected, &buf->b_state);
 	buf->b_count--;
 	remove_from_hash_queue(buf);
 	buf->b_dev = NODEV;
@@ -1023,7 +1027,7 @@
 	} while (nrbuf > 0);
 }
 
-int bread_page(unsigned long address, kdev_t dev, int b[], int size)
+static int bread_page(unsigned long address, kdev_t dev, int b[], int size)
 {
 	struct buffer_head *bh, *next, *arr[MAX_BUF_PER_PAGE];
 	int block, nr;
@@ -1072,6 +1076,34 @@
 	return 0;
 }
 
+/*
+ * Generic "readpage" function for block devices that have the
+ * normal bmap functionality. This is most of the block device
+ * filesystems.
+ */
+int generic_readpage(struct inode * inode, struct page * page)
+{
+	unsigned long block;
+	int *p, nr[PAGE_SIZE/512];
+	int i;
+
+	i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
+	block = page->offset >> inode->i_sb->s_blocksize_bits;
+	p = nr;
+	do {
+		*p = inode->i_op->bmap(inode, block);
+		i--;
+		block++;
+		p++;
+	} while (i > 0);
+
+	/* We should make this asynchronous, but this is good enough for now.. */
+	bread_page(page_address(page), inode->i_dev, nr, inode->i_sb->s_blocksize);
+	page->uptodate = 1;
+	wake_up(&page->wait);
+	return 0;
+}
+
 #if 0
 /*
  * bwrite_page writes a page out to the buffer cache and/or the physical device.
@@ -1781,22 +1813,35 @@
  * the syscall above, but now we launch it ourselves internally with
  * kernel_thread(...)  directly after the first thread in init/main.c */
 
-int bdflush(void * unused) {
-	
+int bdflush(void * unused) 
+{
 	int i;
 	int ndirty;
 	int nlist;
 	int ncount;
 	struct buffer_head * bh, *next;
 
-	/* We have a bare-bones task_struct, and really should fill
-	in a few more things so "top" and /proc/2/{exe,root,cwd}
-	display semi-sane things. Not real crucial though...  */
+	/*
+	 *	We have a bare-bones task_struct, and really should fill
+	 *	in a few more things so "top" and /proc/2/{exe,root,cwd}
+	 *	display semi-sane things. Not real crucial though...  
+	 */
 
 	current->session = 1;
 	current->pgrp = 1;
 	sprintf(current->comm, "kernel bdflush");
 
+	/*
+	 *	As a kernel thread we want to tamper with system buffers
+	 *	and other internals and thus be subject to the SMP locking
+	 *	rules. (On a uniprocessor box this does nothing).
+	 */
+	 
+#ifdef __SMP__
+	lock_kernel();
+	syscall_count++;
+#endif
+		 
 	for (;;) {
 #ifdef DEBUG
 		printk("bdflush() activated...");
diff -u --recursive --new-file v1.3.53/linux/fs/ext/file.c linux/fs/ext/file.c
--- v1.3.53/linux/fs/ext/file.c	Tue Dec 26 04:45:37 1995
+++ linux/fs/ext/file.c	Thu Jan  4 20:26:24 1996
@@ -64,7 +64,7 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
-	NULL,			/* readpage */
+	generic_readpage,	/* readpage */
 	NULL,			/* writepage */
 	ext_bmap,		/* bmap */
 	ext_truncate,		/* truncate */
diff -u --recursive --new-file v1.3.53/linux/fs/ext2/balloc.c linux/fs/ext2/balloc.c
--- v1.3.53/linux/fs/ext2/balloc.c	Mon Dec 11 15:42:04 1995
+++ linux/fs/ext2/balloc.c	Thu Jan  4 14:14:20 1996
@@ -227,7 +227,7 @@
 				      block);
 		else {
 			if (sb->dq_op)
-				sb->dq_op->free_block(inode, fs_to_dq_blocks(1, inode->i_blksize));
+				sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize));
 			gdp->bg_free_blocks_count++;
 			es->s_free_blocks_count++;
 		}
@@ -404,7 +404,7 @@
 	 * Check quota for allocation of this block.
 	 */
 	if (sb->dq_op)
-		if (sb->dq_op->alloc_block (inode, fs_to_dq_blocks(1, inode->i_blksize))) {
+		if (sb->dq_op->alloc_block (inode, fs_to_dq_blocks(1, sb->s_blocksize))) {
 			unlock_super (sb);
 			*err = -EDQUOT;
 			return 0;
@@ -424,7 +424,7 @@
 		ext2_warning (sb, "ext2_new_block",
 			      "bit already set for block %d", j);
 		if (sb->dq_op)
-			sb->dq_op->free_block(inode, fs_to_dq_blocks(1, inode->i_blksize));
+			sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize));
 		goto repeat;
 	}
 
@@ -440,11 +440,11 @@
 		for (k = 1;
 		     k < 8 && (j + k) < EXT2_BLOCKS_PER_GROUP(sb); k++) {
 			if (sb->dq_op)
-				if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(1, inode->i_blksize)))
+				if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(1, sb->s_blocksize)))
 					break;
 			if (set_bit (j + k, bh->b_data)) {
 				if (sb->dq_op)
-					sb->dq_op->free_block(inode, fs_to_dq_blocks(1, inode->i_blksize));
+					sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize));
 				break;
 			}
 			(*prealloc_count)++;
diff -u --recursive --new-file v1.3.53/linux/fs/ext2/file.c linux/fs/ext2/file.c
--- v1.3.53/linux/fs/ext2/file.c	Tue Jan  2 16:46:27 1996
+++ linux/fs/ext2/file.c	Thu Jan  4 20:26:24 1996
@@ -36,8 +36,6 @@
 #include <linux/fs.h>
 #include <linux/ext2_fs.h>
 
-static int ext2_readpage(struct inode *, unsigned long, char *);
-static int ext2_file_read (struct inode *, struct file *, char *, int);
 static int ext2_file_write (struct inode *, struct file *, const char *, int);
 static void ext2_release_file (struct inode *, struct file *);
 
@@ -47,7 +45,7 @@
  */
 static struct file_operations ext2_file_operations = {
 	NULL,			/* lseek - default */
-	ext2_file_read,		/* read */
+	generic_file_read,	/* read */
 	ext2_file_write,	/* write */
 	NULL,			/* readdir - bad */
 	NULL,			/* select - default */
@@ -74,127 +72,13 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
-	ext2_readpage,		/* readpage */
+	generic_readpage,	/* readpage */
 	NULL,			/* writepage */
 	ext2_bmap,		/* bmap */
 	ext2_truncate,		/* truncate */
 	ext2_permission,	/* permission */
 	NULL			/* smap */
 };
-
-static int ext2_readpage(struct inode * inode, unsigned long offset, char * page)
-{
-	int *p, nr[PAGE_SIZE/512];
-	int i;
-
-	i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
-	offset >>= inode->i_sb->s_blocksize_bits;
-	p = nr;
-	do {
-		*p = ext2_bmap(inode, offset);
-		i--;
-		offset++;
-		p++;
-	} while (i > 0);
-	return bread_page((unsigned long) page, inode->i_dev, nr, inode->i_sb->s_blocksize);
-}
-
-/*
- * This is a generic file read routine, and uses the
- * inode->i_op->readpage() function for the actual low-level
- * stuff. We can put this into the other filesystems too
- * once we've debugged it a bit more.
- */
-static int ext2_file_read (struct inode * inode, struct file * filp,   
-		char * buf, int count)
-{
-	int read = 0;
-	unsigned long pos;
-	unsigned long addr;
-	unsigned long cached_page = 0;
-	struct page *page;
-
-	if (count <= 0)
-		return 0;
-
-	pos = filp->f_pos;
-	
-	for (;;) {
-		unsigned long offset, nr;
-
-		if (pos >= inode->i_size)
-			break;
-
-		offset = pos & ~PAGE_MASK;
-		nr = PAGE_SIZE - offset;
-		if (nr > count)
-			nr = count;
-
-		/* is it already cached? */
-		page = find_page(inode, pos & PAGE_MASK);
-		if (page)
-			goto found_page;
-
-		/* not cached, have to read it in.. */
-		if (!(addr = cached_page)) {
-			addr = cached_page = __get_free_page(GFP_KERNEL);
-			if (!addr) {
-				if (!read)
-					read = -ENOMEM;
-				break;
-			}
-		}
-		inode->i_op->readpage(inode, pos & PAGE_MASK, (char *) addr);
-
-		/* while we did that, things may have changed.. */
-		if (pos >= inode->i_size)
-			break;
-		page = find_page(inode, pos & PAGE_MASK);
-		if (page)
-			goto found_page;
-
-		/* nope, this is the only copy.. */
-		cached_page = 0;
-		page = mem_map + MAP_NR(addr);
-		page->offset = pos & PAGE_MASK;
-		add_page_to_inode_queue(inode, page);
-		add_page_to_hash_queue(inode, page);
-
-found_page:
-		if (nr > inode->i_size - pos)
-			nr = inode->i_size - pos;
-		page->count++;
-		addr = page_address(page);
-		memcpy_tofs(buf, (void *) (addr + offset), nr);
-		free_page(addr);
-		buf += nr;
-		pos += nr;
-		read += nr;
-		count -= nr;
-		if (!count)
-			break;
-	}
-	filp->f_pos = pos;
-	if (cached_page)
-		free_page(cached_page);
-	if (!IS_RDONLY(inode)) {
-		inode->i_atime = CURRENT_TIME;
-		inode->i_dirt = 1;
-	}
-	return read;
-}
-
-static inline void update_vm_cache(struct inode * inode, unsigned long pos,
-	char * buf, int count)
-{
-	struct page * page;
-
-	page = find_page(inode, pos & PAGE_MASK);
-	if (page) {
-		pos = (pos & ~PAGE_MASK) + page_address(page);
-		memcpy((void *) pos, buf, count);
-	}
-}
 
 static int ext2_file_write (struct inode * inode, struct file * filp,
 			    const char * buf, int count)
diff -u --recursive --new-file v1.3.53/linux/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- v1.3.53/linux/fs/ext2/ialloc.c	Mon Dec 11 15:42:04 1995
+++ linux/fs/ext2/ialloc.c	Thu Jan  4 14:14:20 1996
@@ -464,7 +464,7 @@
 		inode->i_gid = current->fsgid;
 	inode->i_dirt = 1;
 	inode->i_ino = j;
-	inode->i_blksize = sb->s_blocksize;
+	inode->i_blksize = PAGE_SIZE;	/* This is the optimal IO size (for stat), not the fs block size */
 	inode->i_blocks = 0;
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
 	inode->u.ext2_i.i_flags = dir->u.ext2_i.i_flags;
diff -u --recursive --new-file v1.3.53/linux/fs/ext2/inode.c linux/fs/ext2/inode.c
--- v1.3.53/linux/fs/ext2/inode.c	Mon Dec 11 15:42:04 1995
+++ linux/fs/ext2/inode.c	Thu Jan  4 14:14:20 1996
@@ -533,7 +533,7 @@
 	inode->i_ctime = raw_inode->i_ctime;
 	inode->i_mtime = raw_inode->i_mtime;
 	inode->u.ext2_i.i_dtime = raw_inode->i_dtime;
-	inode->i_blksize = inode->i_sb->s_blocksize;
+	inode->i_blksize = PAGE_SIZE;	/* This is the optimal IO size (for stat), not the fs block size */
 	inode->i_blocks = raw_inode->i_blocks;
 	inode->i_version = ++event;
 	inode->u.ext2_i.i_flags = raw_inode->i_flags;
diff -u --recursive --new-file v1.3.53/linux/fs/hpfs/hpfs_fs.c linux/fs/hpfs/hpfs_fs.c
--- v1.3.53/linux/fs/hpfs/hpfs_fs.c	Tue Dec 26 04:45:38 1995
+++ linux/fs/hpfs/hpfs_fs.c	Thu Jan  4 20:26:24 1996
@@ -175,7 +175,7 @@
 	NULL,				/* rename */
 	NULL,				/* readlink */
 	NULL,				/* follow_link */
-	NULL,				/* readpage */
+	generic_readpage,		/* readpage */
 	NULL,				/* writepage */
 	(int (*)(struct inode *, int))
 	&hpfs_bmap,			/* bmap */
diff -u --recursive --new-file v1.3.53/linux/fs/inode.c linux/fs/inode.c
--- v1.3.53/linux/fs/inode.c	Tue Jan  2 16:46:28 1996
+++ linux/fs/inode.c	Wed Jan  3 09:52:21 1996
@@ -306,13 +306,21 @@
  * notify_change is called for inode-changing operations such as
  * chown, chmod, utime, and truncate.  It is guaranteed (unlike
  * write_inode) to be called from the context of the user requesting
- * the change.  It is not called for ordinary access-time updates.
- * NFS uses this to get the authentication correct.  -- jrs
+ * the change.
  */
 
 int notify_change(struct inode * inode, struct iattr *attr)
 {
 	int retval;
+
+	if (attr->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_CTIME)) {
+		unsigned long now = CURRENT_TIME;
+		if (!(attr->ia_valid & ATTR_ATIME_SET))
+			attr->ia_atime = now;
+		if (!(attr->ia_valid & ATTR_MTIME_SET))
+			attr->ia_mtime = now;
+		attr->ia_ctime = now;
+	}
 
 	if (inode->i_sb && inode->i_sb->s_op  &&
 	    inode->i_sb->s_op->notify_change) 
diff -u --recursive --new-file v1.3.53/linux/fs/isofs/file.c linux/fs/isofs/file.c
--- v1.3.53/linux/fs/isofs/file.c	Tue Dec 26 04:45:38 1995
+++ linux/fs/isofs/file.c	Thu Jan  4 20:26:24 1996
@@ -15,27 +15,16 @@
 #include <linux/errno.h>
 #include <linux/stat.h>
 #include <linux/locks.h>
-
-#include <asm/segment.h>
-#include <asm/system.h>
-
-#define	NBUF	32
-
-#define MIN(a,b) (((a)<(b))?(a):(b))
-#define MAX(a,b) (((a)>(b))?(a):(b))
-
 #include <linux/fs.h>
 #include <linux/iso_fs.h>
 
-static int isofs_file_read(struct inode *, struct file *, char *, int);
-
 /*
  * We have mostly NULL's here: the current defaults are ok for
  * the isofs filesystem.
  */
 static struct file_operations isofs_file_operations = {
 	NULL,			/* lseek - default */
-	isofs_file_read,	/* read */
+	generic_file_read,	/* read */
 	NULL,			/* write */
 	NULL,			/* readdir - bad */
 	NULL,			/* select - default */
@@ -59,202 +48,9 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
-	NULL,			/* readpage */
+	generic_readpage,	/* readpage */
 	NULL,			/* writepage */
 	isofs_bmap,		/* bmap */
 	NULL,	       		/* truncate */
 	NULL			/* permission */
 };
-
-/* This is a heuristic to determine if a file is text of binary.  If it
- * is text, then we translate all 0x0d characters to spaces.  If the 0x0d
- * character is not preceded or followed by a 0x0a, then we turn it into
- * a 0x0a.  A control-Z is also turned into a linefeed.
- */
-
-static inline void unixify_to_fs(char * outbuf, char * buffer, int chars,
-				 int mode)
-{
-	char outchar;
-
-	while(chars--){
-	  	outchar = *buffer;
-		if(outchar == 0x1a) outchar = 0x0a;
-		if(outchar == 0x0d){
-			if(mode == ISOFS_FILE_TEXT_M) outchar = 0x0a;
-			if(mode == ISOFS_FILE_TEXT) outchar = ' ';
-		}
-		put_user(outchar, outbuf++);
-		buffer++;
-	}
-}
-
-/*This function determines if a given file has a DOS-like text format or not*/
-
-static void isofs_determine_filetype(struct inode * inode)
-{
-	int block;
-	int result, i;
-	struct buffer_head * bh;
-	unsigned char * pnt;
-	
-	block = isofs_bmap(inode,0);
-	if (block && (bh = bread(inode->i_dev,block, ISOFS_BUFFER_SIZE(inode)))) {
-		pnt = (unsigned char *) bh->b_data;
-		result = ISOFS_FILE_TEXT_M;
-		for(i=0;i<(inode->i_size < ISOFS_BUFFER_SIZE(inode) ? inode->i_size : ISOFS_BUFFER_SIZE(inode));
-		    i++,pnt++){
-			if(*pnt & 0x80) {result = ISOFS_FILE_BINARY; break;};
-			if(*pnt >= 0x20 || *pnt == 0x1a) continue;
-			if(*pnt == 0x0a) {result = ISOFS_FILE_TEXT; continue;};
-			if(*pnt >= 0x9 && *pnt <= 0x0d) continue;
-			result = ISOFS_FILE_BINARY;
-			break;
-		}
-		brelse(bh);
-		inode->u.isofs_i.i_file_format = result;
-	}
-}
-
-static int isofs_file_read(struct inode * inode, struct file * filp, char * buf, int count)
-{
-	int read,left,chars;
-	int block, blocks, offset, total_blocks;
-	int bhrequest;
-	int ra_blocks, max_block, nextblock;
-	struct buffer_head ** bhb, ** bhe;
-	struct buffer_head * bhreq[NBUF];
-	struct buffer_head * buflist[NBUF];
-	
-	if (!inode) {
-		printk("isofs_file_read: inode = NULL\n");
-		return -EINVAL;
-	}
-	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
-		printk("isofs_file_read: mode = %07o\n",inode->i_mode);
-		return -EINVAL;
-	}
-	if (inode->u.isofs_i.i_file_format == ISOFS_FILE_UNKNOWN)
-		isofs_determine_filetype(inode);
-	if (filp->f_pos > inode->i_size)
-		left = 0;
-	else
-		left = inode->i_size - filp->f_pos;
-	if (left > count)
-		left = count;
-	if (left <= 0)
-		return 0;
-	read = 0;
-	block = filp->f_pos >> ISOFS_BUFFER_BITS(inode);
-	offset = (inode->u.isofs_i.i_first_extent + filp->f_pos)
-	  & (ISOFS_BUFFER_SIZE(inode)-1);
-	blocks = (left + offset + ISOFS_BUFFER_SIZE(inode) - 1) / ISOFS_BUFFER_SIZE(inode);
-	bhb = bhe = buflist;
-
-	ra_blocks = read_ahead[MAJOR(inode->i_dev)] / (BLOCK_SIZE >> 9);
-	if(ra_blocks > blocks) blocks = ra_blocks;
-
-	/*
-	 * this is for stopping read ahead at EOF. It's  important for
-	 * reading PhotoCD's, because they have many small data tracks instead
-	 * of one big. And between two data-tracks are some unreadable sectors.
-	 * A read ahead after a EOF may try to read such an unreadable sector.
-	 *    kraxel@cs.tu-berlin.de (Gerd Knorr)
-	 */
-	total_blocks = (inode->i_size + (1 << ISOFS_BUFFER_BITS(inode)) - 1)
-	   >> ISOFS_BUFFER_BITS(inode);
-	if (block + blocks > total_blocks)
-		blocks = total_blocks - block;
-
-	max_block = (inode->i_size + BLOCK_SIZE - 1)/BLOCK_SIZE;
-	nextblock = -1;
-
-	/* We do this in a two stage process.  We first try and request
-	   as many blocks as we can, then we wait for the first one to
-	   complete, and then we try and wrap up as many as are actually
-	   done.  This routine is rather generic, in that it can be used
-	   in a filesystem by substituting the appropriate function in
-	   for getblk.
-
-	   This routine is optimized to make maximum use of the various
-	   buffers and caches. */
-
-	do {
-	        bhrequest = 0;
-		while (blocks) {
-		        int uptodate;
-			--blocks;
-			*bhb = getblk(inode->i_dev,isofs_bmap(inode, block++), ISOFS_BUFFER_SIZE(inode));
-			uptodate = 1;
-			if (*bhb && !buffer_uptodate(*bhb)) {
-			        uptodate = 0;
-			        bhreq[bhrequest++] = *bhb;
-			      };
-
-			if (++bhb == &buflist[NBUF])
-				bhb = buflist;
-
-			/* If the block we have on hand is uptodate, go ahead
-			   and complete processing. */
-			if(uptodate) break;
-
-			if (bhb == bhe)
-				break;
-		      }
-
-		/* Now request them all */
-		if (bhrequest)
-		  ll_rw_block(READ, bhrequest, bhreq);
-
-		do{ /* Finish off all I/O that has actually completed */
-		  if (*bhe) {/* test for valid buffer */
-		    wait_on_buffer(*bhe);
-		    if (!buffer_uptodate(*bhe)) {
-		      brelse(*bhe);
-		      if (++bhe == &buflist[NBUF])
-			bhe = buflist;
-		      left = 0;
-		      break;
-		    }
-		  }
-		  
-		  if (left < ISOFS_BUFFER_SIZE(inode) - offset)
-		    chars = left;
-		  else
-		    chars = ISOFS_BUFFER_SIZE(inode) - offset;
-		  filp->f_pos += chars;
-		  left -= chars;
-		  read += chars;
-		  if (*bhe) {
-		    if (inode->u.isofs_i.i_file_format == ISOFS_FILE_TEXT ||
-			inode->u.isofs_i.i_file_format == ISOFS_FILE_TEXT_M)
-		      unixify_to_fs(buf, offset+(*bhe)->b_data, chars, 
-				    inode->u.isofs_i.i_file_format);
-		    else
-		      memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
-		    brelse(*bhe);
-		    buf += chars;
-		  } else {
-		    while (chars-->0)
-		      put_user(0,buf++);
-		  }
-		  offset = 0;
-		  if (++bhe == &buflist[NBUF])
-		    bhe = buflist;
-		} while( bhe != bhb && (*bhe == 0 || !buffer_locked(*bhe)) && 
-			(left > 0));
-	} while (left > 0);
-
-/* Release the read-ahead blocks */
-	while (bhe != bhb) {
-	  if (*bhe) brelse(*bhe);
-	  if (++bhe == &buflist[NBUF])
-	    bhe = buflist;
-	};
-
-	filp->f_reada = 1;
-
-	if (!read)
-		return -EIO;
-	return read;
-}
diff -u --recursive --new-file v1.3.53/linux/fs/minix/file.c linux/fs/minix/file.c
--- v1.3.53/linux/fs/minix/file.c	Tue Dec 26 04:45:38 1995
+++ linux/fs/minix/file.c	Thu Jan  4 20:26:24 1996
@@ -25,7 +25,6 @@
 #include <linux/fs.h>
 #include <linux/minix_fs.h>
 
-static int minix_file_read(struct inode *, struct file *, char *, int);
 static int minix_file_write(struct inode *, struct file *, const char *, int);
 
 /*
@@ -34,7 +33,7 @@
  */
 static struct file_operations minix_file_operations = {
 	NULL,			/* lseek - default */
-	minix_file_read,	/* read */
+	generic_file_read,	/* read */
 	minix_file_write,	/* write */
 	NULL,			/* readdir - bad */
 	NULL,			/* select - default */
@@ -58,136 +57,13 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
-	NULL,			/* readpage */
+	generic_readpage,	/* readpage */
 	NULL,			/* writepage */
 	minix_bmap,		/* bmap */
 	minix_truncate,		/* truncate */
 	NULL			/* permission */
 };
 
-static int minix_file_read(struct inode * inode, struct file * filp, char * buf, int count)
-{
-	int read,left,chars;
-	int block, blocks, offset;
-	int bhrequest, uptodate;
-	struct buffer_head ** bhb, ** bhe;
-	struct buffer_head * bhreq[NBUF];
-	struct buffer_head * buflist[NBUF];
-	unsigned int size;
-
-	if (!inode) {
-		printk("minix_file_read: inode = NULL\n");
-		return -EINVAL;
-	}
-	if (!S_ISREG(inode->i_mode)) {
-		printk("minix_file_read: mode = %07o\n",inode->i_mode);
-		return -EINVAL;
-	}
-	offset = filp->f_pos;
-	size = inode->i_size;
-	if (offset > size)
-		left = 0;
-	else
-		left = size - offset;
-	if (left > count)
-		left = count;
-	if (left <= 0)
-		return 0;
-	read = 0;
-	block = offset >> BLOCK_SIZE_BITS;
-	offset &= BLOCK_SIZE-1;
-	size = (size + (BLOCK_SIZE-1)) >> BLOCK_SIZE_BITS;
-	blocks = (left + offset + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS;
-	bhb = bhe = buflist;
-	if (filp->f_reada) {
-	        if(blocks < read_ahead[MAJOR(inode->i_dev)] / (BLOCK_SIZE >> 9))
-		  blocks = read_ahead[MAJOR(inode->i_dev)] / (BLOCK_SIZE >> 9);
-		if (block + blocks > size)
-			blocks = size - block;
-	}
-
-	/* We do this in a two stage process.  We first try and request
-	   as many blocks as we can, then we wait for the first one to
-	   complete, and then we try and wrap up as many as are actually
-	   done.  This routine is rather generic, in that it can be used
-	   in a filesystem by substituting the appropriate function in
-	   for getblk.
-
-	   This routine is optimized to make maximum use of the various
-	   buffers and caches. */
-
-	do {
-		bhrequest = 0;
-		uptodate = 1;
-		while (blocks) {
-			--blocks;
-			*bhb = minix_getblk(inode, block++, 0);
-			if (*bhb && !buffer_uptodate(*bhb)) {
-				uptodate = 0;
-				bhreq[bhrequest++] = *bhb;
-			}
-
-			if (++bhb == &buflist[NBUF])
-				bhb = buflist;
-
-			/* If the block we have on hand is uptodate, go ahead
-			   and complete processing. */
-			if (uptodate)
-				break;
-			if (bhb == bhe)
-				break;
-		}
-
-		/* Now request them all */
-		if (bhrequest)
-			ll_rw_block(READ, bhrequest, bhreq);
-
-		do { /* Finish off all I/O that has actually completed */
-			if (*bhe) {
-				wait_on_buffer(*bhe);
-				if (!buffer_uptodate(*bhe)) {	/* read error? */
-				        brelse(*bhe);
-					if (++bhe == &buflist[NBUF])
-					  bhe = buflist;
-					left = 0;
-					break;
-				}
-			}
-			if (left < BLOCK_SIZE - offset)
-				chars = left;
-			else
-				chars = BLOCK_SIZE - offset;
-			filp->f_pos += chars;
-			left -= chars;
-			read += chars;
-			if (*bhe) {
-				memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
-				brelse(*bhe);
-				buf += chars;
-			} else {
-				while (chars-->0)
-					put_user(0,buf++);
-			}
-			offset = 0;
-			if (++bhe == &buflist[NBUF])
-				bhe = buflist;
-		} while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe)));
-	} while (left > 0);
-
-/* Release the read-ahead blocks */
-	while (bhe != bhb) {
-		brelse(*bhe);
-		if (++bhe == &buflist[NBUF])
-			bhe = buflist;
-	};
-	if (!read)
-		return -EIO;
-	filp->f_reada = 1;
-	if (!IS_RDONLY(inode))
-		inode->i_atime = CURRENT_TIME;
-	return read;
-}
-
 static int minix_file_write(struct inode * inode, struct file * filp, const char * buf, int count)
 {
 	off_t pos;
@@ -229,13 +105,14 @@
 			}
 		}
 		p = (pos % BLOCK_SIZE) + bh->b_data;
-		pos += c;
-		written += c;
 		memcpy_fromfs(p,buf,c);
-		buf += c;
+		update_vm_cache(inode, pos, p, c);
 		mark_buffer_uptodate(bh, 1);
 		mark_buffer_dirty(bh, 0);
 		brelse(bh);
+		pos += c;
+		written += c;
+		buf += c;
 	}
 	if (pos > inode->i_size)
 		inode->i_size = pos;
diff -u --recursive --new-file v1.3.53/linux/fs/minix/fsync.c linux/fs/minix/fsync.c
--- v1.3.53/linux/fs/minix/fsync.c	Mon Nov 27 12:48:31 1995
+++ linux/fs/minix/fsync.c	Thu Jan  4 14:07:58 1996
@@ -5,6 +5,9 @@
  *  from
  *  Copyright (C) 1991, 1992 Linus Torvalds
  *
+ *  Copyright (C) 1996 Gertjan van Wingerde (gertjan@cs.vu.nl)
+ *	Minix V2 fs support
+ *
  *  minix fsync primitive
  */
 
@@ -21,9 +24,11 @@
 #include <asm/system.h>
 
 #define blocksize BLOCK_SIZE
-#define addr_per_block 512
 
-static int sync_block (struct inode * inode, unsigned short * block, int wait)
+/*
+ * The functions for minix V1 fs file synchronization.
+ */
+static int V1_sync_block (struct inode * inode, unsigned short * block, int wait)
 {
 	struct buffer_head * bh;
 	unsigned short tmp;
@@ -52,7 +57,7 @@
 	return 0;
 }
 
-static int sync_iblock (struct inode * inode, unsigned short * iblock, 
+static int V1_sync_iblock (struct inode * inode, unsigned short * iblock, 
 			struct buffer_head **bh, int wait) 
 {
 	int rc;
@@ -62,7 +67,7 @@
 	tmp = *iblock;
 	if (!tmp)
 		return 0;
-	rc = sync_block (inode, iblock, wait);
+	rc = V1_sync_block (inode, iblock, wait);
 	if (rc)
 		return rc;
 	*bh = bread(inode->i_dev, tmp, blocksize);
@@ -76,14 +81,14 @@
 	return 0;
 }
 
-
-static int sync_direct(struct inode *inode, int wait)
+static int V1_sync_direct(struct inode *inode, int wait)
 {
 	int i;
 	int rc, err = 0;
 
 	for (i = 0; i < 7; i++) {
-		rc = sync_block (inode, inode->u.minix_i.i_data + i, wait);
+		rc = V1_sync_block (inode, 
+		     (unsigned short *) inode->u.minix_i.u.i1_data + i, wait);
 		if (rc > 0)
 			break;
 		if (rc)
@@ -92,20 +97,20 @@
 	return err;
 }
 
-static int sync_indirect(struct inode *inode, unsigned short *iblock, int wait)
+static int V1_sync_indirect(struct inode *inode, unsigned short *iblock, int wait)
 {
 	int i;
 	struct buffer_head * ind_bh;
 	int rc, err = 0;
 
-	rc = sync_iblock (inode, iblock, &ind_bh, wait);
+	rc = V1_sync_iblock (inode, iblock, &ind_bh, wait);
 	if (rc || !ind_bh)
 		return rc;
-	
-	for (i = 0; i < addr_per_block; i++) {
-		rc = sync_block (inode, 
-				 ((unsigned short *) ind_bh->b_data) + i,
-				 wait);
+
+	for (i = 0; i < 512; i++) {
+		rc = V1_sync_block (inode, 
+				    ((unsigned short *) ind_bh->b_data) + i, 
+				    wait);
 		if (rc > 0)
 			break;
 		if (rc)
@@ -115,31 +120,192 @@
 	return err;
 }
 
-static int sync_dindirect(struct inode *inode, unsigned short *diblock,
+static int V1_sync_dindirect(struct inode *inode, unsigned short *diblock,
 			  int wait)
 {
 	int i;
 	struct buffer_head * dind_bh;
 	int rc, err = 0;
 
-	rc = sync_iblock (inode, diblock, &dind_bh, wait);
+	rc = V1_sync_iblock (inode, diblock, &dind_bh, wait);
 	if (rc || !dind_bh)
 		return rc;
+
+	for (i = 0; i < 512; i++) {
+		rc = V1_sync_indirect (inode,
+				       ((unsigned short *) dind_bh->b_data) + i, 
+				       wait);
+		if (rc > 0)
+			break;
+		if (rc)
+			err = rc;
+	}
+	brelse(dind_bh);
+	return err;
+}
+
+int V1_minix_sync_file(struct inode * inode, struct file * file)
+{
+	int wait, err = 0;
 	
-	for (i = 0; i < addr_per_block; i++) {
-		rc = sync_indirect (inode,
-				    ((unsigned short *) dind_bh->b_data) + i,
+	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+	     S_ISLNK(inode->i_mode)))
+		return -EINVAL;
+
+	for (wait=0; wait<=1; wait++)
+	{
+		err |= V1_sync_direct(inode, wait);
+		err |= V1_sync_indirect(inode, inode->u.minix_i.u.i1_data + 7, wait);
+		err |= V1_sync_dindirect(inode, inode->u.minix_i.u.i1_data + 8, wait);
+	}
+	err |= minix_sync_inode (inode);
+	return (err < 0) ? -EIO : 0;
+}
+
+/* 
+ * The functions for minix V2 fs file synchronization.
+ */
+static int V2_sync_block (struct inode * inode, unsigned long * block, int wait)
+{
+	struct buffer_head * bh;
+	unsigned long tmp;
+	
+	if (!*block)
+		return 0;
+	tmp = *block;
+	bh = get_hash_table(inode->i_dev, *block, blocksize);
+	if (!bh)
+		return 0;
+	if (*block != tmp) {
+		brelse (bh);
+		return 1;
+	}
+	if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
+		brelse(bh);
+		return -1;
+	}
+	if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh))
+	{
+		brelse(bh);
+		return 0;
+	}
+	ll_rw_block(WRITE, 1, &bh);
+	bh->b_count--;
+	return 0;
+}
+
+static int V2_sync_iblock (struct inode * inode, unsigned long * iblock, 
+			struct buffer_head **bh, int wait) 
+{
+	int rc;
+	unsigned long tmp;
+	
+	*bh = NULL;
+	tmp = *iblock;
+	if (!tmp)
+		return 0;
+	rc = V2_sync_block (inode, iblock, wait);
+	if (rc)
+		return rc;
+	*bh = bread(inode->i_dev, tmp, blocksize);
+	if (tmp != *iblock) {
+		brelse(*bh);
+		*bh = NULL;
+		return 1;
+	}
+	if (!*bh)
+		return -1;
+	return 0;
+}
+
+static int V2_sync_direct(struct inode *inode, int wait)
+{
+	int i;
+	int rc, err = 0;
+
+	for (i = 0; i < 7; i++) {
+		rc = V2_sync_block (inode, 
+			(unsigned long *)inode->u.minix_i.u.i2_data + i, wait);
+		if (rc > 0)
+			break;
+		if (rc)
+			err = rc;
+	}
+	return err;
+}
+
+static int V2_sync_indirect(struct inode *inode, unsigned long *iblock, int wait)
+{
+	int i;
+	struct buffer_head * ind_bh;
+	int rc, err = 0;
+
+	rc = V2_sync_iblock (inode, iblock, &ind_bh, wait);
+	if (rc || !ind_bh)
+		return rc;
+
+	for (i = 0; i < 256; i++) {
+		rc = V2_sync_block (inode, 
+				    ((unsigned long *) ind_bh->b_data) + i, 
 				    wait);
 		if (rc > 0)
 			break;
 		if (rc)
 			err = rc;
 	}
+	brelse(ind_bh);
+	return err;
+}
+
+static int V2_sync_dindirect(struct inode *inode, unsigned long *diblock,
+			  int wait)
+{
+	int i;
+	struct buffer_head * dind_bh;
+	int rc, err = 0;
+
+	rc = V2_sync_iblock (inode, diblock, &dind_bh, wait);
+	if (rc || !dind_bh)
+		return rc;
+
+	for (i = 0; i < 256; i++) {
+		rc = V2_sync_indirect (inode,
+				       ((unsigned long *) dind_bh->b_data) + i, 
+				       wait);
+		if (rc > 0)
+			break;
+		if (rc)
+			err = rc;
+	}
 	brelse(dind_bh);
 	return err;
 }
 
-int minix_sync_file(struct inode * inode, struct file * file)
+static int V2_sync_tindirect(struct inode *inode, unsigned long *tiblock,
+			  int wait)
+{
+	int i;
+	struct buffer_head * tind_bh;
+	int rc, err = 0;
+
+	rc = V2_sync_iblock (inode, tiblock, &tind_bh, wait);
+	if (rc || !tind_bh)
+		return rc;
+
+	for (i = 0; i < 256; i++) {
+		rc = V2_sync_dindirect (inode,
+					((unsigned long *) tind_bh->b_data) + i, 
+					wait);
+		if (rc > 0)
+			break;
+		if (rc)
+			err = rc;
+	}
+	brelse(tind_bh);
+	return err;
+}
+
+int V2_minix_sync_file(struct inode * inode, struct file * file)
 {
 	int wait, err = 0;
 	
@@ -149,10 +315,25 @@
 
 	for (wait=0; wait<=1; wait++)
 	{
-		err |= sync_direct(inode, wait);
-		err |= sync_indirect(inode, inode->u.minix_i.i_data+7, wait);
-		err |= sync_dindirect(inode, inode->u.minix_i.i_data+8, wait);
+		err |= V2_sync_direct(inode, wait);
+		err |= V2_sync_indirect(inode, 
+		      (unsigned long *) inode->u.minix_i.u.i2_data + 7, wait);
+		err |= V2_sync_dindirect(inode, 
+		      (unsigned long *) inode->u.minix_i.u.i2_data + 8, wait);
+		err |= V2_sync_tindirect(inode, 
+		      (unsigned long *) inode->u.minix_i.u.i2_data + 9, wait);
 	}
 	err |= minix_sync_inode (inode);
 	return (err < 0) ? -EIO : 0;
+}
+
+/*
+ * The function which is called for file synchronization.
+ */
+int minix_sync_file(struct inode * inode, struct file * file)
+{
+	if (INODE_VERSION(inode) == MINIX_V1)
+		return V1_minix_sync_file(inode, file);
+	else
+		return V2_minix_sync_file(inode, file);
 }
diff -u --recursive --new-file v1.3.53/linux/fs/minix/inode.c linux/fs/minix/inode.c
--- v1.3.53/linux/fs/minix/inode.c	Mon Nov 27 12:48:32 1995
+++ linux/fs/minix/inode.c	Thu Jan  4 14:07:58 1996
@@ -2,6 +2,9 @@
  *  linux/fs/minix/inode.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Copyright (C) 1996  Gertjan van Wingerde    (gertjan@cs.vu.nl)
+ *	Minix V2 fs support.
  */
 
 #include <linux/module.h>
@@ -114,7 +117,6 @@
 	return 0;
 }
 
-
 struct super_block *minix_read_super(struct super_block *s,void *data, 
 				     int silent)
 {
@@ -124,7 +126,9 @@
 	kdev_t dev = s->s_dev;
 
 	if (32 != sizeof (struct minix_inode))
-		panic("bad i-node size");
+		panic("bad V1 i-node size");
+	if (64 != sizeof(struct minix2_inode))
+		panic("bad V2 i-node size");
 	MOD_INC_USE_COUNT;
 	lock_super(s);
 	set_blocksize(dev, BLOCK_SIZE);
@@ -142,7 +146,6 @@
 	s->s_blocksize = 1024;
 	s->s_blocksize_bits = 10;
 	s->u.minix_sb.s_ninodes = ms->s_ninodes;
-	s->u.minix_sb.s_nzones = ms->s_nzones;
 	s->u.minix_sb.s_imap_blocks = ms->s_imap_blocks;
 	s->u.minix_sb.s_zmap_blocks = ms->s_zmap_blocks;
 	s->u.minix_sb.s_firstdatazone = ms->s_firstdatazone;
@@ -150,9 +153,23 @@
 	s->u.minix_sb.s_max_size = ms->s_max_size;
 	s->s_magic = ms->s_magic;
 	if (s->s_magic == MINIX_SUPER_MAGIC) {
+		s->u.minix_sb.s_version = MINIX_V1;
+		s->u.minix_sb.s_nzones = ms->s_nzones;
 		s->u.minix_sb.s_dirsize = 16;
 		s->u.minix_sb.s_namelen = 14;
 	} else if (s->s_magic == MINIX_SUPER_MAGIC2) {
+		s->u.minix_sb.s_version = MINIX_V1;
+		s->u.minix_sb.s_nzones = ms->s_nzones;
+		s->u.minix_sb.s_dirsize = 32;
+		s->u.minix_sb.s_namelen = 30;
+	} else if (s->s_magic == MINIX2_SUPER_MAGIC) {
+		s->u.minix_sb.s_version = MINIX_V2;
+		s->u.minix_sb.s_nzones = ms->s_zones;
+		s->u.minix_sb.s_dirsize = 16;
+		s->u.minix_sb.s_namelen = 14;
+	} else if (s->s_magic == MINIX2_SUPER_MAGIC2) {
+		s->u.minix_sb.s_version = MINIX_V2;
+		s->u.minix_sb.s_nzones = ms->s_zones;
 		s->u.minix_sb.s_dirsize = 32;
 		s->u.minix_sb.s_namelen = 30;
 	} else {
@@ -160,7 +177,7 @@
 		unlock_super(s);
 		brelse(bh);
 		if (!silent)
-			printk("VFS: Can't find a minix filesystem on dev "
+			printk("VFS: Can't find a minix or minix V2 filesystem on dev "
 			       "%s.\n", kdevname(dev));
 		MOD_DEC_USE_COUNT;
 		return NULL;
@@ -224,8 +241,8 @@
 {
 	struct statfs tmp;
 
-	tmp.f_type = MINIX_SUPER_MAGIC;
-	tmp.f_bsize = 1024;
+	tmp.f_type = sb->s_magic;
+	tmp.f_bsize = sb->s_blocksize;
 	tmp.f_blocks = (sb->u.minix_sb.s_nzones - sb->u.minix_sb.s_firstdatazone) << sb->u.minix_sb.s_log_zone_size;
 	tmp.f_bfree = minix_count_free_blocks(sb);
 	tmp.f_bavail = tmp.f_bfree;
@@ -235,9 +252,12 @@
 	memcpy_tofs(buf, &tmp, bufsiz);
 }
 
-#define inode_bmap(inode,nr) ((inode)->u.minix_i.i_data[(nr)])
+/*
+ * The minix V1 fs bmap functions.
+ */
+#define V1_inode_bmap(inode,nr) (((unsigned short *)(inode)->u.minix_i.u.i1_data)[(nr)])
 
-static int block_bmap(struct buffer_head * bh, int nr)
+static int V1_block_bmap(struct buffer_head * bh, int nr)
 {
 	int tmp;
 
@@ -248,7 +268,7 @@
 	return tmp;
 }
 
-int minix_bmap(struct inode * inode,int block)
+static int V1_minix_bmap(struct inode * inode,int block)
 {
 	int i;
 
@@ -256,36 +276,110 @@
 		printk("minix_bmap: block<0");
 		return 0;
 	}
-	if (block >= 7+512+512*512) {
+	if (block >= (inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE)) {
 		printk("minix_bmap: block>big");
 		return 0;
 	}
 	if (block < 7)
-		return inode_bmap(inode,block);
+		return V1_inode_bmap(inode,block);
 	block -= 7;
 	if (block < 512) {
-		i = inode_bmap(inode,7);
+		i = V1_inode_bmap(inode,7);
 		if (!i)
 			return 0;
-		return block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block);
+		return V1_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block);
 	}
 	block -= 512;
-	i = inode_bmap(inode,8);
+	i = V1_inode_bmap(inode,8);
+	if (!i)
+		return 0;
+	i = V1_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block>>9);
+	if (!i)
+		return 0;
+	return V1_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 511);
+}
+
+/*
+ * The minix V2 fs bmap functions.
+ */
+#define V2_inode_bmap(inode,nr) (((unsigned long  *)(inode)->u.minix_i.u.i2_data)[(nr)])
+static int V2_block_bmap(struct buffer_head * bh, int nr)
+{
+	int tmp;
+
+	if (!bh)
+		return 0;
+	tmp = ((unsigned long *) bh->b_data)[nr];
+	brelse(bh);
+	return tmp;
+}
+
+int V2_minix_bmap(struct inode * inode,int block)
+{
+	int i;
+
+	if (block<0) {
+		printk("minix_bmap: block<0");
+		return 0;
+	}
+	if (block >= (inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE)) {
+		printk("minix_bmap: block>big");
+		return 0;
+	}
+	if (block < 7)
+		return V2_inode_bmap(inode,block);
+	block -= 7;
+	if (block < 256) {
+		i = V2_inode_bmap(inode,7);
+		if (!i)
+			return 0;
+		return V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block);
+	}
+	block -= 256;
+	if (block < 256*256) {
+		i = V2_inode_bmap(inode,8);
+		if (!i)
+			return 0;
+		i = V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block >> 8);
+		if (!i)
+			return 0;
+		return V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 255);
+	}
+	block -= 256*256;
+	i = V2_inode_bmap(inode,9);
+	if (!i)
+		return 0;
+	i = V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block >> 16);
 	if (!i)
 		return 0;
-	i = block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block>>9);
+	i = V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),(block >> 8) & 255);
 	if (!i)
 		return 0;
-	return block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 511);
+	return V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 255);
 }
 
-static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create)
+/*
+ * The global minix fs bmap function.
+ */
+int minix_bmap(struct inode * inode,int block)
+{
+	if (INODE_VERSION(inode) == MINIX_V1)
+		return V1_minix_bmap(inode, block);
+	else
+		return V2_minix_bmap(inode, block);
+}
+
+/*
+ * The minix V1 fs getblk functions.
+ */
+static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr, 
+					    int create)
 {
 	int tmp;
 	unsigned short *p;
 	struct buffer_head * result;
 
-	p = inode->u.minix_i.i_data + nr;
+	p = inode->u.minix_i.u.i1_data + nr;
 repeat:
 	tmp = *p;
 	if (tmp) {
@@ -312,7 +406,7 @@
 	return result;
 }
 
-static struct buffer_head * block_getblk(struct inode * inode, 
+static struct buffer_head * V1_block_getblk(struct inode * inode, 
 	struct buffer_head * bh, int nr, int create)
 {
 	int tmp;
@@ -362,7 +456,8 @@
 	return result;
 }
 
-struct buffer_head * minix_getblk(struct inode * inode, int block, int create)
+static struct buffer_head * V1_minix_getblk(struct inode * inode, int block, 
+					    int create)
 {
 	struct buffer_head * bh;
 
@@ -370,21 +465,152 @@
 		printk("minix_getblk: block<0");
 		return NULL;
 	}
-	if (block >= 7+512+512*512) {
+	if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) {
 		printk("minix_getblk: block>big");
 		return NULL;
 	}
 	if (block < 7)
-		return inode_getblk(inode,block,create);
+		return V1_inode_getblk(inode,block,create);
 	block -= 7;
 	if (block < 512) {
-		bh = inode_getblk(inode,7,create);
-		return block_getblk(inode, bh, block, create);
+		bh = V1_inode_getblk(inode,7,create);
+		return V1_block_getblk(inode, bh, block, create);
 	}
 	block -= 512;
-	bh = inode_getblk(inode,8,create);
-	bh = block_getblk(inode, bh, block>>9, create);
-	return block_getblk(inode, bh, block & 511, create);
+	bh = V1_inode_getblk(inode,8,create);
+	bh = V1_block_getblk(inode, bh, (block>>9) & 511, create);
+	return V1_block_getblk(inode, bh, block & 511, create);
+}
+
+/*
+ * The minix V2 fs getblk functions.
+ */
+static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr, 
+					    int create)
+{
+	int tmp;
+	unsigned long *p;
+	struct buffer_head * result;
+
+	p = (unsigned long *) inode->u.minix_i.u.i2_data + nr;
+repeat:
+	tmp = *p;
+	if (tmp) {
+		result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
+		if (tmp == *p)
+			return result;
+		brelse(result);
+		goto repeat;
+	}
+	if (!create)
+		return NULL;
+	tmp = minix_new_block(inode->i_sb);
+	if (!tmp)
+		return NULL;
+	result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
+	if (*p) {
+		minix_free_block(inode->i_sb,tmp);
+		brelse(result);
+		goto repeat;
+	}
+	*p = tmp;
+	inode->i_ctime = CURRENT_TIME;
+	inode->i_dirt = 1;
+	return result;
+}
+
+static struct buffer_head * V2_block_getblk(struct inode * inode, 
+	struct buffer_head * bh, int nr, int create)
+{
+	int tmp;
+	unsigned long *p;
+	struct buffer_head * result;
+
+	if (!bh)
+		return NULL;
+	if (!buffer_uptodate(bh)) {
+		ll_rw_block(READ, 1, &bh);
+		wait_on_buffer(bh);
+		if (!buffer_uptodate(bh)) {
+			brelse(bh);
+			return NULL;
+		}
+	}
+	p = nr + (unsigned long *) bh->b_data;
+repeat:
+	tmp = *p;
+	if (tmp) {
+		result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
+		if (tmp == *p) {
+			brelse(bh);
+			return result;
+		}
+		brelse(result);
+		goto repeat;
+	}
+	if (!create) {
+		brelse(bh);
+		return NULL;
+	}
+	tmp = minix_new_block(inode->i_sb);
+	if (!tmp) {
+		brelse(bh);
+		return NULL;
+	}
+	result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
+	if (*p) {
+		minix_free_block(inode->i_sb,tmp);
+		brelse(result);
+		goto repeat;
+	}
+	*p = tmp;
+	mark_buffer_dirty(bh, 1);
+	brelse(bh);
+	return result;
+}
+
+static struct buffer_head * V2_minix_getblk(struct inode * inode, int block, 
+					    int create)
+{
+	struct buffer_head * bh;
+
+	if (block<0) {
+		printk("minix_getblk: block<0");
+		return NULL;
+	}
+	if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) {
+		printk("minix_getblk: block>big");
+		return NULL;
+	}
+	if (block < 7)
+		return V2_inode_getblk(inode,block,create);
+	block -= 7;
+	if (block < 256) {
+		bh = V2_inode_getblk(inode,7,create);
+		return V2_block_getblk(inode, bh, block, create);
+	}
+	block -= 256;
+	if (block < 256*256) {
+		bh = V2_inode_getblk(inode,8,create);
+		bh = V2_block_getblk(inode, bh, (block>>8) & 255, create);
+		return V2_block_getblk(inode, bh, block & 255, create);
+	}
+	block -= 256*256;
+	bh = V2_inode_getblk(inode,9,create);
+	bh = V2_block_getblk(inode, bh, (block >> 16) & 255, create);
+	bh = V2_block_getblk(inode, bh, (block >> 8) & 255, create);
+	return V2_block_getblk(inode, bh, block & 255, create);
+}
+
+/*
+ * the global minix fs getblk function.
+ */
+struct buffer_head * minix_getblk(struct inode * inode, int block, int create)
+{
+	if (INODE_VERSION(inode) == MINIX_V1)
+		return V1_minix_getblk(inode,block,create);
+	else
+		return V2_minix_getblk(inode,block,create);
 }
 
 struct buffer_head * minix_bread(struct inode * inode, int block, int create)
@@ -402,7 +628,10 @@
 	return NULL;
 }
 
-void minix_read_inode(struct inode * inode)
+/*
+ * The minix V1 function to read an inode.
+ */
+static void V1_minix_read_inode(struct inode * inode)
 {
 	struct buffer_head * bh;
 	struct minix_inode * raw_inode;
@@ -437,7 +666,63 @@
 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
 		inode->i_rdev = to_kdev_t(raw_inode->i_zone[0]);
 	else for (block = 0; block < 9; block++)
-		inode->u.minix_i.i_data[block] = raw_inode->i_zone[block];
+		inode->u.minix_i.u.i1_data[block] = raw_inode->i_zone[block];
+	brelse(bh);
+	if (S_ISREG(inode->i_mode))
+		inode->i_op = &minix_file_inode_operations;
+	else if (S_ISDIR(inode->i_mode))
+		inode->i_op = &minix_dir_inode_operations;
+	else if (S_ISLNK(inode->i_mode))
+		inode->i_op = &minix_symlink_inode_operations;
+	else if (S_ISCHR(inode->i_mode))
+		inode->i_op = &chrdev_inode_operations;
+	else if (S_ISBLK(inode->i_mode))
+		inode->i_op = &blkdev_inode_operations;
+	else if (S_ISFIFO(inode->i_mode))
+		init_fifo(inode);
+}
+
+/*
+ * The minix V2 function to read an inode.
+ */
+static void V2_minix_read_inode(struct inode * inode)
+{
+	struct buffer_head * bh;
+	struct minix2_inode * raw_inode;
+	int block, ino;
+
+	ino = inode->i_ino;
+	inode->i_op = NULL;
+	inode->i_mode = 0;
+	if (!ino || ino >= inode->i_sb->u.minix_sb.s_ninodes) {
+		printk("Bad inode number on dev %s"
+		       ": %d is out of range\n",
+			kdevname(inode->i_dev), ino);
+		return;
+	}
+	block = 2 + inode->i_sb->u.minix_sb.s_imap_blocks +
+		    inode->i_sb->u.minix_sb.s_zmap_blocks +
+		    (ino-1)/MINIX2_INODES_PER_BLOCK;
+	if (!(bh=bread(inode->i_dev,block, BLOCK_SIZE))) {
+		printk("Major problem: unable to read inode from dev "
+		       "%s\n", kdevname(inode->i_dev));
+		return;
+	}
+	raw_inode = ((struct minix2_inode *) bh->b_data) +
+		    (ino-1)%MINIX2_INODES_PER_BLOCK;
+	inode->i_mode = raw_inode->i_mode;
+	inode->i_uid = raw_inode->i_uid;
+	inode->i_gid = raw_inode->i_gid;
+	inode->i_nlink = raw_inode->i_nlinks;
+	inode->i_size = raw_inode->i_size;
+	inode->i_mtime = raw_inode->i_mtime;
+	inode->i_atime = raw_inode->i_atime;
+	inode->i_ctime = raw_inode->i_ctime;
+	inode->i_blocks = inode->i_blksize = 0;
+	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+		inode->i_rdev = to_kdev_t(raw_inode->i_zone[0]);
+	else for (block = 0; block < 10; block++)
+		inode->u.minix_i.u.i2_data[block] = raw_inode->i_zone[block];
 	brelse(bh);
 	if (S_ISREG(inode->i_mode))
 		inode->i_op = &minix_file_inode_operations;
@@ -453,7 +738,21 @@
 		init_fifo(inode);
 }
 
-static struct buffer_head * minix_update_inode(struct inode * inode)
+/*
+ * The global function to read an inode.
+ */
+void minix_read_inode(struct inode * inode)
+{
+	if (INODE_VERSION(inode) == MINIX_V1)
+		V1_minix_read_inode(inode);
+	else
+		V2_minix_read_inode(inode);
+}
+
+/*
+ * The minix V1 function to synchronize an inode.
+ */
+static struct buffer_head * V1_minix_update_inode(struct inode * inode)
 {
 	struct buffer_head * bh;
 	struct minix_inode * raw_inode;
@@ -485,15 +784,67 @@
 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
 		raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev);
 	else for (block = 0; block < 9; block++)
-		raw_inode->i_zone[block] = inode->u.minix_i.i_data[block];
+		raw_inode->i_zone[block] = inode->u.minix_i.u.i1_data[block];
 	inode->i_dirt=0;
 	mark_buffer_dirty(bh, 1);
 	return bh;
 }
 
+/*
+ * The minix V2 function to synchronize an inode.
+ */
+static struct buffer_head * V2_minix_update_inode(struct inode * inode)
+{
+	struct buffer_head * bh;
+	struct minix2_inode * raw_inode;
+	int ino, block;
+
+	ino = inode->i_ino;
+	if (!ino || ino >= inode->i_sb->u.minix_sb.s_ninodes) {
+		printk("Bad inode number on dev %s"
+		       ": %d is out of range\n",
+			kdevname(inode->i_dev), ino);
+		inode->i_dirt = 0;
+		return 0;
+	}
+	block = 2 + inode->i_sb->u.minix_sb.s_imap_blocks + inode->i_sb->u.minix_sb.s_zmap_blocks +
+		(ino-1)/MINIX2_INODES_PER_BLOCK;
+	if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) {
+		printk("unable to read i-node block\n");
+		inode->i_dirt = 0;
+		return 0;
+	}
+	raw_inode = ((struct minix2_inode *)bh->b_data) +
+		(ino-1)%MINIX2_INODES_PER_BLOCK;
+	raw_inode->i_mode = inode->i_mode;
+	raw_inode->i_uid = inode->i_uid;
+	raw_inode->i_gid = inode->i_gid;
+	raw_inode->i_nlinks = inode->i_nlink;
+	raw_inode->i_size = inode->i_size;
+	raw_inode->i_mtime = inode->i_mtime;
+	raw_inode->i_atime = inode->i_atime;
+	raw_inode->i_ctime = inode->i_ctime;
+	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+		raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev);
+	else for (block = 0; block < 10; block++)
+		raw_inode->i_zone[block] = inode->u.minix_i.u.i2_data[block];
+	inode->i_dirt=0;
+	mark_buffer_dirty(bh, 1);
+	return bh;
+}
+
+struct buffer_head *minix_update_inode(struct inode *inode)
+{
+	if (INODE_VERSION(inode) == MINIX_V1)
+		return V1_minix_update_inode(inode);
+	else
+		return V2_minix_update_inode(inode);
+}
+
 void minix_write_inode(struct inode * inode)
 {
 	struct buffer_head *bh;
+
 	bh = minix_update_inode(inode);
 	brelse(bh);
 }
@@ -539,4 +890,3 @@
 }
 
 #endif
-
diff -u --recursive --new-file v1.3.53/linux/fs/minix/truncate.c linux/fs/minix/truncate.c
--- v1.3.53/linux/fs/minix/truncate.c	Thu Nov  9 11:23:51 1995
+++ linux/fs/minix/truncate.c	Thu Jan  4 14:07:58 1996
@@ -2,6 +2,9 @@
  *  linux/fs/truncate.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Copyright (C) 1996  Gertjan van Wingerde (gertjan@cs.vu.nl)
+ *	Minix V2 fs support.
  */
 
 #include <linux/errno.h>
@@ -10,6 +13,11 @@
 #include <linux/stat.h>
 #include <linux/fcntl.h>
 
+#define DIRECT_BLOCK		((inode->i_size + 1023) >> 10)
+#define INDIRECT_BLOCK(offset)	(DIRECT_BLOCK-offset)
+#define DINDIRECT_BLOCK(offset) ((DIRECT_BLOCK-offset)>>9)
+#define TINDIRECT_BLOCK(offset)	((DIRECT_BLOCK-(offset))>>9)
+
 /*
  * Truncate has the most races in the whole filesystem: coding it is
  * a pain in the a**. Especially as I don't do any locking...
@@ -23,17 +31,19 @@
  * general case (size = XXX). I hope.
  */
 
-static int trunc_direct(struct inode * inode)
+/*
+ * The functions for minix V1 fs truncation.
+ */
+static int V1_trunc_direct(struct inode * inode)
 {
 	unsigned short * p;
 	struct buffer_head * bh;
 	int i, tmp;
 	int retry = 0;
-#define DIRECT_BLOCK ((inode->i_size + 1023) >> 10)
 
 repeat:
 	for (i = DIRECT_BLOCK ; i < 7 ; i++) {
-		p = i + inode->u.minix_i.i_data;
+		p = i + inode->u.minix_i.u.i1_data;
 		if (!(tmp = *p))
 			continue;
 		bh = get_hash_table(inode->i_dev,tmp,BLOCK_SIZE);
@@ -57,14 +67,13 @@
 	return retry;
 }
 
-static int trunc_indirect(struct inode * inode, int offset, unsigned short * p)
+static int V1_trunc_indirect(struct inode * inode, int offset, unsigned short * p)
 {
 	struct buffer_head * bh;
 	int i, tmp;
 	struct buffer_head * ind_bh;
 	unsigned short * ind;
 	int retry = 0;
-#define INDIRECT_BLOCK (DIRECT_BLOCK-offset)
 
 	tmp = *p;
 	if (!tmp)
@@ -79,17 +88,17 @@
 		return 0;
 	}
 repeat:
-	for (i = INDIRECT_BLOCK ; i < 512 ; i++) {
+	for (i = INDIRECT_BLOCK(offset) ; i < 512 ; i++) {
 		if (i < 0)
 			i = 0;
-		if (i < INDIRECT_BLOCK)
+		if (i < INDIRECT_BLOCK(offset))
 			goto repeat;
 		ind = i+(unsigned short *) ind_bh->b_data;
 		tmp = *ind;
 		if (!tmp)
 			continue;
 		bh = get_hash_table(inode->i_dev,tmp,BLOCK_SIZE);
-		if (i < INDIRECT_BLOCK) {
+		if (i < INDIRECT_BLOCK(offset)) {
 			brelse(bh);
 			goto repeat;
 		}
@@ -118,16 +127,14 @@
 	brelse(ind_bh);
 	return retry;
 }
-		
-static int trunc_dindirect(struct inode * inode)
+
+static int V1_trunc_dindirect(struct inode * inode, int offset, unsigned short *p)
 {
 	int i, tmp;
 	struct buffer_head * dind_bh;
-	unsigned short * dind, * p;
+	unsigned short * dind;
 	int retry = 0;
-#define DINDIRECT_BLOCK ((DIRECT_BLOCK-(512+7))>>9)
 
-	p = 8 + inode->u.minix_i.i_data;
 	if (!(tmp = *p))
 		return 0;
 	dind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE);
@@ -140,13 +147,13 @@
 		return 0;
 	}
 repeat:
-	for (i = DINDIRECT_BLOCK ; i < 512 ; i ++) {
+	for (i = DINDIRECT_BLOCK(offset) ; i < 512 ; i ++) {
 		if (i < 0)
 			i = 0;
-		if (i < DINDIRECT_BLOCK)
+		if (i < DINDIRECT_BLOCK(offset))
 			goto repeat;
 		dind = i+(unsigned short *) dind_bh->b_data;
-		retry |= trunc_indirect(inode,7+512+(i<<9),dind);
+		retry |= V1_trunc_indirect(inode,offset+(i<<9),dind);
 		mark_buffer_dirty(dind_bh, 1);
 	}
 	dind = (unsigned short *) dind_bh->b_data;
@@ -165,8 +172,215 @@
 	brelse(dind_bh);
 	return retry;
 }
-		
-void minix_truncate(struct inode * inode)
+
+void V1_minix_truncate(struct inode * inode)
+{
+	int retry;
+
+	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+	     S_ISLNK(inode->i_mode)))
+		return;
+	while (1) {
+		retry = V1_trunc_direct(inode);
+		retry |= V1_trunc_indirect(inode, 7, inode->u.minix_i.u.i1_data + 7);
+		retry |= V1_trunc_dindirect(inode, 7+512, inode->u.minix_i.u.i1_data + 8);
+		if (!retry)
+			break;
+		current->counter = 0;
+		schedule();
+	}
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	inode->i_dirt = 1;
+}
+
+/*
+ * The functions for minix V2 fs truncation.
+ */
+static int V2_trunc_direct(struct inode * inode)
+{
+	unsigned long * p;
+	struct buffer_head * bh;
+	int i, tmp;
+	int retry = 0;
+
+repeat:
+	for (i = DIRECT_BLOCK ; i < 7 ; i++) {
+		p = (unsigned long *) inode->u.minix_i.u.i2_data + i;
+		if (!(tmp = *p))
+			continue;
+		bh = get_hash_table(inode->i_dev,tmp,BLOCK_SIZE);
+		if (i < DIRECT_BLOCK) {
+			brelse(bh);
+			goto repeat;
+		}
+		if ((bh && bh->b_count != 1) || tmp != *p) {
+			retry = 1;
+			brelse(bh);
+			continue;
+		}
+		*p = 0;
+		inode->i_dirt = 1;
+		if (bh) {
+			mark_buffer_clean(bh);
+			brelse(bh);
+		}
+		minix_free_block(inode->i_sb,tmp);
+	}
+	return retry;
+}
+
+static int V2_trunc_indirect(struct inode * inode, int offset, unsigned long * p)
+{
+	struct buffer_head * bh;
+	int i, tmp;
+	struct buffer_head * ind_bh;
+	unsigned long * ind;
+	int retry = 0;
+
+	tmp = *p;
+	if (!tmp)
+		return 0;
+	ind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE);
+	if (tmp != *p) {
+		brelse(ind_bh);
+		return 1;
+	}
+	if (!ind_bh) {
+		*p = 0;
+		return 0;
+	}
+repeat:
+	for (i = INDIRECT_BLOCK(offset) ; i < 256 ; i++) {
+		if (i < 0)
+			i = 0;
+		if (i < INDIRECT_BLOCK(offset))
+			goto repeat;
+		ind = i+(unsigned long *) ind_bh->b_data;
+		tmp = *ind;
+		if (!tmp)
+			continue;
+		bh = get_hash_table(inode->i_dev,tmp,BLOCK_SIZE);
+		if (i < INDIRECT_BLOCK(offset)) {
+			brelse(bh);
+			goto repeat;
+		}
+		if ((bh && bh->b_count != 1) || tmp != *ind) {
+			retry = 1;
+			brelse(bh);
+			continue;
+		}
+		*ind = 0;
+		mark_buffer_dirty(ind_bh, 1);
+		brelse(bh);
+		minix_free_block(inode->i_sb,tmp);
+	}
+	ind = (unsigned long *) ind_bh->b_data;
+	for (i = 0; i < 256; i++)
+		if (*(ind++))
+			break;
+	if (i >= 256)
+		if (ind_bh->b_count != 1)
+			retry = 1;
+		else {
+			tmp = *p;
+			*p = 0;
+			minix_free_block(inode->i_sb,tmp);
+		}
+	brelse(ind_bh);
+	return retry;
+}
+
+static int V2_trunc_dindirect(struct inode * inode, int offset, unsigned long *p)
+{
+	int i, tmp;
+	struct buffer_head * dind_bh;
+	unsigned long * dind;
+	int retry = 0;
+
+	if (!(tmp = *p))
+		return 0;
+	dind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE);
+	if (tmp != *p) {
+		brelse(dind_bh);
+		return 1;
+	}
+	if (!dind_bh) {
+		*p = 0;
+		return 0;
+	}
+repeat:
+	for (i = DINDIRECT_BLOCK(offset) ; i < 256 ; i ++) {
+		if (i < 0)
+			i = 0;
+		if (i < DINDIRECT_BLOCK(offset))
+			goto repeat;
+		dind = i+(unsigned long *) dind_bh->b_data;
+		retry |= V2_trunc_indirect(inode,offset+(i<<9),dind);
+		mark_buffer_dirty(dind_bh, 1);
+	}
+	dind = (unsigned long *) dind_bh->b_data;
+	for (i = 0; i < 256; i++)
+		if (*(dind++))
+			break;
+	if (i >= 256)
+		if (dind_bh->b_count != 1)
+			retry = 1;
+		else {
+			tmp = *p;
+			*p = 0;
+			inode->i_dirt = 1;
+			minix_free_block(inode->i_sb,tmp);
+		}
+	brelse(dind_bh);
+	return retry;
+}
+
+static int V2_trunc_tindirect(struct inode * inode, int offset, unsigned long * p)
+{
+        int i, tmp;
+        struct buffer_head * tind_bh;
+        unsigned long * tind;
+        int retry = 0;
+
+        if (!(tmp = *p))
+                return 0;
+        tind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE);
+        if (tmp != *p) {
+                brelse(tind_bh);
+                return 1;
+	}
+        if (!tind_bh) {
+                *p = 0;
+                return 0;
+	}
+repeat:
+        for (i = TINDIRECT_BLOCK(offset) ; i < 256 ; i ++) {
+                if (i < 0)
+                        i = 0;
+                if (i < TINDIRECT_BLOCK(offset))
+                        goto repeat;
+                tind = i+(unsigned long *) tind_bh->b_data;
+                retry |= V2_trunc_dindirect(inode,offset+(i<<9),tind);
+                mark_buffer_dirty(tind_bh, 1);
+	}
+        tind = (unsigned long *) tind_bh->b_data;
+        for (i = 0; i < 256; i++)
+                if (*(tind++))
+                        break;
+        if (i >= 256)
+                if (tind_bh->b_count != 1)
+                        retry = 1;
+                else {
+                        tmp = *p;
+                        *p = 0;
+                        inode->i_dirt = 1;
+                        minix_free_block(inode->i_sb,tmp);
+		}
+        brelse(tind_bh);
+        return retry;
+}
+
+static void V2_minix_truncate(struct inode * inode)
 {
 	int retry;
 
@@ -174,9 +388,13 @@
 	     S_ISLNK(inode->i_mode)))
 		return;
 	while (1) {
-		retry = trunc_direct(inode);
-		retry |= trunc_indirect(inode,7,inode->u.minix_i.i_data+7);
-		retry |= trunc_dindirect(inode);
+		retry = V2_trunc_direct(inode);
+		retry |= V2_trunc_indirect(inode,7,
+			(unsigned long *) inode->u.minix_i.u.i2_data + 7);
+		retry |= V2_trunc_dindirect(inode, 7+256, 
+			(unsigned long *) inode->u.minix_i.u.i2_data + 8);
+		retry |= V2_trunc_tindirect(inode, 7+256+256*256, 
+			(unsigned long *) inode->u.minix_i.u.i2_data + 9);
 		if (!retry)
 			break;
 		current->counter = 0;
@@ -184,4 +402,15 @@
 	}
 	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 	inode->i_dirt = 1;
+}
+
+/*
+ * The function that is called for file truncation.
+ */
+void minix_truncate(struct inode * inode)
+{
+	if (INODE_VERSION(inode) == MINIX_V1)
+		V1_minix_truncate(inode);
+	else
+		V2_minix_truncate(inode);
 }
diff -u --recursive --new-file v1.3.53/linux/fs/msdos/file.c linux/fs/msdos/file.c
--- v1.3.53/linux/fs/msdos/file.c	Tue Dec 26 04:45:38 1995
+++ linux/fs/msdos/file.c	Thu Jan  4 20:26:24 1996
@@ -52,7 +52,7 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
-	NULL,			/* readpage */
+	generic_readpage,	/* readpage */
 	NULL,			/* writepage */
 	msdos_bmap,		/* bmap */
 	msdos_truncate,		/* truncate */
diff -u --recursive --new-file v1.3.53/linux/fs/ncpfs/file.c linux/fs/ncpfs/file.c
--- v1.3.53/linux/fs/ncpfs/file.c	Tue Jan  2 16:46:28 1996
+++ linux/fs/ncpfs/file.c	Wed Jan  3 13:52:56 1996
@@ -1,7 +1,7 @@
 /*
  *  file.c
  *
- *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *  Copyright (C) 1995 by Volker Lendecke
  *
  */
 
@@ -82,7 +82,7 @@
 static int 
 ncp_file_read(struct inode *inode, struct file *file, char *buf, int count)
 {
-	int bufsize, to_read, already_read;
+	int bufsize, already_read;
 	off_t pos;
         int errno;
 
@@ -126,17 +126,8 @@
         while (already_read < count)
 	{
 		int read_this_time;
-
-		if ((pos % bufsize) != 0)
-		{
-			to_read = bufsize - (pos % bufsize);
-		}
-		else
-		{
-			to_read = bufsize;
-		}
-
-		to_read = min(to_read, count - already_read);
+		int to_read = min(bufsize - (pos % bufsize),
+				  count - already_read);
 
 		if (ncp_read(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
 			     pos, to_read, buf, &read_this_time) != 0)
@@ -156,7 +147,11 @@
 
         file->f_pos = pos;
 
-	if (!IS_RDONLY(inode)) inode->i_atime = CURRENT_TIME;
+	if (!IS_RDONLY(inode))
+	{
+		inode->i_atime = CURRENT_TIME;
+	}
+
 	inode->i_dirt = 1;
 
         DPRINTK("ncp_file_read: exit %s\n", NCP_ISTRUCT(inode)->entryName);
@@ -168,7 +163,7 @@
 ncp_file_write(struct inode *inode, struct file *file, const char *buf,
 	       int count)
 {
-	int bufsize, to_write, already_written;
+	int bufsize, already_written;
         off_t pos;
         int errno;
 			  
@@ -211,17 +206,9 @@
         while (already_written < count)
 	{
 		int written_this_time;
+		int to_write = min(bufsize - (pos % bufsize),
+				   count - already_written);
 
-		if ((pos % bufsize) != 0)
-		{
-			to_write = bufsize - (pos % bufsize);
-		}
-		else
-		{
-			to_write = bufsize;
-		}
-
-		to_write = min(to_write, count - already_written);
 		if (ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
 			      pos, to_write, buf, &written_this_time) != 0)
 		{
diff -u --recursive --new-file v1.3.53/linux/fs/ncpfs/ioctl.c linux/fs/ncpfs/ioctl.c
--- v1.3.53/linux/fs/ncpfs/ioctl.c	Tue Jan  2 16:46:28 1996
+++ linux/fs/ncpfs/ioctl.c	Wed Jan  3 13:52:56 1996
@@ -20,14 +20,16 @@
 {
 	int result;
 	struct ncp_ioctl_request request;
-	struct ncp_server *server;
+	struct ncp_fs_info info;
+	struct ncp_server *server = NCP_SERVER(inode);
 
 	switch(cmd) {
 	case NCP_IOC_NCPREQUEST:
 
-		if (!suser())
+		if (   (permission(inode, MAY_WRITE) != 0)
+		    && (current->uid != server->m.mounted_uid))
 		{
-			return -EPERM;
+			return -EACCES;
 		}
 		
 		if ((result = verify_area(VERIFY_READ, (char *)arg,
@@ -52,37 +54,70 @@
 			return result;
 		}
 
-		server = NCP_SERVER(inode);
 		ncp_lock_server(server);
 
 		/* FIXME: We hack around in the server's structures
                    here to be able to use ncp_request */
 
 		server->has_subfunction = 0;
-		server->current_size =
-			request.size + sizeof(struct ncp_request_header);
-		memcpy_fromfs(server->packet, request.data,
-			      request.size+sizeof(struct ncp_request_header));
+		server->current_size = request.size;
+		memcpy_fromfs(server->packet, request.data, request.size);
 
-		
 		ncp_request(server, request.function);
 
 		DPRINTK("ncp_ioctl: copy %d bytes\n",
 			server->reply_size);
-		memcpy_tofs(request.data, server->packet,
-			    server->reply_size);
+		memcpy_tofs(request.data, server->packet, server->reply_size);
 
 		ncp_unlock_server(server);
 
 		return server->reply_size;
 
+	case NCP_IOC_GET_FS_INFO:
+
+		if (   (permission(inode, MAY_WRITE) != 0)
+		    && (current->uid != server->m.mounted_uid))
+		{
+			return -EACCES;
+		}
+		
+		if ((result = verify_area(VERIFY_WRITE, (char *)arg,
+					  sizeof(info))) != 0)
+		{
+			return result;
+		}
+
+		memcpy_fromfs(&info, (struct ncp_fs_info *)arg,
+			      sizeof(info));
+
+		if (info.version != NCP_GET_FS_INFO_VERSION)
+		{
+			DPRINTK("info.version invalid: %d\n", info.version);
+			return -EINVAL;
+		}
+
+		info.addr        = server->m.serv_addr;
+		info.mounted_uid = server->m.mounted_uid;
+		info.connection  = server->connection;
+		info.buffer_size = server->buffer_size;
+
+		memcpy_tofs((struct ncp_fs_info *)arg, &info, sizeof(info));
+		return 0;		
+
         case NCP_IOC_GETMOUNTUID:
+
+		if (   (permission(inode, MAY_READ) != 0)
+		    && (current->uid != server->m.mounted_uid))
+		{
+			return -EACCES;
+		}
+		
                 if ((result = verify_area(VERIFY_WRITE, (uid_t*) arg,
                                           sizeof(uid_t))) != 0)
 		{
                         return result;
                 }
-                put_fs_word(NCP_SERVER(inode)->m.mounted_uid, (uid_t*) arg);
+                put_fs_word(server->m.mounted_uid, (uid_t*) arg);
                 return 0;
 
 	default:
diff -u --recursive --new-file v1.3.53/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v1.3.53/linux/fs/nfs/inode.c	Thu Nov  9 11:23:51 1995
+++ linux/fs/nfs/inode.c	Wed Jan  3 09:39:00 1996
@@ -268,13 +268,13 @@
 	else
 		sattr.size = (unsigned) -1;
 
-	if (attr->ia_valid & ATTR_MTIME) {
+	if (attr->ia_valid & ATTR_MTIME_SET) {
 		sattr.mtime.seconds = attr->ia_mtime;
 		sattr.mtime.useconds = 0;
 	} else 
 		sattr.mtime.seconds = sattr.mtime.useconds = (unsigned) -1;
 
-	if (attr->ia_valid & ATTR_ATIME) {
+	if (attr->ia_valid & ATTR_ATIME_SET) {
 		sattr.atime.seconds = attr->ia_atime;
 		sattr.atime.useconds = 0;
 	} else
diff -u --recursive --new-file v1.3.53/linux/fs/open.c linux/fs/open.c
--- v1.3.53/linux/fs/open.c	Sat Dec 30 15:50:54 1995
+++ linux/fs/open.c	Wed Jan  3 10:00:15 1996
@@ -66,22 +66,18 @@
 	int error;
 	struct iattr newattrs;
 
+	down(&inode->i_sem);
 	newattrs.ia_size = length;
-	newattrs.ia_ctime = newattrs.ia_mtime = CURRENT_TIME;
 	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME | ATTR_MTIME;
 	error = notify_change(inode, &newattrs);
-	if (error)
-		return error;
-
-	/* truncate virtual mappings of this file */
-	down(&inode->i_sem);
-	vmtruncate(inode, length);
-	inode->i_size = length;
-	inode->i_dirt = 1;
-	if (inode->i_op && inode->i_op->truncate)
-		inode->i_op->truncate(inode);
+	if (!error) {
+		/* truncate virtual mappings of this file */
+		vmtruncate(inode, length);
+		if (inode->i_op && inode->i_op->truncate)
+			inode->i_op->truncate(inode);
+	}
 	up(&inode->i_sem);
-	return 0;
+	return error;
 }
 
 asmlinkage int sys_truncate(const char * path, unsigned long length)
@@ -141,10 +137,8 @@
  */
 asmlinkage int sys_utime(char * filename, struct utimbuf * times)
 {
-	struct inode * inode;
-	long actime,modtime;
 	int error;
-	unsigned int flags = 0;
+	struct inode * inode;
 	struct iattr newattrs;
 
 	error = namei(filename,&inode);
@@ -155,27 +149,22 @@
 		return -EROFS;
 	}
 	/* Don't worry, the checks are done in inode_change_ok() */
+	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
 	if (times) {
 		error = verify_area(VERIFY_READ, times, sizeof(*times));
 		if (error) {
 			iput(inode);
 			return error;
 		}
-		actime = get_user(&times->actime);
-		modtime = get_user(&times->modtime);
-		newattrs.ia_ctime = CURRENT_TIME;
-		flags = ATTR_ATIME_SET | ATTR_MTIME_SET;
+		newattrs.ia_atime = get_user(&times->actime);
+		newattrs.ia_mtime = get_user(&times->modtime);
+		newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
 	} else {
 		if ((error = permission(inode,MAY_WRITE)) != 0) {
 			iput(inode);
 			return error;
 		}
-		actime = modtime = newattrs.ia_ctime = CURRENT_TIME;
 	}
-	newattrs.ia_atime = actime;
-	newattrs.ia_mtime = modtime;
-	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME | flags;
-	inode->i_dirt = 1;
 	error = notify_change(inode, &newattrs);
 	iput(inode);
 	return error;
@@ -187,10 +176,8 @@
  */
 asmlinkage int sys_utimes(char * filename, struct timeval * utimes)
 {
-	struct inode * inode;
-	long actime,modtime;
 	int error;
-	unsigned int flags = 0;
+	struct inode * inode;
 	struct iattr newattrs;
 
 	error = namei(filename,&inode);
@@ -201,6 +188,7 @@
 		return -EROFS;
 	}
 	/* Don't worry, the checks are done in inode_change_ok() */
+	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
 	if (utimes) {
 		struct timeval times[2];
 		error = verify_area(VERIFY_READ, utimes, sizeof(times));
@@ -209,21 +197,15 @@
 			return error;
 		}
 		memcpy_fromfs(&times, utimes, sizeof(times));
-		actime = times[0].tv_sec;
-		modtime = times[1].tv_sec;
-		newattrs.ia_ctime = CURRENT_TIME;
-		flags = ATTR_ATIME_SET | ATTR_MTIME_SET;
+		newattrs.ia_atime = times[0].tv_sec;
+		newattrs.ia_mtime = times[1].tv_sec;
+		newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
 	} else {
 		if ((error = permission(inode,MAY_WRITE)) != 0) {
 			iput(inode);
 			return error;
 		}
-		actime = modtime = newattrs.ia_ctime = CURRENT_TIME;
 	}
-	newattrs.ia_atime = actime;
-	newattrs.ia_mtime = modtime;
-	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME | flags;
-	inode->i_dirt = 1;
 	error = notify_change(inode, &newattrs);
 	iput(inode);
 	return error;
@@ -334,7 +316,6 @@
 	if (mode == (mode_t) -1)
 		mode = inode->i_mode;
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
-	newattrs.ia_ctime = CURRENT_TIME;
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
 	inode->i_dirt = 1;
 	return notify_change(inode, &newattrs);
@@ -360,7 +341,6 @@
 	if (mode == (mode_t) -1)
 		mode = inode->i_mode;
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
-	newattrs.ia_ctime = CURRENT_TIME;
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
 	inode->i_dirt = 1;
 	error = notify_change(inode, &newattrs);
@@ -390,7 +370,6 @@
 	newattrs.ia_mode = inode->i_mode;
 	newattrs.ia_uid = user;
 	newattrs.ia_gid = group;
-	newattrs.ia_ctime = CURRENT_TIME;
 	newattrs.ia_valid =  ATTR_UID | ATTR_GID | ATTR_CTIME;
 	/*
 	 * If the owner has been changed, remove the setuid bit
@@ -443,7 +422,6 @@
 	newattrs.ia_mode = inode->i_mode;
 	newattrs.ia_uid = user;
 	newattrs.ia_gid = group;
-	newattrs.ia_ctime = CURRENT_TIME;
 	newattrs.ia_valid =  ATTR_UID | ATTR_GID | ATTR_CTIME;
 	/*
 	 * If the owner has been changed, remove the setuid bit
diff -u --recursive --new-file v1.3.53/linux/fs/sysv/file.c linux/fs/sysv/file.c
--- v1.3.53/linux/fs/sysv/file.c	Tue Dec 26 04:45:40 1995
+++ linux/fs/sysv/file.c	Thu Jan  4 20:26:24 1996
@@ -64,7 +64,7 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
-	NULL,			/* readpage */
+	generic_readpage,	/* readpage */
 	NULL,			/* writepage */
 	sysv_bmap,		/* bmap */
 	sysv_truncate,		/* truncate */
diff -u --recursive --new-file v1.3.53/linux/fs/umsdos/file.c linux/fs/umsdos/file.c
--- v1.3.53/linux/fs/umsdos/file.c	Tue Dec 26 04:45:40 1995
+++ linux/fs/umsdos/file.c	Thu Jan  4 20:26:25 1996
@@ -87,7 +87,7 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
-	NULL,			/* readpage */
+	generic_readpage,	/* readpage */
 	NULL,			/* writepage */
 	msdos_bmap,		/* bmap */
 	UMSDOS_truncate,/* truncate */
diff -u --recursive --new-file v1.3.53/linux/fs/xiafs/file.c linux/fs/xiafs/file.c
--- v1.3.53/linux/fs/xiafs/file.c	Tue Dec 26 04:45:40 1995
+++ linux/fs/xiafs/file.c	Thu Jan  4 20:26:25 1996
@@ -61,7 +61,7 @@
     NULL,			/* rename */
     NULL,			/* readlink */
     NULL,			/* follow_link */
-    NULL,			/* readpage */
+    generic_readpage,		/* readpage */
     NULL,			/* writepage */
     xiafs_bmap,			/* bmap */
     xiafs_truncate,		/* truncate */
diff -u --recursive --new-file v1.3.53/linux/include/asm-alpha/pgtable.h linux/include/asm-alpha/pgtable.h
--- v1.3.53/linux/include/asm-alpha/pgtable.h	Sun Dec 17 11:43:22 1995
+++ linux/include/asm-alpha/pgtable.h	Tue Jan  2 16:47:00 1996
@@ -112,7 +112,6 @@
 #define _PAGE_UWE	0x2000	/* used to do the dirty bit in software */
 
 /* .. and these are ours ... */
-#define _PAGE_COW	0x10000
 #define _PAGE_DIRTY	0x20000
 #define _PAGE_ACCESSED	0x40000
 
@@ -141,13 +140,13 @@
  */
 #define PAGE_NONE	__pgprot(_PAGE_VALID | __ACCESS_BITS | _PAGE_FOR | _PAGE_FOW | _PAGE_FOE)
 #define PAGE_SHARED	__pgprot(_PAGE_VALID | __ACCESS_BITS)
-#define PAGE_COPY	__pgprot(_PAGE_VALID | __ACCESS_BITS | _PAGE_FOW | _PAGE_COW)
+#define PAGE_COPY	__pgprot(_PAGE_VALID | __ACCESS_BITS | _PAGE_FOW)
 #define PAGE_READONLY	__pgprot(_PAGE_VALID | __ACCESS_BITS | _PAGE_FOW)
 #define PAGE_KERNEL	__pgprot(_PAGE_VALID | _PAGE_ASM | _PAGE_KRE | _PAGE_KWE)
 
 #define _PAGE_NORMAL(x) __pgprot(_PAGE_VALID | __ACCESS_BITS | (x))
 
-#define _PAGE_P(x) _PAGE_NORMAL((x) | (((x) & _PAGE_FOW)?0:(_PAGE_FOW | _PAGE_COW)))
+#define _PAGE_P(x) _PAGE_NORMAL((x) | (((x) & _PAGE_FOW)?0:_PAGE_FOW))
 #define _PAGE_S(x) _PAGE_NORMAL(x)
 
 /*
@@ -268,20 +267,17 @@
 extern inline int pte_exec(pte_t pte)		{ return !(pte_val(pte) & _PAGE_FOE); }
 extern inline int pte_dirty(pte_t pte)		{ return pte_val(pte) & _PAGE_DIRTY; }
 extern inline int pte_young(pte_t pte)		{ return pte_val(pte) & _PAGE_ACCESSED; }
-extern inline int pte_cow(pte_t pte)		{ return pte_val(pte) & _PAGE_COW; }
 
 extern inline pte_t pte_wrprotect(pte_t pte)	{ pte_val(pte) |= _PAGE_FOW; return pte; }
 extern inline pte_t pte_rdprotect(pte_t pte)	{ pte_val(pte) |= _PAGE_FOR; return pte; }
 extern inline pte_t pte_exprotect(pte_t pte)	{ pte_val(pte) |= _PAGE_FOE; return pte; }
 extern inline pte_t pte_mkclean(pte_t pte)	{ pte_val(pte) &= ~(__DIRTY_BITS); return pte; }
 extern inline pte_t pte_mkold(pte_t pte)	{ pte_val(pte) &= ~(__ACCESS_BITS); return pte; }
-extern inline pte_t pte_uncow(pte_t pte)	{ pte_val(pte) &= ~_PAGE_COW; return pte; }
 extern inline pte_t pte_mkwrite(pte_t pte)	{ pte_val(pte) &= ~_PAGE_FOW; return pte; }
 extern inline pte_t pte_mkread(pte_t pte)	{ pte_val(pte) &= ~_PAGE_FOR; return pte; }
 extern inline pte_t pte_mkexec(pte_t pte)	{ pte_val(pte) &= ~_PAGE_FOE; return pte; }
 extern inline pte_t pte_mkdirty(pte_t pte)	{ pte_val(pte) |= __DIRTY_BITS; return pte; }
 extern inline pte_t pte_mkyoung(pte_t pte)	{ pte_val(pte) |= __ACCESS_BITS; return pte; }
-extern inline pte_t pte_mkcow(pte_t pte)	{ pte_val(pte) |= _PAGE_COW; return pte; }
 
 /* 
  * To set the page-dir. Note the self-mapping in the last entry
diff -u --recursive --new-file v1.3.53/linux/include/asm-i386/byteorder.h linux/include/asm-i386/byteorder.h
--- v1.3.53/linux/include/asm-i386/byteorder.h	Tue Jan  2 16:46:29 1996
+++ linux/include/asm-i386/byteorder.h	Wed Jan  3 20:36:22 1996
@@ -14,6 +14,11 @@
 #define __LITTLE_ENDIAN_BITFIELD
 #endif
 
+/* For avoiding bswap on i386 */
+#ifdef __KERNEL__
+#include <linux/config.h>
+#endif
+
 extern unsigned long int	ntohl(unsigned long int);
 extern unsigned short int	ntohs(unsigned short int);
 extern unsigned long int	htonl(unsigned long int);
diff -u --recursive --new-file v1.3.53/linux/include/asm-i386/pgtable.h linux/include/asm-i386/pgtable.h
--- v1.3.53/linux/include/asm-i386/pgtable.h	Tue Jan  2 16:46:29 1996
+++ linux/include/asm-i386/pgtable.h	Tue Jan  2 16:47:00 1996
@@ -153,14 +153,13 @@
 #define _PAGE_ACCESSED	0x020
 #define _PAGE_DIRTY	0x040
 #define _PAGE_4M	0x080	/* 4 MB page, Pentium+.. */
-#define _PAGE_COW	0x200	/* implemented in software (one of the AVL bits) */
 
 #define _PAGE_TABLE	(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY)
 #define _PAGE_CHG_MASK	(PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
 
 #define PAGE_NONE	__pgprot(_PAGE_PRESENT | _PAGE_ACCESSED)
 #define PAGE_SHARED	__pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED)
-#define PAGE_COPY	__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED | _PAGE_COW)
+#define PAGE_COPY	__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
 #define PAGE_READONLY	__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
 #define PAGE_KERNEL	__pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
 
@@ -275,20 +274,17 @@
 extern inline int pte_exec(pte_t pte)		{ return pte_val(pte) & _PAGE_USER; }
 extern inline int pte_dirty(pte_t pte)		{ return pte_val(pte) & _PAGE_DIRTY; }
 extern inline int pte_young(pte_t pte)		{ return pte_val(pte) & _PAGE_ACCESSED; }
-extern inline int pte_cow(pte_t pte)		{ return pte_val(pte) & _PAGE_COW; }
 
 extern inline pte_t pte_wrprotect(pte_t pte)	{ pte_val(pte) &= ~_PAGE_RW; return pte; }
 extern inline pte_t pte_rdprotect(pte_t pte)	{ pte_val(pte) &= ~_PAGE_USER; return pte; }
 extern inline pte_t pte_exprotect(pte_t pte)	{ pte_val(pte) &= ~_PAGE_USER; return pte; }
 extern inline pte_t pte_mkclean(pte_t pte)	{ pte_val(pte) &= ~_PAGE_DIRTY; return pte; }
 extern inline pte_t pte_mkold(pte_t pte)	{ pte_val(pte) &= ~_PAGE_ACCESSED; return pte; }
-extern inline pte_t pte_uncow(pte_t pte)	{ pte_val(pte) &= ~_PAGE_COW; return pte; }
 extern inline pte_t pte_mkwrite(pte_t pte)	{ pte_val(pte) |= _PAGE_RW; return pte; }
 extern inline pte_t pte_mkread(pte_t pte)	{ pte_val(pte) |= _PAGE_USER; return pte; }
 extern inline pte_t pte_mkexec(pte_t pte)	{ pte_val(pte) |= _PAGE_USER; return pte; }
 extern inline pte_t pte_mkdirty(pte_t pte)	{ pte_val(pte) |= _PAGE_DIRTY; return pte; }
 extern inline pte_t pte_mkyoung(pte_t pte)	{ pte_val(pte) |= _PAGE_ACCESSED; return pte; }
-extern inline pte_t pte_mkcow(pte_t pte)	{ pte_val(pte) |= _PAGE_COW; return pte; }
 
 /*
  * Conversion functions: convert a page and protection to a page entry,
diff -u --recursive --new-file v1.3.53/linux/include/asm-i386/smp_lock.h linux/include/asm-i386/smp_lock.h
--- v1.3.53/linux/include/asm-i386/smp_lock.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-i386/smp_lock.h	Wed Jan  3 20:36:23 1996
@@ -0,0 +1,67 @@
+#ifndef __LINUX_SMPLOCK_H
+#define __LINUX_SMPLOCK_H
+
+#ifdef __SMP__
+
+/*
+ *	Locking the kernel 
+ */
+ 
+extern __inline void lock_kernel(void)
+{
+	unsigned long flags;
+	int proc = smp_processor_id();
+
+	save_flags(flags);
+	cli();
+	/* set_bit works atomic in SMP machines */
+	while(set_bit(0, (void *)&kernel_flag)) 
+	{
+		/*
+		 *	We just start another level if we have the lock 
+		 */
+		if (proc == active_kernel_processor)
+			break;
+		do 
+		{
+			smp_spins++;
+			/*
+			 *	Doing test_bit here doesn't lock the bus 
+			 */
+			if (test_bit(proc, (void *)&smp_invalidate_needed))
+				if (clear_bit(proc, (void *)&smp_invalidate_needed))
+					local_invalidate();
+		}
+		while(test_bit(0, (void *)&kernel_flag));
+	}
+	/* 
+	 *	We got the lock, so tell the world we are here and increment
+	 *	the level counter 
+	 */
+	active_kernel_processor = proc;
+	kernel_counter++;
+	restore_flags(flags);
+}
+
+extern __inline void unlock_kernel(void)
+{
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	/*
+	 *	If it's the last level we have in the kernel, then
+	 *	free the lock 
+	 */
+	if (kernel_counter == 0)
+		panic("Kernel counter wrong.\n"); /* FIXME: Why is kernel_counter sometimes 0 here? */
+	
+	if(! --kernel_counter) 
+	{
+		active_kernel_processor = NO_PROC_ID;
+		clear_bit(0, (void *)&kernel_flag);
+	}
+	restore_flags(flags);
+}
+
+#endif
+#endif
diff -u --recursive --new-file v1.3.53/linux/include/linux/fdreg.h linux/include/linux/fdreg.h
--- v1.3.53/linux/include/linux/fdreg.h	Thu Jul 13 16:20:21 1995
+++ linux/include/linux/fdreg.h	Tue Jan  2 16:47:00 1996
@@ -91,6 +91,8 @@
 #define FD_LOCK			0x94	/* Fifo config lock */
 #define FD_RSEEK_OUT		0x8f	/* seek out (i.e. to lower tracks) */
 #define FD_RSEEK_IN		0xcf	/* seek in (i.e. to higher tracks) */
+#define FD_PARTID		0x18	/* part id ("extended" version cmd) */
+#define FD_SAVE			0x2e	/* save fdc regs for later restore */
 
 /* DMA commands */
 #define DMA_READ	0x46
@@ -104,6 +106,8 @@
 #define FDC_82072	0x40	/* Intel 82072; 8272a + FIFO + DUMPREGS */
 #define FDC_82077_ORIG	0x50	/* Original version of 82077AA, sans LOCK */
 #define FDC_82077	0x52	/* 82077AA-1 */
+#define FDC_82078	0x60	/* 44pin 82078 or 64pin 82078SL */
+#define FDC_82078_1	0x61	/* 82078-1 (2Mbps fdc) */
 
 #define FD_RESET_DELAY 20
 #endif
diff -u --recursive --new-file v1.3.53/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.3.53/linux/include/linux/fs.h	Sat Dec 30 15:50:55 1995
+++ linux/include/linux/fs.h	Thu Jan  4 20:26:25 1996
@@ -404,7 +404,7 @@
 	int (*rename) (struct inode *,const char *,int,struct inode *,const char *,int);
 	int (*readlink) (struct inode *,char *,int);
 	int (*follow_link) (struct inode *,struct inode *,int,int,struct inode **);
-	int (*readpage) (struct inode *, unsigned long, char *);
+	int (*readpage) (struct inode *, struct page *);
 	int (*writepage) (struct inode *, struct page *);
 	int (*bmap) (struct inode *,int);
 	void (*truncate) (struct inode *);
@@ -574,10 +574,14 @@
 }
 extern void set_blocksize(kdev_t dev, int size);
 extern struct buffer_head * bread(kdev_t dev, int block, int size);
-extern int bread_page(unsigned long addr,kdev_t dev,int b[],int size);
-extern void bwrite_page(unsigned long addr,kdev_t dev,int b[],int size);
 extern struct buffer_head * breada(kdev_t dev,int block, int size, 
 				   unsigned int pos, unsigned int filesize);
+
+extern int generic_readpage(struct inode *, struct page *);
+extern int generic_file_read(struct inode *, struct file *, char *, int);
+extern int generic_mmap(struct inode *, struct file *, struct vm_area_struct *);
+
+extern void bwrite_page(unsigned long addr,kdev_t dev,int b[],int size);
 extern void put_super(kdev_t dev);
 unsigned long generate_cluster(kdev_t dev, int b[], int size);
 extern kdev_t ROOT_DEV;
@@ -591,8 +595,6 @@
 
 extern int char_write(struct inode *, struct file *, const char *, int);
 extern int block_write(struct inode *, struct file *, const char *, int);
-
-extern int generic_mmap(struct inode *, struct file *, struct vm_area_struct *);
 
 extern int block_fsync(struct inode *, struct file *);
 extern int file_fsync(struct inode *, struct file *);
diff -u --recursive --new-file v1.3.53/linux/include/linux/minix_fs.h linux/include/linux/minix_fs.h
--- v1.3.53/linux/include/linux/minix_fs.h	Mon Dec 11 15:42:05 1995
+++ linux/include/linux/minix_fs.h	Thu Jan  4 14:07:58 1996
@@ -8,8 +8,7 @@
 /*
  * Thanks to Kees J Bot for sending me the definitions of the new
  * minix filesystem (aka V2) with bigger inodes and 32-bit block
- * pointers. It's not actually implemented yet, but I'll look into
- * it.
+ * pointers.
  */
 
 #define MINIX_ROOT_INO 1
@@ -18,14 +17,21 @@
 #define MINIX_LINK_MAX	250
 
 #define MINIX_I_MAP_SLOTS	8
-#define MINIX_Z_MAP_SLOTS	8
+#define MINIX_Z_MAP_SLOTS	64
 #define MINIX_SUPER_MAGIC	0x137F		/* original minix fs */
 #define MINIX_SUPER_MAGIC2	0x138F		/* minix fs, 30 char names */
-#define NEW_MINIX_SUPER_MAGIC	0x2468		/* minix V2 - not implemented */
+#define MINIX2_SUPER_MAGIC	0x2468		/* minix V2 fs */
+#define MINIX2_SUPER_MAGIC2	0x2478		/* minix V2 fs, 30 char names */
 #define MINIX_VALID_FS		0x0001		/* Clean fs. */
 #define MINIX_ERROR_FS		0x0002		/* fs has errors. */
 
 #define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode)))
+#define MINIX2_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix2_inode)))
+
+#define MINIX_V1		0x0001		/* original minix fs */
+#define MINIX_V2		0x0002		/* minix V2 fs */
+
+#define INODE_VERSION(inode)	inode->i_sb->u.minix_sb.s_version
 
 /*
  * This is the original minix inode layout on disk.
@@ -47,7 +53,7 @@
  * instead of 7+1+1). Also, some previously 8-bit values are
  * now 16-bit. The inode is now 64 bytes instead of 32.
  */
-struct new_minix_inode {
+struct minix2_inode {
 	__u16 i_mode;
 	__u16 i_nlinks;
 	__u16 i_uid;
@@ -72,6 +78,7 @@
 	__u32 s_max_size;
 	__u16 s_magic;
 	__u16 s_state;
+	__u32 s_zones;
 };
 
 struct minix_dir_entry {
diff -u --recursive --new-file v1.3.53/linux/include/linux/minix_fs_i.h linux/include/linux/minix_fs_i.h
--- v1.3.53/linux/include/linux/minix_fs_i.h	Mon Jan  9 07:58:38 1995
+++ linux/include/linux/minix_fs_i.h	Thu Jan  4 14:07:58 1996
@@ -5,7 +5,10 @@
  * minix fs inode data in memory
  */
 struct minix_inode_info {
-	__u16 i_data[16];
+	union {
+		__u16 i1_data[16];
+		__u32 i2_data[16];
+	} u;
 };
 
 #endif
diff -u --recursive --new-file v1.3.53/linux/include/linux/minix_fs_sb.h linux/include/linux/minix_fs_sb.h
--- v1.3.53/linux/include/linux/minix_fs_sb.h	Mon Jan  3 07:56:16 1994
+++ linux/include/linux/minix_fs_sb.h	Thu Jan  4 14:07:58 1996
@@ -13,12 +13,13 @@
 			unsigned long s_log_zone_size;
 			unsigned long s_max_size;
 			struct buffer_head * s_imap[8];
-			struct buffer_head * s_zmap[8];
+			struct buffer_head * s_zmap[64];
 			unsigned long s_dirsize;
 			unsigned long s_namelen;
 			struct buffer_head * s_sbh;
 			struct minix_super_block * s_ms;
 			unsigned short s_mount_state;
+			unsigned short s_version;
 };
 
 #endif
diff -u --recursive --new-file v1.3.53/linux/include/linux/mm.h linux/include/linux/mm.h
--- v1.3.53/linux/include/linux/mm.h	Tue Jan  2 16:46:30 1996
+++ linux/include/linux/mm.h	Thu Jan  4 14:21:28 1996
@@ -110,10 +110,13 @@
 	unsigned int count;
 	unsigned dirty:16,
 		 age:8,
-		 unused:7,
+		 uptodate:1,
+		 error:1,
+		 unused:5,
 		 reserved:1;
 	unsigned long offset;
 	struct inode *inode;
+	struct wait_queue *wait;
 	struct page *write_list;
 	struct page *next, *prev;
 	struct page *next_hash, *prev_hash;
diff -u --recursive --new-file v1.3.53/linux/include/linux/ncp_fs.h linux/include/linux/ncp_fs.h
--- v1.3.53/linux/include/linux/ncp_fs.h	Tue Jan  2 16:46:30 1996
+++ linux/include/linux/ncp_fs.h	Thu Jan  4 14:28:09 1996
@@ -26,8 +26,25 @@
 	char          *data;
 };
 
+struct ncp_fs_info {
+	int    version;
+	struct sockaddr_ipx addr;
+	uid_t  mounted_uid;
+	int    connection;	/* Connection number the server assigned us */
+	int    buffer_size;	/* The negotiated buffer size, to be
+				   used for read/write requests! */
+
+	/* Not used yet, but here some day the namespace numbers will be
+	   stored. */
+	int    volume_number;
+	__u32  directory_id;
+};	
+
 #define	NCP_IOC_NCPREQUEST		_IOR('n', 1, unsigned char *)
 #define	NCP_IOC_GETMOUNTUID		_IOR('u', 1, uid_t)
+
+#define NCP_GET_FS_INFO_VERSION (1)
+#define NCP_IOC_GET_FS_INFO             _IOWR('i', 1, unsigned char *)
 
 /*
  * The packet size to allocate. One page should be enough.
diff -u --recursive --new-file v1.3.53/linux/include/linux/pagemap.h linux/include/linux/pagemap.h
--- v1.3.53/linux/include/linux/pagemap.h	Tue Jan  2 16:46:30 1996
+++ linux/include/linux/pagemap.h	Thu Jan  4 20:26:25 1996
@@ -36,6 +36,7 @@
 		if (page->offset != offset)
 			continue;
 		page->age = PAGE_AGE_VALUE | (page->age >> 1);
+		page->count++;
 		break;
 	}
 	return page;
@@ -94,5 +95,7 @@
 		page->next->prev = page;
 	*p = page;
 }
+
+extern void update_vm_cache(struct inode *, unsigned long, const char *, int);
 
 #endif
diff -u --recursive --new-file v1.3.53/linux/include/linux/random.h linux/include/linux/random.h
--- v1.3.53/linux/include/linux/random.h	Thu Nov  9 11:23:52 1995
+++ linux/include/linux/random.h	Wed Jan  3 14:04:53 1996
@@ -27,7 +27,7 @@
 
 void rand_initialize(void);
 void rand_initialize_irq(int irq);
-void rand_initialize_blkdev(int irq);
+void rand_initialize_blkdev(int irq, int mode);
 
 void add_keyboard_randomness(unsigned char scancode);
 void add_mouse_randomness(__u32 mouse_data);
diff -u --recursive --new-file v1.3.53/linux/include/linux/smp.h linux/include/linux/smp.h
--- v1.3.53/linux/include/linux/smp.h	Wed Nov  8 07:11:42 1995
+++ linux/include/linux/smp.h	Thu Jan  4 14:14:20 1996
@@ -8,7 +8,7 @@
 
 #ifdef __SMP__
 #include <asm/smp.h>
-
+#include <asm/smp_lock.h>
  
 extern void smp_message_pass(int target, int msg, unsigned long data, int wait);
 extern void smp_boot_cpus(void);		/* Boot processor call to load the other CPU's */
@@ -39,5 +39,6 @@
 #define smp_processor_id()		0
 #define smp_message_pass(t,m,d,w)	
 #define smp_threads_ready		1
+#define kernel_lock()
 #endif
 #endif
diff -u --recursive --new-file v1.3.53/linux/init/main.c linux/init/main.c
--- v1.3.53/linux/init/main.c	Tue Jan  2 16:46:31 1996
+++ linux/init/main.c	Wed Jan  3 20:36:23 1996
@@ -537,6 +537,10 @@
 
 	for(i=1;i<smp_num_cpus;i++)
 	{
+		/*
+		 *	We use kernel_thread for the idlers which are
+		 *	unlocked tasks running in kernel space.
+		 */
 		kernel_thread(cpu_idle, NULL, CLONE_PID);
 		/*
 		 *	Assume linear processor numbering
@@ -636,7 +640,11 @@
 #ifdef __SMP__
 	smp_init();
 #endif
-	/* we count on the initial thread going ok */
+	/* 
+	 *	We count on the initial thread going ok 
+	 *	Like idlers init is an unlocked kernel thread, which will
+	 *	make syscalls (and thus be locked).
+	 */
 	kernel_thread(init, NULL, 0);
 /*
  * task[0] is meant to be used as an "idle" task: it may not sleep, but
diff -u --recursive --new-file v1.3.53/linux/kernel/exit.c linux/kernel/exit.c
--- v1.3.53/linux/kernel/exit.c	Sun Dec 17 11:43:33 1995
+++ linux/kernel/exit.c	Wed Jan  3 14:04:52 1996
@@ -516,9 +516,9 @@
 	__exit_fs(current);
 	__exit_sighand(current);
 	exit_thread();
-	exit_notify();
 	current->state = TASK_ZOMBIE;
 	current->exit_code = code;
+	exit_notify();
 #ifdef DEBUG_PROC_TREE
 	audit_ptree();
 #endif
diff -u --recursive --new-file v1.3.53/linux/kernel/printk.c linux/kernel/printk.c
--- v1.3.53/linux/kernel/printk.c	Mon Dec 11 15:42:06 1995
+++ linux/kernel/printk.c	Thu Jan  4 07:35:47 1996
@@ -23,7 +23,7 @@
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
 
-#define LOG_BUF_LEN	4096
+#define LOG_BUF_LEN	8192
 
 static char buf[1024];
 
diff -u --recursive --new-file v1.3.53/linux/mm/filemap.c linux/mm/filemap.c
--- v1.3.53/linux/mm/filemap.c	Tue Jan  2 16:46:31 1996
+++ linux/mm/filemap.c	Thu Jan  4 20:26:25 1996
@@ -84,9 +84,9 @@
 	while (priority-- > 0) {
 		if (page->inode) {
 			unsigned age = page->age;
-			/* if the page is shared, we juvenate it slightly */
+			/* if the page is shared, we juvenate it */
 			if (page->count != 1)
-				age |= PAGE_AGE_VALUE;
+				age |= PAGE_AGE_VALUE << 1;
 			page->age = age >> 1;
 			if (age <= PAGE_AGE_VALUE/2) {
 				remove_page_from_hash_queue(page);
@@ -127,28 +127,174 @@
 }
 
 /*
- * This should be a low-level fs-specific function (ie
- * inode->i_op->readpage).
+ * Update a page cache copy, when we're doing a "write()" system call
+ * See also "update_vm_cache()".
  */
-static int readpage(struct inode * inode, unsigned long offset, char * page)
+void update_vm_cache(struct inode * inode, unsigned long pos, const char * buf, int count)
 {
-	int *p, nr[PAGE_SIZE/512];
-	int i;
+	struct page * page;
 
-	i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
-	offset >>= inode->i_sb->s_blocksize_bits;
-	p = nr;
-	do {
-		*p = inode->i_op->bmap(inode, offset);
-		i--;
-		offset++;
-		p++;
-	} while (i > 0);
-	bread_page((unsigned long) page, inode->i_dev, nr, inode->i_sb->s_blocksize);
+	page = find_page(inode, pos & PAGE_MASK);
+	if (page) {
+		unsigned long addr;
+
+		page->count++;
+		if (!page->uptodate)
+			sleep_on(&page->wait);
+		addr = page_address(page);
+		memcpy((void *) ((pos & ~PAGE_MASK) + addr), buf, count);
+		free_page(addr);
+	}
+}
+
+/*
+ * Find a cached page and wait for it to become up-to-date, return
+ * the page address.
+ *
+ * If no cached page can be found, create one using the supplied
+ * new page instead (and return zero to indicate that we used the
+ * supplied page in doing so).
+ */
+static unsigned long fill_page(struct inode * inode, unsigned long offset, unsigned long newpage)
+{
+	struct page * page;
+
+	page = find_page(inode, offset);
+	if (page) {
+		if (!page->uptodate)
+			sleep_on(&page->wait);
+		return page_address(page);
+	}
+	page = mem_map + MAP_NR(newpage);
+	page->count++;
+	page->uptodate = 0;
+	page->error = 0;
+	page->offset = offset;
+	add_page_to_inode_queue(inode, page);
+	add_page_to_hash_queue(inode, page);
+	inode->i_op->readpage(inode, page);
+	page->uptodate = 1;
+	wake_up(&page->wait);
 	return 0;
 }
 
 /*
+ * Try to read ahead in the file. "page_cache" is a potentially free page
+ * that we could use for the cache (if it is 0 we can try to create one,
+ * this is all overlapped with the IO on the previous page finishing anyway)
+ */
+static unsigned long try_to_read_ahead(struct inode * inode, unsigned long offset, unsigned long page_cache)
+{
+	if (!page_cache)
+		page_cache = __get_free_page(GFP_KERNEL);
+	offset = (offset + PAGE_SIZE) & PAGE_MASK;
+	/*
+	 * read-ahead is not implemented yet, but this is
+	 * where we should start..
+	 */
+	return page_cache;
+}
+
+/*
+ * This is a generic file read routine, and uses the
+ * inode->i_op->readpage() function for the actual low-level
+ * stuff.
+ */
+int generic_file_read(struct inode * inode, struct file * filp, char * buf, int count)
+{
+	int read = 0;
+	unsigned long pos;
+	unsigned long page_cache = 0;
+
+	if (count <= 0)
+		return 0;
+
+	pos = filp->f_pos;
+	do {
+		struct page *page;
+		unsigned long offset, addr, nr;
+
+		if (pos >= inode->i_size)
+			break;
+		offset = pos & ~PAGE_MASK;
+		nr = PAGE_SIZE - offset;
+		/*
+		 * Try to find the data in the page cache..
+		 */
+		page = find_page(inode, pos & PAGE_MASK);
+		if (page)
+			goto found_page;
+
+		/*
+		 * Ok, it wasn't cached, so we need to create a new
+		 * page..
+		 */
+		if (!page_cache) {
+			page_cache = __get_free_page(GFP_KERNEL);
+			if (!page_cache) {
+				if (!read)
+					read = -ENOMEM;
+				break;
+			}
+		}
+
+		/*
+		 * That could have slept, so we need to check again..
+		 */
+		if (pos >= inode->i_size)
+			break;
+		page = find_page(inode, pos & PAGE_MASK);
+		if (page)
+			goto found_page;
+
+		/*
+		 * Ok, add the new page to the hash-queues...
+		 */
+		page = mem_map + MAP_NR(page_cache);
+		page_cache = 0;
+		page->count++;
+		page->uptodate = 0;
+		page->error = 0;
+		page->offset = pos & PAGE_MASK;
+		add_page_to_inode_queue(inode, page);
+		add_page_to_hash_queue(inode, page);
+
+		/* 
+		 * And start IO on it..
+		 * (this should be asynchronous, but currently isn't)
+		 */
+		inode->i_op->readpage(inode, page);
+
+found_page:
+		addr = page_address(page);
+		if (nr > count)
+			nr = count;
+		if (!page->uptodate) {
+			page_cache = try_to_read_ahead(inode, offset, page_cache);
+			if (!page->uptodate)
+				sleep_on(&page->wait);
+		}
+		if (nr > inode->i_size - pos)
+			nr = inode->i_size - pos;
+		memcpy_tofs(buf, (void *) (addr + offset), nr);
+		free_page(addr);
+		buf += nr;
+		pos += nr;
+		read += nr;
+		count -= nr;
+	} while (count);
+
+	filp->f_pos = pos;
+	if (page_cache)
+		free_page(page_cache);
+	if (!IS_RDONLY(inode)) {
+		inode->i_atime = CURRENT_TIME;
+		inode->i_dirt = 1;
+	}
+	return read;
+}
+
+/*
  * Semantics for shared and private memory areas are different past the end
  * of the file. A shared mapping past the last page of the file is an error
  * and results in a SIBGUS, while a private mapping just maps in a zero page.
@@ -156,51 +302,36 @@
 static unsigned long filemap_nopage(struct vm_area_struct * area, unsigned long address,
 	unsigned long page, int no_share)
 {
+	unsigned long offset;
 	struct inode * inode = area->vm_inode;
-	unsigned long new_page, old_page;
-	struct page *p;
+	unsigned long new_page;
 
-	address = (address & PAGE_MASK) - area->vm_start + area->vm_offset;
-	if (address >= inode->i_size && (area->vm_flags & VM_SHARED) && area->vm_mm == current->mm)
+	offset = (address & PAGE_MASK) - area->vm_start + area->vm_offset;
+	if (offset >= inode->i_size && (area->vm_flags & VM_SHARED) && area->vm_mm == current->mm)
 		send_sig(SIGBUS, current, 1);
-	p = find_page(inode, address);
-	if (p)
-		goto old_page_exists;
-	new_page = 0;
+
+	new_page = fill_page(inode, offset, page);
+	if (new_page) {
+		if (no_share) {
+			memcpy((void *) page, (void *) new_page, PAGE_SIZE);
+			free_page(new_page);
+			return page;
+		}
+		free_page(page);
+		return new_page;
+	}
+
 	if (no_share) {
 		new_page = __get_free_page(GFP_USER);
 		if (!new_page) {
 			oom(current);
-			return page;
+			new_page = pte_page(BAD_PAGE);
 		}
-	}
-	/* inode->i_op-> */ readpage(inode, address, (char *) page);
-	p = find_page(inode, address);
-	if (p)
-		goto old_and_new_page_exists;
-	p = mem_map + MAP_NR(page);
-	p->offset = address;
-	add_page_to_inode_queue(inode, p);
-	add_page_to_hash_queue(inode, p);
-	if (new_page) {
 		memcpy((void *) new_page, (void *) page, PAGE_SIZE);
-		return new_page;
+		free_page(page);
+		page = new_page;
 	}
-	p->count++;
 	return page;
-
-old_and_new_page_exists:
-	if (new_page)
-		free_page(new_page);
-old_page_exists:
-	old_page = page_address(p);
-	if (no_share) {
-		memcpy((void *) page, (void *) old_page, PAGE_SIZE);
-		return page;
-	}
-	p->count++;
-	free_page(page);
-	return old_page;
 }
 
 /*
@@ -479,7 +610,7 @@
 	}
 	if (!inode->i_sb || !S_ISREG(inode->i_mode))
 		return -EACCES;
-	if (!inode->i_op || !inode->i_op->bmap)
+	if (!inode->i_op || !inode->i_op->readpage)
 		return -ENOEXEC;
 	if (!IS_RDONLY(inode)) {
 		inode->i_atime = CURRENT_TIME;
diff -u --recursive --new-file v1.3.53/linux/mm/memory.c linux/mm/memory.c
--- v1.3.53/linux/mm/memory.c	Tue Jan  2 16:46:31 1996
+++ linux/mm/memory.c	Thu Jan  4 14:14:20 1996
@@ -189,7 +189,7 @@
 	return 0;
 }
 
-static inline void copy_one_pte(pte_t * old_pte, pte_t * new_pte)
+static inline void copy_one_pte(pte_t * old_pte, pte_t * new_pte, int cow)
 {
 	pte_t pte = *old_pte;
 
@@ -204,7 +204,7 @@
 		set_pte(new_pte, pte);
 		return;
 	}
-	if (pte_cow(pte))
+	if (cow)
 		pte = pte_wrprotect(pte);
 	if (delete_from_swap_cache(pte_page(pte)))
 		pte = pte_mkdirty(pte);
@@ -213,7 +213,7 @@
 	mem_map[MAP_NR(pte_page(pte))].count++;
 }
 
-static inline int copy_pte_range(pmd_t *dst_pmd, pmd_t *src_pmd, unsigned long address, unsigned long size)
+static inline int copy_pte_range(pmd_t *dst_pmd, pmd_t *src_pmd, unsigned long address, unsigned long size, int cow)
 {
 	pte_t * src_pte, * dst_pte;
 	unsigned long end;
@@ -239,13 +239,13 @@
 		/* I would like to switch arguments here, to make it
 		 * consistent with copy_xxx_range and memcpy syntax.
 		 */
-		copy_one_pte(src_pte++, dst_pte++);
+		copy_one_pte(src_pte++, dst_pte++, cow);
 		address += PAGE_SIZE;
 	} while (address < end);
 	return 0;
 }
 
-static inline int copy_pmd_range(pgd_t *dst_pgd, pgd_t *src_pgd, unsigned long address, unsigned long size)
+static inline int copy_pmd_range(pgd_t *dst_pgd, pgd_t *src_pgd, unsigned long address, unsigned long size, int cow)
 {
 	pmd_t * src_pmd, * dst_pmd;
 	unsigned long end;
@@ -269,7 +269,7 @@
 	if (end > PGDIR_SIZE)
 		end = PGDIR_SIZE;
 	do {
-		error = copy_pte_range(dst_pmd++, src_pmd++, address, end - address);
+		error = copy_pte_range(dst_pmd++, src_pmd++, address, end - address, cow);
 		if (error)
 			break;
 		address = (address + PMD_SIZE) & PMD_MASK; 
@@ -288,12 +288,13 @@
 	pgd_t * src_pgd, * dst_pgd;
 	unsigned long address = vma->vm_start;
 	unsigned long end = vma->vm_end;
-	int error = 0;
+	int error = 0, cow;
 
+	cow = (vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE;
 	src_pgd = pgd_offset(src, address);
 	dst_pgd = pgd_offset(dst, address);
 	while (address < end) {
-		error = copy_pmd_range(dst_pgd++, src_pgd++, address, end - address);
+		error = copy_pmd_range(dst_pgd++, src_pgd++, address, end - address, cow);
 		if (error)
 			break;
 		address = (address + PGDIR_SIZE) & PGDIR_MASK;
@@ -752,170 +753,6 @@
 }
 
 /*
- * try_to_share() checks the page at address "address" in the task "p",
- * to see if it exists, and if it is clean. If so, share it with the current
- * task.
- *
- * NOTE! This assumes we have checked that p != current, and that they
- * share the same inode and can generally otherwise be shared.
- */
-static int try_to_share(unsigned long to_address, struct vm_area_struct * to_area,
-	unsigned long from_address, struct vm_area_struct * from_area,
-	unsigned long newpage)
-{
-	pgd_t * from_dir, * to_dir;
-	pmd_t * from_middle, * to_middle;
-	pte_t * from_table, * to_table;
-	pte_t from, to;
-
-	from_dir = pgd_offset(from_area->vm_mm,from_address);
-/* is there a page-directory at from? */
-	if (pgd_none(*from_dir))
-		return 0;
-	if (pgd_bad(*from_dir)) {
-		printk("try_to_share: bad page directory %08lx\n", pgd_val(*from_dir));
-		pgd_clear(from_dir);
-		return 0;
-	}
-	from_middle = pmd_offset(from_dir, from_address);
-/* is there a mid-directory at from? */
-	if (pmd_none(*from_middle))
-		return 0;
-	if (pmd_bad(*from_middle)) {
-		printk("try_to_share: bad mid directory %08lx\n", pmd_val(*from_middle));
-		pmd_clear(from_middle);
-		return 0;
-	}
-	from_table = pte_offset(from_middle, from_address);
-	from = *from_table;
-/* is the page present? */
-	if (!pte_present(from))
-		return 0;
-/* if it is dirty it must be from a shared mapping to be shared */
-	if (pte_dirty(from)) {
-		if (!(from_area->vm_flags & VM_SHARED))
-			return 0;
-	}
-/* is the page reasonable at all? */
-	if (pte_page(from) >= high_memory)
-		return 0;
-	if (mem_map[MAP_NR(pte_page(from))].reserved)
-		return 0;
-/* is the destination ok? */
-	to_dir = pgd_offset(to_area->vm_mm,to_address);
-/* is there a page-directory at to? */
-	if (pgd_none(*to_dir))
-		return 0;
-	if (pgd_bad(*to_dir)) {
-		printk("try_to_share: bad page directory %08lx\n", pgd_val(*to_dir));
-		return 0;
-	}
-	to_middle = pmd_offset(to_dir, to_address);
-/* is there a mid-directory at to? */
-	if (pmd_none(*to_middle))
-		return 0;
-	if (pmd_bad(*to_middle)) {
-		printk("try_to_share: bad mid directory %08lx\n", pmd_val(*to_middle));
-		return 0;
-	}
-	to_table = pte_offset(to_middle, to_address);
-	to = *to_table;
-	if (!pte_none(to))
-		return 0;
-/* do we copy? */
-	if (newpage) {
-		/* if it's in the swap cache, it's dirty by implication */
-		/* so we can't use it if it's not from a shared mapping */
-		if (in_swap_cache(pte_page(from))) {
-			if (!(from_area->vm_flags & VM_SHARED))
-				return 0;
-		}
-		copy_page(pte_page(from), newpage);
-		set_pte(to_table, mk_pte(newpage, to_area->vm_page_prot));
-		return 1;
-	}
-/*
- * do a final swap-cache test before sharing them: if it's in the swap
- * cache, we have to remove it now, as we get two pointers to the same
- * physical page and the cache can't handle it. Mark the original dirty.
- *
- * NOTE! Even if "from" is dirty, "to" will be clean: if we get here
- * with a dirty "from", the from-mapping is a shared map, so we can trust
- * the page contents to be up-to-date
- */
-	if (in_swap_cache(pte_page(from))) {
-		if (!(from_area->vm_flags & VM_SHARED))
-			return 0;
-		set_pte(from_table, pte_mkdirty(from));
-		delete_from_swap_cache(pte_page(from));
-	}
-	mem_map[MAP_NR(pte_page(from))].count++;
-	set_pte(to_table, mk_pte(pte_page(from), to_area->vm_page_prot));
-/* Check if we need to do anything at all to the 'from' field */
-	if (!pte_write(from))
-		return 1;
-	if (from_area->vm_flags & VM_SHARED)
-		return 1;
-/* ok, need to mark it read-only, so invalidate any possible old TB entry */
-	set_pte(from_table, pte_wrprotect(from));
-	invalidate_page(from_area, from_address);
-	return 1;
-}
-
-/*
- * share_page() tries to find a process that could share a page with
- * the current one.
- *
- * We first check if it is at all feasible by checking inode->i_count.
- * It should be >1 if there are other tasks sharing this inode.
- */
-static int share_page(struct vm_area_struct * area, unsigned long address,
-	int write_access, unsigned long newpage)
-{
-	struct inode * inode;
-	unsigned long offset;
-	unsigned long from_address;
-	unsigned long give_page;
-	struct vm_area_struct * mpnt;
-
-	if (!area || !(inode = area->vm_inode) || inode->i_count < 2)
-		return 0;
-	/* do we need to copy or can we just share? */
-	give_page = 0;
-	if (write_access && !(area->vm_flags & VM_SHARED)) {
-		if (!newpage)
-			return 0;
-		give_page = newpage;
-	}
-	offset = address - area->vm_start + area->vm_offset;
-	/* See if there is something in the VM we can share pages with. */
-	/* Traverse the entire circular i_mmap list, except `area' itself. */
-	for (mpnt = area->vm_next_share; mpnt != area; mpnt = mpnt->vm_next_share) {
-		/* must be same inode */
-		if (mpnt->vm_inode != inode) {
-			printk("Aiee! Corrupt vm_area_struct i_mmap ring\n");
-			break;	
-		}
-		/* offsets must be mutually page-aligned */
-		if ((mpnt->vm_offset ^ area->vm_offset) & ~PAGE_MASK)
-			continue;
-		/* the other area must actually cover the wanted page.. */
-		from_address = offset + mpnt->vm_start - mpnt->vm_offset;
-		if (from_address < mpnt->vm_start || from_address >= mpnt->vm_end)
-			continue;
-		/* .. NOW we can actually try to use the same physical page */
-		if (!try_to_share(address, area, from_address, mpnt, give_page))
-			continue;
-		/* free newpage if we never used it.. */
-		if (give_page || !newpage)
-			return 1;
-		free_page(newpage);
-		return 1;
-	}
-	return 0;
-}
-
-/*
  * This function zeroes out partial mmap'ed pages at truncation time..
  */
 static void partial_clear(struct vm_area_struct *vma, unsigned long address)
@@ -1069,11 +906,6 @@
 		return;
 	}
 	page = __get_free_page(GFP_KERNEL);
-	if (share_page(vma, address, write_access, page)) {
-		++vma->vm_mm->rss;
-		++tsk->min_flt;
-		return;
-	}
 	if (!page) {
 		oom(tsk);
 		put_page(page_table, BAD_PAGE);
@@ -1088,10 +920,6 @@
 	 */
 	page = vma->vm_ops->nopage(vma, address, page,
 		write_access && !(vma->vm_flags & VM_SHARED));
-	if (share_page(vma, address, write_access, 0)) {
-		free_page(page);
-		return;
-	}
 	/*
 	 * This silly early PAGE_DIRTY setting removes a race
 	 * due to the bad i386 page protection. But it's valid
diff -u --recursive --new-file v1.3.53/linux/mm/swap.c linux/mm/swap.c
--- v1.3.53/linux/mm/swap.c	Tue Jan  2 16:46:31 1996
+++ linux/mm/swap.c	Thu Jan  4 14:14:20 1996
@@ -1323,17 +1323,11 @@
 	mem_map = (mem_map_t *) start_mem;
 	p = mem_map + MAP_NR(end_mem);
 	start_mem = LONG_ALIGN((unsigned long) p);
-	while (p > mem_map) {
+	memset(mem_map, 0, start_mem - (unsigned long) mem_map);
+	do {
 		--p;
-		p->count = 0;
-		p->dirty = 0;
 		p->reserved = 1;
-		p->inode = NULL;
-		p->offset = 0;
-		p->write_list = NULL;
-		p->next = p->prev = NULL;
-		p->next_hash = p->next_hash = NULL;
-	}
+	} while (p > mem_map);
 
 	for (i = 0 ; i < NR_MEM_LISTS ; i++) {
 		unsigned long bitmap_size;
diff -u --recursive --new-file v1.3.53/linux/net/Changes linux/net/Changes
--- v1.3.53/linux/net/Changes	Wed Dec 27 09:12:14 1995
+++ linux/net/Changes	Wed Jan  3 20:36:23 1996
@@ -314,6 +314,14 @@
 o	Packetwin driver [Craig]			[IN]
 o	Net alias changes [Juan]			[IN]
 
+-------->>>>> 1.3.53 <<<<<<--------
+
+o	Missing htons() in ip_build_xmit [Mike Kilburn]	[IN]
+o	Missing protocol type set in appletalk		[IN]
+o	Net alias changes/documentation [Juan Ciarlante][IN]
+o	Set protocol type on IPX sends [Various]	[IN]
+o	Lance driver packet length sanity check		[IN]
+
 ---------- Things I thought Linus had for a while and not merged ----------------
 
 
@@ -324,6 +332,7 @@
 o	Chase Donald for new drivers, get people to sort out what net
 	drivers should cease to be 'Alpha'.
 o	IPX PPP support
+o	IP multicast bug fixes
 
 ---------- Things pending for me to merge --------------
 
@@ -337,7 +346,7 @@
 o	Forwarding queue control (+ fairness algorithms ??)
 o	IP forward flow control.
 o	IPX memory leak ?????				[Done with luck]
-o	Clean up RAW AX.25 sockets.
+o	Clean up RAW AX.25 sockets.			[Sorted by skb_clone change]
 o	Finish IPIP bug fixes				[Done hopefully]
 o	Multicast routing				[STARTED BITS]
 o	PPP/IPX
diff -u --recursive --new-file v1.3.53/linux/net/appletalk/aarp.c linux/net/appletalk/aarp.c
--- v1.3.53/linux/net/appletalk/aarp.c	Wed Nov  8 07:11:43 1995
+++ linux/net/appletalk/aarp.c	Wed Jan  3 20:36:23 1996
@@ -441,6 +441,7 @@
 	}
 
 	skb->dev = dev;
+	skb->protocol = htons(ETH_P_ATALK);
 			
 	hash=sa->s_node%(AARP_HASH_SIZE-1);
 	save_flags(flags);
diff -u --recursive --new-file v1.3.53/linux/net/core/net_alias.c linux/net/core/net_alias.c
--- v1.3.53/linux/net/core/net_alias.c	Wed Dec 27 09:12:14 1995
+++ linux/net/core/net_alias.c	Wed Jan  3 20:36:23 1996
@@ -1218,7 +1218,7 @@
     
   dev = nat->dev_select(nat, main_dev, (struct sockaddr *)&sin_src);
 
-  if (dev == NULL) return main_dev;
+  if (dev == NULL || dev->family != family) return main_dev;
   
   /*
    * dev ok only if it is alias of main_dev
diff -u --recursive --new-file v1.3.53/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v1.3.53/linux/net/ipv4/ip_forward.c	Sat Nov 25 19:04:58 1995
+++ linux/net/ipv4/ip_forward.c	Wed Jan  3 20:36:23 1996
@@ -9,6 +9,7 @@
  *
  * Fixes:
  *		Many		:	Split from ip.c , see ip_input.c for history.
+ *		Dave Gregorich	:	NULL ip_rt_put fix for multicast routing.
  */
 
 #include <linux/config.h>
@@ -242,11 +243,13 @@
 #endif
 		IS_SKB(skb);
 
-		if (skb->len+encap > dev2->mtu && (ntohs(iph->frag_off) & IP_DF)) {
-		  ip_statistics.IpFragFails++;
-		  icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dev2->mtu), dev);
-		  ip_rt_put(rt);
-		  return -1;
+		if (skb->len+encap > dev2->mtu && (ntohs(iph->frag_off) & IP_DF)) 
+		{
+			ip_statistics.IpFragFails++;
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dev2->mtu), dev);
+			if(rt)
+				ip_rt_put(rt);
+			return -1;
 		}
 
 #ifdef CONFIG_IP_MROUTE
@@ -266,7 +269,8 @@
 			if (skb2 == NULL)
 			{
 				NETDEBUG(printk("\nIP: No memory available for IP forward\n"));
-				ip_rt_put(rt);
+				if(rt)
+					ip_rt_put(rt);
 				return -1;
 			}
 		
@@ -427,10 +431,12 @@
 	}
 	else
 	{
-	        ip_rt_put(rt);
+	        if(rt)
+	        	ip_rt_put(rt);
 		return -1;
 	}
-	ip_rt_put(rt);
+	if(rt)
+		ip_rt_put(rt);
 	
 	/*
 	 *	Tell the caller if their buffer is free.
diff -u --recursive --new-file v1.3.53/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v1.3.53/linux/net/ipv4/ip_output.c	Sat Nov 25 19:04:58 1995
+++ linux/net/ipv4/ip_output.c	Wed Jan  3 20:36:23 1996
@@ -20,6 +20,7 @@
  *
  *	Fixes:
  *		Alan Cox	:	Missing nonblock feature in ip_build_xmit.
+ *		Mike Kilburn	:	htons() missing in ip_build_xmit.
  */
 
 #include <asm/segment.h>
@@ -153,7 +154,7 @@
 
 	skb->dev = dev;
 	skb->arp = 1;
-	skb->protocol = ETH_P_IP;
+	skb->protocol = htons(ETH_P_IP);
 	if (dev->hard_header)
 	{
 		skb_reserve(skb,MAX_HEADER);
@@ -652,6 +653,7 @@
 			return error;
 		}
 		skb->dev=dev;
+		skb->protocol = htons(ETH_P_IP);
 		skb->free=1;
 		skb->when=jiffies;
 		skb->sk=sk;
@@ -817,8 +819,8 @@
 		 *	Fill in the control structures
 		 */
 		 
-		skb->next = skb->prev = NULL;
 		skb->dev = dev;
+		skb->protocol = htons(ETH_P_IP);
 		skb->when = jiffies;
 		skb->free = 1; /* dubious, this one */
 		skb->sk = sk;
diff -u --recursive --new-file v1.3.53/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v1.3.53/linux/net/ipv4/tcp.c	Sat Dec 30 15:50:56 1995
+++ linux/net/ipv4/tcp.c	Wed Jan  3 20:36:23 1996
@@ -180,7 +180,9 @@
  *		Alan Cox	:	Small hooks for enSKIP.
  *		Alexey Kuznetsov:	Path MTU discovery.
  *		Alan Cox	:	Support soft errors.
- *
+ *		Alan Cox	:	Fix MTU discovery pathalogical case
+ *					when the remote claims no mtu!
+ *		Marc Tamsky	:	TCP_CLOSE fix.
  *
  * To Fix:
  *		Fast path the code. Two things here - fix the window calculation
@@ -766,6 +768,7 @@
 		 */
 		 
 		ct++;
+		sk->retransmits++;
 		sk->prot->retransmits ++;
 		tcp_statistics.TcpRetransSegs++;
 		
@@ -950,8 +953,15 @@
 	struct sock *sk = (struct sock*)data;
 	int why = sk->ip_xmit_timeout;
 
+	/*
+	 *	We are reset. We will send no more retransmits.
+	 */
+	 
+	if(sk->zapped)
+		return;
+		
 	/* 
-	 * only process if socket is not in use
+	 *	Only process if socket is not in use
 	 */
 
 	cli();
@@ -969,7 +979,7 @@
 
 	/* Always see if we need to send an ack. */
 
-	if (sk->ack_backlog && !sk->zapped) 
+	if (sk->ack_backlog) 
 	{
 		sk->prot->read_wakeup (sk);
 		if (! sk->dead)
@@ -1105,7 +1115,8 @@
 			if (rt->rt_mtu > new_mtu)
 				rt->rt_mtu = new_mtu;
 
-		if (sk->mtu > new_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr))
+		if (sk->mtu > new_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr)
+			&& new_mtu > sizeof(struct iphdr)+sizeof(struct tcphdr))
 			sk->mtu = new_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr);
 
 		return;
@@ -3877,8 +3888,9 @@
 		if (sk->rcv_ack_seq == sk->write_seq /*&& sk->acked_seq == sk->fin_seq*/) 
 		{
 			flag |= 1;
-			tcp_set_state(sk,TCP_CLOSE);
 			sk->shutdown = SHUTDOWN_MASK;
+			tcp_set_state(sk,TCP_CLOSE);
+			return 1;
 		}
 	}
 
@@ -4163,9 +4175,10 @@
 					tcp_reset(sk->saddr, sk->daddr, skb->h.th,
 						sk->prot, NULL, skb->dev, sk->ip_tos, sk->ip_ttl);
 					tcp_statistics.TcpEstabResets++;
-					tcp_set_state(sk,TCP_CLOSE);
 					sk->err = EPIPE;
+					sk->error_report(sk);
 					sk->shutdown = SHUTDOWN_MASK;
+					tcp_set_state(sk,TCP_CLOSE);
 					kfree_skb(skb, FREE_READ);
 					return 0;
 				}
@@ -5425,9 +5438,9 @@
 
 	sk->backoff++;
 	sk->rto = min(sk->rto << 1, 120*HZ);
-	reset_xmit_timer (sk, TIME_PROBE0, sk->rto);
 	sk->retransmits++;
 	sk->prot->retransmits ++;
+	reset_xmit_timer (sk, TIME_PROBE0, sk->rto);
 }
 
 /*
diff -u --recursive --new-file v1.3.53/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v1.3.53/linux/net/ipx/af_ipx.c	Wed Dec 27 09:12:15 1995
+++ linux/net/ipx/af_ipx.c	Wed Jan  3 20:37:05 1996
@@ -573,6 +573,7 @@
 
 	/* set up data link and physical headers */
 	skb->dev = dev;
+	skb->protocol = htons(ETH_P_IPX);
 	dl->datalink_header(dl, skb, dest_node);
 #if 0
 	/* 
@@ -1094,10 +1095,11 @@
 	skb=sock_alloc_send_skb(sk, size, 0, 0, &err);
 	if(skb==NULL)
 		return err;
-		
+
 	skb_reserve(skb,ipx_offset);
 	skb->free=1;
 	skb->arp=1;
+	skb->sk=sk;
 
 	/* Fill in IPX header */
 	ipx=(ipx_packet *)skb_put(skb,sizeof(ipx_packet));
diff -u --recursive --new-file v1.3.53/linux/scripts/header.tk linux/scripts/header.tk
--- v1.3.53/linux/scripts/header.tk	Thu Nov  9 11:23:56 1995
+++ linux/scripts/header.tk	Thu Jan  4 14:07:58 1996
@@ -1,12 +1,4 @@
 #
-# Used to determine size of canvas inserted for booleans. It holds the
-# place where the 'm' button goes in a tristate, and keeps the look and feel
-# consistent.
-#
-set modbutton_width 0
-set modbutton_height 0
-
-#
 # Set to be the x and y position of subwindows.
 #
 set winx 100
@@ -23,7 +15,7 @@
 
 proc menu_option { w menu_num text } {
 	button .f0.x$menu_num -text "$text" -width 50 -command "$w .$w \"$text\""
-	pack .f0.x$menu_num -anchor w -pady 1
+	pack .f0.x$menu_num -pady 1 -expand on
 }
 
 #
@@ -173,7 +165,7 @@
 	set file1 [open $filename r]
 	clear_choices
 	while { [gets $file1 line] >= 0} {
-		if [regexp {([0-9A-Z_]+)=([ynm])} $line foo var value] {
+		if [regexp {([0-9A-Za-z_]+)=([ynm])} $line foo var value] {
 			if { $value == "y" } then { set cmd "global $var; set $var 1" }
 			if { $value == "n" } then { set cmd "global $var; set $var 0" }
 			if { $value == "m" } then { set cmd "global $var; set $var 2" }
@@ -213,45 +205,59 @@
 	}
 }
 
-proc bool {w mnum line text variable} {
-	frame $w.x$line
-	global modbutton_width
-	global modbutton_height
-	radiobutton $w.x$line.y -text "y" -variable $variable -value 1 \
-		-width 2 -command "update_menu$mnum .menu$mnum"
-	radiobutton $w.x$line.n -text "n"  -variable $variable -value 0 \
-		-width 2 -command "update_menu$mnum .menu$mnum"
-	canvas $w.x$line.m -width $modbutton_width -relief raised \
-		-height $modbutton_height
+proc option_name {w mnum line text variable} {
 	button $w.x$line.help -text "Help" -relief raised \
 		-command "dohelp .dohelp $variable"
-	pack $w.x$line.y $w.x$line.m  -anchor w -side left
-	button $w.x$line.l -text "$text" -relief raised 
-	pack $w.x$line.n $w.x$line.help $w.x$line.l -anchor w -side left
-	pack $w.x$line -anchor w
-#
-# If we don't know the size yet, update the screen and calculate the size
-# of the 'm' button by measuring the size of the 'y' button.  The buttons
-# have fixed size anyways, so it should always be correct.
-#
-	if { $modbutton_width == 0 } then {
-		update
-		set modbutton_width [winfo width $w.x$line.y]
-		set modbutton_height [winfo height $w.x$line.y]
-		$w.x$line.m configure -height $modbutton_height \
-			-width $modbutton_width
-	}
+	button $w.x$line.l -text "$text" -relief groove -anchor w
+	pack $w.x$line.help -side right -fill y
+	pack $w.x$line.l -side right -fill both -expand on
+}
+
+proc toggle_switch {w mnum line text variable} {
+	frame $w.x$line -relief sunken
+	radiobutton $w.x$line.y -text "y" -variable $variable -value 1 \
+		-relief groove -width 2 -command "update_menu$mnum .menu$mnum"
+	radiobutton $w.x$line.n -text "n"  -variable $variable -value 0 \
+		-relief groove -width 2 -command "update_menu$mnum .menu$mnum"
+	radiobutton $w.x$line.m -text "m"  -variable $variable -value 2 \
+		-relief groove -width 2 -command "update_menu$mnum .menu$mnum"
+		
+	option_name $w $mnum $line $text $variable
+		
+	pack $w.x$line.n $w.x$line.m $w.x$line.y -side right -fill y
+}
+
+proc bool {w mnum line text variable} {
+	toggle_switch $w $mnum $line $text $variable
+	$w.x$line.m configure -state disabled
+	pack $w.x$line -anchor w -padx 15 -fill both -expand on
+}
+
+proc tristate {w mnum line text variable } {
+	toggle_switch $w $mnum $line $text $variable
+	pack $w.x$line -anchor w -padx 15 -fill both -expand on
+}
+
+proc dep_tristate {w mnum line text variable } {
+	tristate $w $mnum $line $text $variable
 }
 
 proc int { w mnum line text variable } {
 	frame $w.x$line
-	button $w.x$line.help -text "Help" -relief raised \
-		-command "dohelp .dohelp $variable "
-	button $w.x$line.l -text "$text" -relief raised 
+	option_name $w $mnum $line $text $variable
 	entry $w.x$line.x -width 15 -relief sunken -borderwidth 2 \
 		-textvariable $variable
-	pack $w.x$line.x $w.x$line.help $w.x$line.l -anchor w -side left
-	pack $w.x$line -anchor w
+	pack $w.x$line.x -anchor w -side right -fill y
+	pack $w.x$line -anchor w -padx 15 -fill both -expand on
+}
+
+proc minimenu { w mnum line text variable } {
+	frame $w.x$line
+	option_name $w $mnum $line $text $variable
+	menubutton $w.x$line.x -text "Choose..." -menu $w.x$line.x.menu \
+		-relief raised -width 15
+	pack $w.x$line.x -anchor w -side right -fill y
+	pack $w.x$line -anchor w -padx 15 -fill both -expand on
 }
 
 proc comment {w line text } {
@@ -349,37 +355,6 @@
 proc check_sound_config { num } {
 #nothing for now.
 }
-
-proc tristate {w mnum line text variable } {
-	frame $w.x$line
-	radiobutton $w.x$line.y -text "y" -variable $variable -value 1 \
-		-width 2 -command "update_menu$mnum .menu$mnum"
-	radiobutton $w.x$line.n -text "n"  -variable $variable -value 0 \
-		-width 2 -command "update_menu$mnum .menu$mnum"
-	radiobutton $w.x$line.m -text "m"  -variable $variable -value 2 \
-		-width 2 -command "update_menu$mnum .menu$mnum"
-	button $w.x$line.help -text "Help" -relief raised \
-		-command "dohelp .dohelp $variable"
-	button $w.x$line.l -text "$text" -relief raised 
-	pack $w.x$line.y $w.x$line.m $w.x$line.n $w.x$line.help $w.x$line.l -anchor w -side left
-	pack $w.x$line -anchor w
-}
-
-proc dep_tristate {w mnum line text variable } {
-	frame $w.x$line
-	radiobutton $w.x$line.y -text "y" -variable $variable -value 1 \
-		-width 2 -command "update_menu$mnum .menu$mnum"
-	radiobutton $w.x$line.n -text "n"  -variable $variable -value 0 \
-		-width 2 -command "update_menu$mnum .menu$mnum"
-	radiobutton $w.x$line.m -text "m"  -variable $variable -value 2 \
-		-width 2 -command "update_menu$mnum .menu$mnum"
-	button $w.x$line.help -text "Help" -relief raised \
-		-command "dohelp .dohelp $variable"
-	button $w.x$line.l -text "$text" -relief raised 
-	pack $w.x$line.y $w.x$line.m $w.x$line.n $w.x$line.help $w.x$line.l -anchor w -side left
-	pack $w.x$line -anchor w
-}
-
 
 proc do_sound {w mnum line} {
 	message $w.x$line -width 400 -aspect 300 -text "Note: The sound drivers cannot as of yet be configured via the X-based interface" -relief raised
diff -u --recursive --new-file v1.3.53/linux/scripts/patch-kernel linux/scripts/patch-kernel
--- v1.3.53/linux/scripts/patch-kernel	Thu Jan  1 02:00:00 1970
+++ linux/scripts/patch-kernel	Wed Jan  3 14:04:53 1996
@@ -0,0 +1,47 @@
+#! /bin/sh
+# Script to apply kernel patches.
+#   usage: patch-kernel [ sourcedir [ patchdir ] ]
+#     The source directory defaults to /usr/src/linux, and the patch
+#     directory defaults to the current directory.
+#
+# It determines the current kernel version from the top-level Makefile.
+# It then looks for patches for the next sublevel in the patch directory.
+# This is applied using "patch -p1 -s" from within the kernel directory.
+# A check is then made for "*.rej" files to see if the patch was
+# successful.  If it is, then all of the "*.orig" files are removed.
+#
+#       Nick Holloway <Nick.Holloway@alfie.demon.co.uk>, 2nd January 1995.
+
+# Set directories from arguments, or use defaults.
+sourcedir=${1-/usr/src/linux}
+patchdir=${2-.}
+
+# set current VERSION, PATCHLEVEL, SUBLEVEL
+eval `sed -n 's/^\([A-Z]*\) = \([0-9]*\)$/\1=\2/p' $sourcedir/Makefile`
+if [ -z "$VERSION" -o -z "$PATCHLEVEL" -o -z "$SUBLEVEL" ]
+then
+    echo "unable to determine current kernel version" >&2
+    exit 1
+fi
+
+echo "Current kernel version is $VERSION.$PATCHLEVEL.$SUBLEVEL"
+
+while :
+do
+    SUBLEVEL=`expr $SUBLEVEL + 1`
+    patch=patch-$VERSION.$PATCHLEVEL.$SUBLEVEL.gz 
+    if [ ! -r $patchdir/$patch ]
+    then
+        break
+    fi
+
+    echo "Applying $patch..."
+    gunzip -dc $patchdir/$patch | patch -p1 -s -E -f -d $sourcedir
+    if [ "`find $sourcedir -name \*.rej -print`" ]
+    then
+        echo "Aborting.  Reject files found."
+        break
+    fi
+    # Remove backup files
+    find $sourcedir -name \*.orig -print | xargs rm -f
+done
diff -u --recursive --new-file v1.3.53/linux/scripts/tail.tk linux/scripts/tail.tk
--- v1.3.53/linux/scripts/tail.tk	Thu Nov  9 11:23:56 1995
+++ linux/scripts/tail.tk	Thu Jan  4 14:07:58 1996
@@ -1,12 +1,13 @@
 
-pack .header -side top -padx 10 -pady 10
-pack .f0 -side top -padx 15 -pady 10
+pack .header -side top -padx 10 -pady 10 -expand on
+pack .f0 -side top -padx 15 -pady 10 -fill y -expand on
 
 #
 # Misc buttons to save/restore state and so forth.
 # 
-frame .f0r_bot
-frame .f0l_bot
+frame .f0_bot
+frame .f0_bot.r
+frame .f0_bot.l
 
 #
 # Read the users settings from .config.  These wil override whatever is
@@ -31,45 +32,47 @@
 
 update_mainmenu .f0
 
-button .f0r_bot.save -text "Write Configuration" -width 25 -command {
+button .f0_bot.r.save -text "Save and Exit" -width 25 -command {
 	writeconfig .config include/linux/autoconf.h; wrapup .wrap }
 
-button .f0r_bot.quit -text "Quit" -command { maybe_exit .maybe } -width 25 \
+button .f0_bot.r.quit -text "Quit Without Saving" -command { maybe_exit .maybe } -width 25 \
 	-activebackground red -activeforeground white
 
-button .f0l_bot.store -text "Store Configuration to file" -width 25 -command {
+button .f0_bot.l.store -text "Store Configuration to File" -width 25 -command {
 	load_configfile .load "Save Configuration in file" write_config_file
 }
 
-button .f0l_bot.load -text "Load Configuration" -width 25 -command {
+button .f0_bot.l.load -text "Load Configuration from File" -width 25 -command {
 	load_configfile .load "Load Configuration from file" read_config_file
 }
 
-pack  .f0r_bot.save .f0r_bot.quit -padx 25 -ipadx 10 -ipady 2 -anchor w
+pack  .f0_bot.r.save .f0_bot.r.quit -padx 25 -ipadx 10 -ipady 2 -expand on
+pack  .f0_bot.l.load .f0_bot.l.store -padx 25 -ipadx 10 -ipady 2 -expand on
 
-pack  .f0l_bot.load .f0l_bot.store -padx 25 -ipadx 10 -ipady 2 -anchor w
+pack .f0_bot.r -side left -padx 15 -pady 10 -expand on -fill y
+pack .f0_bot.l -side right -padx 15 -pady 10 -expand on -fill y
 
-pack .f0r_bot .f0l_bot -side left -padx 15 -pady 10 -anchor w
+pack .f0_bot -fill both -expand on
 
 #
 # If we cannot write our config files, disable the write button.
 #
 if { [file exists .config] == 1 } then {
 		if { [file writable .config] == 0 } then {
-			.f0r_bot.save configure -state disabled
+			.f0_bot.r.save configure -state disabled
 		}
 	} else {
 		if { [file writable .] == 0 } then {
-			.f0r_bot.save configure -state disabled
+			.f0_bot.r.save configure -state disabled
 		}
 	}
 
 if { [file exists include/linux/autoconf.h] == 1 } then {
 		if { [file writable include/linux/autoconf.h] == 0 } then {
-			.f0r_bot.save configure -state disabled
+			.f0_bot.r.save configure -state disabled
 		}
 	} else {
 		if { [file writable include/linux/] == 0 } then {
-			.f0r_bot.save configure -state disabled
+			.f0_bot.r.save configure -state disabled
 		}
 	}
diff -u --recursive --new-file v1.3.53/linux/scripts/tkgen.c linux/scripts/tkgen.c
--- v1.3.53/linux/scripts/tkgen.c	Tue Dec 26 04:45:42 1995
+++ linux/scripts/tkgen.c	Thu Jan  4 14:07:58 1996
@@ -3,6 +3,9 @@
  * Version 1.0
  * Eric Youngdale
  * 10/95
+ *
+ * 1995 01 04 - Aesthetic improvements by Avery Pennarun
+ *              <apenwarr@foxnet.net>
  */
 #include <stdio.h>
 #include "tkparse.h"
@@ -399,8 +402,8 @@
   printf("\tbutton $w.f.back -text \"Main Menu\" -activebackground green \\\n");
   printf("\t\t-width 15 -command \"destroy $w; focus $oldFocus; update_mainmenu $w\"\n");
 
-  printf("\tpack $w.f.back $w.f.next $w.f.prev -side left -pady 10 -padx 45\n");
-  printf("\tpack $w.f -pady 10 -side top -padx 10 -anchor w\n");
+  printf("\tpack $w.f.back $w.f.next $w.f.prev -side left -expand on\n");
+  printf("\tpack $w.f -pady 10 -side top -anchor w -fill x -expand on\n");
   printf("\tfocus $w\n");
   printf("\tupdate_menu%d $w\n", menu_num);
   printf("\tglobal winx; global winy\n");
@@ -638,7 +641,7 @@
 	  break;
 
 	case tok_choice:
-	  printf("\t$w.line%d.menu add radiobutton -label \"%s\" -variable %s -value %d -command \"update_menu%d .menu%d\"\n",
+	  printf("\t$w.x%d.x.menu add radiobutton -label \"%s\" -variable %s -value %d -command \"update_menu%d .menu%d\"\n",
 		 cfg1->menu_line,
 		 cfg->label,
 		 cfg1->optionname,
@@ -652,11 +655,20 @@
 	      start_proc(menulabel, cfg->menu_number, FALSE);
 	      menu_num = cfg->menu_number;
 	    }
+#if 0
 	  printf("\tmenubutton $w.line%d -text \"%s\" -menu $w.line%d.menu \\\n",
 		 cfg->menu_line, cfg->label, cfg->menu_line);
 	  printf("\t	-relief raised -width 35\n");
 	  printf("\tpack $w.line%d -anchor w\n", cfg->menu_line);
 	  printf("\tmenu $w.line%d.menu\n", cfg->menu_line);
+#else
+	  printf("\tminimenu $w %d %d \"%s\" %s\n",
+	  	cfg->menu_number,
+	  	cfg->menu_line,
+	  	cfg->label,
+	  	cfg->optionname);
+	  printf("\tmenu $w.x%d.x.menu\n", cfg->menu_line);
+#endif
 	  cfg1 = cfg;
 	  break;
 	case tok_tristate:
@@ -733,7 +745,7 @@
 #endif
   printf("\tlabel $w.m0 -bitmap error\n");
   printf("\tmessage $w.m1 -width 400 -aspect 300 -text \"The sound drivers cannot as of yet be configured via the X-based interface\" -relief raised\n");
-  printf("\tpack $w.m0 $w.m1 -side top -pady 10\n");
+  printf("\tpack $w.m0 $w.m1 -side top -pady 10 -expand on\n");
   /*
    * Close out the last menu.
    */
