diff -u --recursive --new-file v2.1.58/linux/Makefile linux/Makefile
--- v2.1.58/linux/Makefile	Wed Oct 15 16:04:23 1997
+++ linux/Makefile	Wed Oct 15 21:09:09 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 58
+SUBLEVEL = 59
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
diff -u --recursive --new-file v2.1.58/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c
--- v2.1.58/linux/arch/alpha/kernel/irq.c	Wed Apr 16 14:14:59 1997
+++ linux/arch/alpha/kernel/irq.c	Fri Oct 17 14:02:01 1997
@@ -37,6 +37,18 @@
 #  error Unable to handle more than 64 irq levels.
 #endif
 
+/* PROBE_MASK is the bitset of irqs that we consider for autoprobing: */
+#if defined(CONFIG_ALPHA_P2K)
+  /* always mask out unused timer irq 0 and RTC irq 8 */
+# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0x101UL)
+#elif defined(CONFIG_ALPHA_ALCOR)
+  /* always mask out unused timer irq 0, "irqs" 20-30, and the EISA cascade: */
+# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0xfff000000001UL)
+#else
+  /* always mask out unused timer irq 0: */
+# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~1UL)
+#endif
+
 /* Reserved interrupts.  These must NEVER be requested by any driver!
  */
 #define	IS_RESERVED_IRQ(irq)	((irq)==2)	/* IRQ 2 used by hw cascade */
@@ -644,10 +656,13 @@
 	unsigned int i;
 
 	for (i = NR_IRQS - 1; i > 0; i--) {
+		if (!(PROBE_MASK & (1UL << i))) {
+			continue;
+		}
 		action = irq_action[i];
 		if (!action) {
 			enable_irq(i);
-			irqs |= (1 << i);
+			irqs |= (1UL << i);
 		}
 	}
 	/*
@@ -668,14 +683,10 @@
  */
 int probe_irq_off(unsigned long irqs)
 {
+	unsigned long delay;
 	int i;
 	
-        /* as irq 0 & 8 handling don't use this function, i didn't
-	 * bother changing the following: */
-        irqs &= irq_mask & ~1;  /* always mask out irq 0---it's the unused timer */
-#ifdef CONFIG_ALPHA_P2K
-	irqs &= ~(1 << 8);	/* mask out irq 8 since that's the unused RTC input to PIC */
-#endif
+        irqs &= irq_mask;
 	if (!irqs)
 		return 0;
 	i = ffz(~irqs);
diff -u --recursive --new-file v2.1.58/linux/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c
--- v2.1.58/linux/arch/i386/kernel/i386_ksyms.c	Mon Aug 18 18:19:42 1997
+++ linux/arch/i386/kernel/i386_ksyms.c	Fri Oct 17 13:59:30 1997
@@ -63,6 +63,7 @@
 
 #ifdef CONFIG_MCA
 /* Adapter probing and info methods. */
+EXPORT_SYMBOL(machine_id);
 EXPORT_SYMBOL(mca_find_adapter);
 EXPORT_SYMBOL(mca_write_pos);
 EXPORT_SYMBOL(mca_read_pos);
diff -u --recursive --new-file v2.1.58/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c
--- v2.1.58/linux/drivers/block/ide-probe.c	Tue Sep 23 16:48:47 1997
+++ linux/drivers/block/ide-probe.c	Fri Oct 17 15:51:08 1997
@@ -176,7 +176,7 @@
 	int rc;
 	ide_ioreg_t hd_status;
 	unsigned long timeout;
-	int irqs = 0;
+	unsigned long irqs = 0;
 
 	if (!HWIF(drive)->irq) {		/* already got an IRQ? */
 		probe_irq_off(probe_irq_on());	/* clear dangling irqs */
@@ -235,7 +235,7 @@
 			(void) GET_STAT();	/* clear drive IRQ */
 
 		} else {	/* Mmmm.. multiple IRQs.. don't know which was ours */
-			printk("%s: IRQ probe failed (%d)\n", drive->name, irqs);
+			printk("%s: IRQ probe failed (%ld)\n", drive->name, irqs);
 #ifdef CONFIG_BLK_DEV_CMD640
 #ifdef CMD640_DUMP_REGS
 			if (HWIF(drive)->chipset == ide_cmd640) {
diff -u --recursive --new-file v2.1.58/linux/drivers/net/8390.h linux/drivers/net/8390.h
--- v2.1.58/linux/drivers/net/8390.h	Sun Feb  2 05:18:35 1997
+++ linux/drivers/net/8390.h	Fri Oct 17 14:02:01 1997
@@ -46,8 +46,8 @@
 #ifndef HAVE_AUTOIRQ
 /* From auto_irq.c */
 extern struct device *irq2dev_map[16];
-extern int autoirq_setup(int waittime);
-extern int autoirq_report(int waittime);
+extern void autoirq_setup(int waittime);
+extern unsigned long autoirq_report(int waittime);
 #endif
 
 /* Most of these entries should be in 'struct device' (or most of the
diff -u --recursive --new-file v2.1.58/linux/drivers/net/auto_irq.c linux/drivers/net/auto_irq.c
--- v2.1.58/linux/drivers/net/auto_irq.c	Thu Feb 29 21:50:43 1996
+++ linux/drivers/net/auto_irq.c	Fri Oct 17 14:02:01 1997
@@ -39,76 +39,18 @@
 #include <asm/irq.h>
 #include <linux/netdevice.h>
 
-struct device *irq2dev_map[16] = {0, 0, /* ... zeroed */};
+struct device *irq2dev_map[NR_IRQS] = {0, 0, /* ... zeroed */};
 
-unsigned long irqs_busy = 0x2147;		/* The set of fixed IRQs (keyboard, timer, etc) */
-unsigned long irqs_used = 0x0001;		/* The set of fixed IRQs sometimes enabled. */
-unsigned long irqs_reserved = 0x0000;		/* An advisory "reserved" table. */
-unsigned long irqs_shared = 0x0000;		/* IRQ lines "shared" among conforming cards.*/
+static unsigned long irqs;
 
-static volatile unsigned long irq_bitmap;	/* The irqs we actually found. */
-static unsigned long irq_handled;		/* The irq lines we have a handler on. */
-static volatile int irq_number;			/* The latest irq number we actually found. */
-
-static void autoirq_probe(int irq, void *dev_id, struct pt_regs * regs)
+void autoirq_setup(int waittime)
 {
-	irq_number = irq;
-	set_bit(irq, (void *)&irq_bitmap);	/* irq_bitmap |= 1 << irq; */
-	disable_irq(irq);
-	return;
-}
-
-int autoirq_setup(int waittime)
-{
-	int i, mask;
-	int timeout = jiffies + waittime;
-	int boguscount = (waittime*loops_per_sec) / 100;
-
-	irq_handled = 0;
-	for (i = 0; i < 16; i++) {
-		if (test_bit(i, &irqs_busy) == 0
-			&& request_irq(i, autoirq_probe, SA_INTERRUPT, "irq probe", NULL) == 0)
-			set_bit(i, (void *)&irq_handled);	/* irq_handled |= 1 << i;*/
-	}
-	/* Update our USED lists. */
-	irqs_used |= ~irq_handled;
-	irq_number = 0;
-	irq_bitmap = 0;
-
-	/* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
-	while (timeout > jiffies  &&  --boguscount > 0)
-		;
-
-	for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
-		if (irq_bitmap & irq_handled & mask) {
-			irq_handled &= ~mask;
-#ifdef notdef
-			printk(" Spurious interrupt on IRQ %d\n", i);
-#endif
-			free_irq(i, NULL);
-		}
-	}
-	return irq_handled;
+	irqs = probe_irq_on();
 }
 
 int autoirq_report(int waittime)
 {
-	int i;
-	int timeout = jiffies+waittime;
-	int boguscount = (waittime*loops_per_sec) / 100;
-
-	/* Hang out at least <waittime> jiffies waiting for the IRQ. */
-
-	while (timeout > jiffies  &&  --boguscount > 0)
-		if (irq_number)
-			break;
-
-	/* Retract the irq handlers that we installed. */
-	for (i = 0; i < 16; i++) {
-		if (test_bit(i, (void *)&irq_handled))
-			free_irq(i, NULL);
-	}
-	return irq_number;
+	return probe_irq_off(irqs);
 }
 
 /*
diff -u --recursive --new-file v2.1.58/linux/drivers/net/ne.c linux/drivers/net/ne.c
--- v2.1.58/linux/drivers/net/ne.c	Mon Aug 11 14:47:04 1997
+++ linux/drivers/net/ne.c	Fri Oct 17 14:04:26 1997
@@ -42,6 +42,7 @@
 #include <linux/pci.h>
 #include <linux/bios32.h>
 #include <linux/init.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 
@@ -408,6 +409,7 @@
 	outb_p(0x00, ioaddr + EN0_RCNTLO);
 	outb_p(0x00, ioaddr + EN0_RCNTHI);
 	outb_p(E8390_RREAD+E8390_START, ioaddr); /* Trigger it... */
+	udelay(10000);		/* wait 10ms for interrupt to propagate */
 	outb_p(0x00, ioaddr + EN0_IMR); 		/* Mask it again. */
 	dev->irq = autoirq_report(0);
 	if (ei_debug > 2)
diff -u --recursive --new-file v2.1.58/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.1.58/linux/drivers/scsi/Config.in	Tue Sep 23 16:48:48 1997
+++ linux/drivers/scsi/Config.in	Fri Oct 17 13:59:30 1997
@@ -82,7 +82,10 @@
   fi
 fi
 if [ "$CONFIG_MCA" = "y" ]; then
-  dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI
+   dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI
+   if [ "$CONFIG_SCSI_IBMMCA" != "n" ]; then
+      bool '  reset SCSI-devices while booting' SCSI_IBMMCA_DEV_RESET
+   fi
 fi
 if [ "$CONFIG_PARPORT" != "n" ]; then
   dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PARPORT
diff -u --recursive --new-file v2.1.58/linux/drivers/scsi/ibmmca.c linux/drivers/scsi/ibmmca.c
--- v2.1.58/linux/drivers/scsi/ibmmca.c	Mon Apr 14 16:28:16 1997
+++ linux/drivers/scsi/ibmmca.c	Fri Oct 17 13:59:30 1997
@@ -7,6 +7,7 @@
 
 /* Update history:
    Jan 15 1996:  First public release.
+   - Martin Kolinek
 
    Jan 23 1996:  Scrapped code which reassigned scsi devices to logical
    device numbers. Instead, the existing assignment (created
@@ -16,15 +17,18 @@
    and also the hard disks are ordered under Linux the 
    same way as they are under dos (i.e., C: disk is sda, 
    D: disk is sdb, etc.).
+   - Martin Kolinek
 
    I think that the CD-ROM is now detected only if a CD is 
    inside CD_ROM while Linux boots. This can be fixed later,
    once the driver works on all types of PS/2's.
+   - Martin Kolinek
 
    Feb 7 1996:   Modified biosparam function. Fixed the CD-ROM detection. 
    For now, devices other than harddisk and CD_ROM are 
    ignored. Temporarily modified abort() function 
-   to behave like reset(). 
+   to behave like reset().
+   - Martin Kolinek
 
    Mar 31 1996:  The integrated scsi subsystem is correctly found
    in PS/2 models 56,57, but not in model 76. Therefore
@@ -32,21 +36,200 @@
    This function allows the user to force detection of
    scsi subsystem. The kernel option has format
    ibmmcascsi=n
-   where n is the scsi_id (pun) of the subsystem. Most
-   likely, n is 7. 
+   where n is the scsi_id (pun) of the subsystem. Most likely, n is 7.
+   - Martin Kolinek
 
    Aug 21 1996:  Modified the code which maps ldns to (pun,0).  It was
    insufficient for those of us with CD-ROM changers.
    - Chris Beauregard
-
-   Mar 16 1997: Modified driver to run as a module and to support
-   multiple adapters.
+ 
+   Dec 14 1996: More improvements to the ldn mapping.  See check_devices
+   for details.  Did more fiddling with the integrated SCSI detection,
+   but I think it's ultimately hopeless without actually testing the
+   model of the machine.  The 56, 57, 76 and 95 (ultimedia) all have
+   different integrated SCSI register configurations.  However, the 56
+   and 57 are the only ones that have problems with forced detection.
+   - Chris Beauregard
+ 
+   Mar 8-16 1997: Modified driver to run as a module and to support 
+   multiple adapters. A structure, called ibmmca_hostdata, is now
+   present, containing all the variables, that were once only
+   available for one single adapter. The find_subsystem-routine has vanished.
+   The hardware recognition is now done in ibmmca_detect directly.
+   This routine checks for presence of MCA-bus, checks the interrupt
+   level and continues with checking the installed hardware.
+   Certain PS/2-models do not recognize a SCSI-subsystem automatically.
+   Hence, the setup defined by command-line-parameters is checked first.
+   Thereafter, the routine probes for an integrated SCSI-subsystem.
+   Finally, adapters are checked. This method has the advantage to cover all
+   possible combinations of multiple SCSI-subsystems on one MCA-board. Up to
+   eight SCSI-subsystems can be recognized and announced to the upper-level
+   drivers with this improvement. A set of defines made changes to other
+   routines as small as possible.
    - Klaus Kudielka
- */
+   
+   May 30 1997: (v1.5b)
+   1) SCSI-command capability enlarged by the recognition of MODE_SELECT.
+      This needs the RD-Bit to be disabled on IM_OTHER_SCSI_CMD_CMD which 
+      allows data to be written from the system to the device. It is a
+      necessary step to be allowed to set blocksize of SCSI-tape-drives and 
+      the tape-speed, whithout confusing the SCSI-Subsystem.
+   2) The recognition of a tape is included in the check_devices routine.
+      This is done by checking for TYPE_TAPE, that is already defined in
+      the kernel-scsi-environment. The markup of a tape is done in the 
+      global ldn_is_tape[] array. If the entry on index ldn 
+      is 1, there is a tapedrive connected.
+   3) The ldn_is_tape[] array is necessary to distinguish between tape- and 
+      other devices. Fixed blocklength devices should not cause a problem
+      with the SCB-command for read and write in the ibmmca_queuecommand
+      subroutine. Therefore, I only derivate the READ_XX, WRITE_XX for
+      the tape-devices, as recommended by IBM in this Technical Reference,
+      mentioned below. (IBM recommends to avoid using the read/write of the
+      subsystem, but the fact was, that read/write causes a command error from
+      the subsystem and this causes kernel-panic.)
+   4) In addition, I propose to use the ldn instead of a fix char for the
+      display of PS2_DISK_LED_ON(). On 95, one can distinguish between the
+      devices that are accessed. It shows activity and easyfies debugging.   
+   The tape-support has been tested with a SONY SDT-5200 and a HP DDS-2
+   (I do not know yet the type). Optimization and CD-ROM audio-support, 
+   I am working on ...
+   - Michael Lang
+   
+   June 19 1997: (v1.6b)
+   1) Submitting the extra-array ldn_is_tape[] -> to the local ld[]
+      device-array. 
+   2) CD-ROM Audio-Play seems to work now.
+   3) When using DDS-2 (120M) DAT-Tapes, mtst shows still density-code
+      0x13 for ordinary DDS (61000 BPM) instead 0x24 for DDS-2. This appears 
+      also on Adaptec 2940 adaptor in a PCI-System. Therefore, I assume that 
+      the problem is independent of the low-level-driver/bus-architecture.
+   4) Hexadecimal ldn on PS/2-95 LED-display.
+   5) Fixing of the PS/2-LED on/off that it works right with tapedrives and
+      does not confuse the disk_rw_in_progress counter.
+   - Michael Lang
+  
+   June 21 1997: (v1.7b)
+   1) Adding of a proc_info routine to inform in /proc/scsi/ibmmca/<host> the
+      outer-world about operational load statistics on the different ldns,
+      seen by the driver. Everybody that has more than one IBM-SCSI should
+      test this, because I only have one and cannot see what happens with more
+      than one IBM-SCSI hosts.
+   2) Definition of a driver version-number to have a better recognition of 
+      the source when there are existing too much releases that may confuse
+      the user, when reading about release-specific problems. Up to know,
+      I calculated the version-number to be 1.7. Because we are in BETA-test
+      yet, it is today 1.7b.
+   3) Sorry for the heavy bug I programmed on June 19 1997! After that, the
+      CD-ROM did not work any more! The C7-command was a fake impression
+      I got while programming. Now, the READ and WRITE commands for CD-ROM are
+      no longer running over the subsystem, but just over 
+      IM_OTHER_SCSI_CMD_CMD. On my observations (PS/2-95), now CD-ROM mounts
+      much faster(!) and hopefully all fancy multimedia-functions, like direct
+      digital recording from audio-CDs also work. (I tried it with cdda2wav
+      from the cdwtools-package and it filled up the harddisk immediately :-).)
+      To easify boolean logics, a further local device-type in ld[], called
+      is_cdrom has been included.
+   4) If one uses a SCSI-device of unsupported type/commands, one
+      immediately runs into a kernel-panic caused by Command Error. To better
+      understand which SCSI-command caused the problem, I extended this
+      specific panic-message slightly.
+   - Michael Lang
+ 
+   June 25 1997: (v1.8b)
+   1) Some cosmetical changes for the handling of SCSI-device-types.
+      Now, also CD-Burners / WORMs and SCSI-scanners should work. For
+      MO-drives I have no experience, therefore not yet supported.
+      In logical_devices I changed from different type-variables to one
+      called 'device_type' where the values, corresponding to scsi.h,
+      of a SCSI-device are stored.
+   2) There existed a small bug, that maps a device, coming after a SCSI-tape
+      wrong. Therefore, e.g. a CD-ROM changer would have been mapped wrong
+      -> problem removed.
+   3) Extension of the logical_device structure. Now it contains also device,
+      vendor and revision-level of a SCSI-device for internal usage.
+   - Michael Lang
+
+   June 26-29 1997: (v2.0b)
+   1) The release number 2.0b is necessary because of the completely new done
+      recognition and handling of SCSI-devices with the adapter. As I got
+      from Chris the hint, that the subsystem can reassign ldns dynamically,
+      I remembered this immediate_assign-command, I found once in the handbook.
+      Now, the driver first kills all ldn assignments that are set by default
+      on the SCSI-subsystem. After that, it probes on all puns and luns for
+      devices by going through all combinations with immediate_assign and
+      probing for devices, using device_inquiry. The found physical(!) pun,lun
+      structure is stored in get_scsi[][] as device types. This is followed
+      by the assignment of all ldns to existing SCSI-devices. If more ldns
+      than devices are available, they are assigned to non existing pun,lun
+      combinations to satisfy the adapter. With this, the dynamical mapping
+      was possible to implement. (For further info see the text in the 
+      source-code and in the description below. Read the description
+      below BEFORE installing this driver on your system!)
+   2) Changed the name IBMMCA_DRIVER_VERSION to IBMMCA_SCSI_DRIVER_VERSION.
+   3) The LED-display shows on PS/2-95 no longer the ldn, but the SCSI-ID
+      (pun) of the accessed SCSI-device. This is now senseful, because the 
+      pun known within the driver is exactly the pun of the physical device
+      and no longer a fake one.
+   4) The /proc/scsi/ibmmca/<host_no> consists now of the first part, where
+      hit-statistics of ldns is shown and a second part, where the maps of 
+      physical and logical SCSI-devices are displayed. This could be very 
+      interesting, when one is using more than 15 SCSI-devices in order to 
+      follow the dynamical remapping of ldns.
+   - Michael Lang
+ 
+   June 26-29 1997: (v2.0b-1)
+   1) I forgot to switch the local_checking_phase_flag to 1 and back to 0
+      in the dynamical remapping part in ibmmca_queuecommand for the 
+      device_exist routine. Sorry.
+   - Michael Lang
+ 
+   July 1-13 1997: (v3.0b,c)
+   1) Merging of the driver-developments of Klaus Kudielka and Michael Lang 
+      in order to get a optimum and unified driver-release for the 
+      IBM-SCSI-Subsystem-Adapter(s).
+         For people, using the Kernel-release >=2.1.0, module-support should 
+      be no problem. For users, running under <2.1.0, module-support may not 
+      work, because the methods have changed between 2.0.x and 2.1.x.
+   2) Added some more effective statistics for /proc-output.
+   3) Change typecasting at necessary points from (unsigned long) to
+      virt_to_bus().
+   4) Included #if... at special points to have specific adaption of the
+      driver to kernel 2.0.x and 2.1.x. It should therefore also run with 
+      later releases.
+   5) Magneto-Optical drives and medium-changers are also recognized, now.
+      Therefore, we have a completely gapfree recognition of all SCSI-
+      device-types, that are known by Linux up to kernel 2.1.31.
+   6) The flag SCSI_IBMMCA_DEV_RESET has been inserted. If it is set within
+      the configuration, each connected SCSI-device will get a reset command
+      during boottime. This can be necessary for some special SCSI-devices.
+      This flag should be included in Config.in.
+      (See also the new Config.in file.)
+   Probable next improvement: bad disk handler.
+   - Michael Lang
+ 
+   Sept 14 1997: (v3.0c)
+   1) Some debugging and speed optimization applied.
+   - Michael Lang
+
+
+ 
+	TODO:
+ 
+	- It seems that the handling of bad disks is really bad -
+	  non-existent, in fact.
+        - More testing of the full driver-controlled dynamical ldn 
+          (re)mapping for up to 56 SCSI-devices.
+        - Support more SCSI-device-types, if Linux defines more.
+        - Support more of the SCSI-command set.
+	- Support some of the caching abilities, particularly Read Prefetch.
+	  This fetches data into the cache, which later gets hit by the
+	  regular Read Data.
+        - Abort and Reset functions still slightly buggy. Especially when
+          floppydisk(!) operations report errors.
 
-#include <linux/module.h>
+******************************************************************************/
 
-#include <linux/config.h>
+#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/head.h>
 #include <linux/types.h>
@@ -65,8 +248,13 @@
 #include "hosts.h"
 #include "ibmmca.h"
 
+#include <linux/config.h>		/* for CONFIG_SCSI_IBMMCA etc. */
+
 /*--------------------------------------------------------------------*/
 
+/* current version of this driver-source: */
+#define IBMMCA_SCSI_DRIVER_VERSION "3.0d"
+
 /*
    Driver Description
 
@@ -115,6 +303,47 @@
    ldn -> (ldn/8,ldn%8).  We end up with a real mishmash of puns
    and luns, but it all seems to work. - Chris Beaurgard
 
+   And that last paragraph is also no longer correct.  It uses a
+   slightly more complex mapping that will always map hard disks to
+   (x,0), for some x, and consecutive none disk devices will usually
+   share puns.
+ 
+   Again, the last paragraphs are no longer correct. Now, the physical
+   SCSI-devices on the SCSI-bus are probed via immediate_assign- and
+   device_inquiry-commands. This delivers a exact map of the physical
+   SCSI-world that is now stored in the get_scsi[][]-array. This means,
+   that the once hidden pun,lun assignment is now known to this driver.
+   It no longer believes in default-settings of the subsystem and maps all
+   ldns to existing pun,lun by foot. This assures full control of the ldn
+   mapping and allows dynamical remapping of ldns to different pun,lun, if
+   there are more SCSI-devices installed than ldns available (n>15). The
+   ldns from 0 to 6 get 'hardwired' by this driver to puns 0 to 7 at lun=0,
+   excluding the pun of the subsystem. This assures, that at least simple 
+   SCSI-installations have optimum access-speed and are not touched by
+   dynamical remapping. The ldns 7 to 14 are put to existing devices with 
+   lun>0 or to non-existing devices, in order to satisfy the subsystem, if 
+   there are less than 15 SCSI-devices connected. In the case of more than 15 
+   devices, the dynamical mapping goes active. If the get_scsi[][] reports a 
+   device to be existant, but it has no ldn assigned, it gets a ldn out of 7 
+   to 14. The numbers are assigned in cyclic order. Therefore it takes 8 
+   dynamical assignments on SCSI-devices, until a certain device 
+   looses its ldn again. This assures, that dynamical remapping is avoided 
+   during intense I/O between up to eight SCSI-devices (means pun,lun 
+   combinations). A further advantage of this method is, that people who
+   build their kernel without probing on all luns will get what they expect.
+ 
+   IMPORTANT: Because of the now correct recognition of physical pun,lun, and 
+   their report to mid-level- and higher-level-drivers, the new reported puns
+   can be different from the old, faked puns. Therefore, Linux will eventually
+   change /dev/sdXXX assignments and prompt you for corrupted superblock
+   repair on boottime. In this case DO NOT PANIC, YOUR DISKS ARE STILL OK!!!
+   You have to reboot (CTRL-D) with a old kernel and set the /etc/fstab-file
+   entries right. After that, the system should come up as errorfree as before.
+   If your boot-partition is not coming up, also edit the /etc/lilo.conf-file
+   in a Linux session booted on old kernel and run lilo before reboot. Check
+   lilo.conf anyway to get boot on other partitions with foreign OSes right
+   again.
+
    (C) Regular Processing 
    Only three functions get involved: ibmmca_queuecommand(), issue_cmd(),
    and interrupt_handler().
@@ -123,7 +352,8 @@
    ibmmca_queuecommand(). This function fills a "subsystem control block"
    (scb) and calls a local function issue_cmd(), which writes a scb 
    command into subsystem I/O ports. Once the scb command is carried out, 
-   interrupt_handler() is invoked.
+   interrupt_handler() is invoked. If a device is determined to be existant
+   and it has not assigned any ldn, it gets one dynamically.
 
    (D) Abort, Reset.
    These are implemented with busy waiting for interrupt to arrive.
@@ -137,12 +367,24 @@
    100% sure that it is correct for larger disks.
 
    (F) Kernel Boot Option 
-   The function ibmmca_scsi_setup() is called if option ibmmcascsi=... 
+   The function ibmmca_scsi_setup() is called if option ibmmcascsi=n 
    is passed to the kernel. See file linux/init/main.c for details.
+   
+   (G) Driver Module Support
+   Is implemented and tested by K. Kudielka. This could probably not work
+   on kernels <2.1.0.
+  
+   (H) Multiple Hostadapter Support
+   This driver supports up to eight interfaces of type IBM-SCSI-Subsystem. 
+   Integrated-, and MCA-adapters are automatically recognized. Unrecognizable
+   IBM-SCSI-Subsystem interfaces can be specified as kernel-parameters.
+ 
+   (I) /proc-Filesystem Information
+   Information about the driver condition is given in 
+   /proc/scsi/ibmmca/<host_no>. ibmmca_proc_info provides this information.
  */
 
 /*--------------------------------------------------------------------*/
-
 /* Here are the values and structures specific for the subsystem. 
  * The source of information is "Update for the PS/2 Hardware 
  * Interface Technical Reference, Common Interfaces", September 1991, 
@@ -152,26 +394,57 @@
  * In addition to SCSI subsystem, this update contains fairly detailed 
  * (at hardware register level) sections on diskette  controller,
  * keyboard controller, serial port controller, VGA, and XGA.
+ *
+ * Additional information from "Personal System/2 Micro Channel SCSI
+ * Adapter with Cache Technical Reference", March 1990, PN 68X2365,
+ * probably available from the same source (or possibly found buried
+ * in officemates desk).
+ *
+ * Further literature/program-sources referred for this driver:
+ * 
+ * Friedhelm Schmidt, "SCSI-Bus und IDE-Schnittstelle - Moderne Peripherie-
+ * Schnittstellen: Hardware, Protokollbeschreibung und Anwendung", 2. Aufl.
+ * Addison Wesley, 1996.
+ * 
+ * Michael K. Johnson, "The Linux Kernel Hackers' Guide", Version 0.6, Chapel
+ * Hill - North Carolina, 1995
+ * 
+ * Andreas Kaiser, "SCSI TAPE BACKUP for OS/2 2.0", Version 2.12, Stuttgart
+ * 1993
  */
 
+/*--------------------------------------------------------------------*/
+
 /* driver configuration */
 #define IM_MAX_HOSTS      8             /* maximum number of host adapters */
 #define IM_RESET_DELAY    10            /* seconds allowed for a reset */
 
 /* driver debugging - #undef all for normal operation */
-#undef  IM_DEBUG_TIMEOUT  50            /* if defined: count interrupts
-					   and ignore this special one */
-#undef  IM_DEBUG_INT                    /* verbose interrupt */
-#undef  IM_DEBUG_CMD                    /* verbose queuecommand */
-
-/* addresses of hardware registers on the subsystem */
-#define IM_CMD_REG   (shpnt->io_port)	/*Command Interface, (4 bytes long) */
-#define IM_ATTN_REG  (shpnt->io_port+4)	/*Attention (1 byte) */
-#define IM_CTR_REG   (shpnt->io_port+5)	/*Basic Control (1 byte) */
-#define IM_INTR_REG  (shpnt->io_port+6)	/*Interrupt Status (1 byte, r/o) */
-#define IM_STAT_REG  (shpnt->io_port+7)	/*Basic Status (1 byte, read only) */
 
+/* if defined: count interrupts and ignore this special one: */
+#undef  IM_DEBUG_TIMEOUT  50            
+/* verbose interrupt: */
+#undef  IM_DEBUG_INT                   
+/* verbose queuecommand: */
+#undef  IM_DEBUG_CMD    
+/* verbose queucommand for specific SCSI-device type: */
+#undef  IM_DEBUG_CMD_SPEC_DEV          
+/* verbose device probing */
+#undef  IM_DEBUG_PROBE
+
+/* device type that shall be displayed on syslog (only during debugging): */
+#define IM_DEBUG_CMD_DEVICE   TYPE_TAPE
+
+/* relative addresses of hardware registers on a subsystem */
+#define IM_CMD_REG   (shpnt->io_port)   /*Command Interface, (4 bytes long) */
+#define IM_ATTN_REG  (shpnt->io_port+4) /*Attention (1 byte) */
+#define IM_CTR_REG   (shpnt->io_port+5) /*Basic Control (1 byte) */
+#define IM_INTR_REG  (shpnt->io_port+6) /*Interrupt Status (1 byte, r/o) */
+#define IM_STAT_REG  (shpnt->io_port+7) /*Basic Status (1 byte, read only) */
+
+/* basic I/O-port of first adapter */
 #define IM_IO_PORT   0x3540
+/* maximum number of hosts that can be find */
 #define IM_N_IO_PORT 8
 
 /*requests going into the upper nibble of the Attention register */
@@ -204,11 +477,11 @@
 
 /*immediate commands (word written into low 2 bytes of command reg) */
 #define IM_RESET_IMM_CMD        0x0400
-#define IM_FORMAT_PREP_IMM_CMD  0x0417
 #define IM_FEATURE_CTR_IMM_CMD  0x040c
 #define IM_DMA_PACING_IMM_CMD   0x040d
 #define IM_ASSIGN_IMM_CMD       0x040e
 #define IM_ABORT_IMM_CMD        0x040f
+#define IM_FORMAT_PREP_IMM_CMD  0x0417
 
 /*SCB (Subsystem Control Block) structure */
 struct im_scb
@@ -257,6 +530,13 @@
 #define IM_DEVICE_INQUIRY_CMD  0x1c0b
 #define IM_OTHER_SCSI_CMD_CMD  0x241f
 
+/* unused, but supported, SCB commands */
+#define IM_GET_COMMAND_COMPLETE_STATUS_CMD   0x1c07 /* command status */
+#define IM_GET_POS_INFO_CMD                  0x1c0a /* returns neat stuff */
+#define IM_READ_PREFETCH_CMD                 0x1c31 /* caching controller only */
+#define IM_FOMAT_UNIT_CMD                    0x1c16 /* format unit */
+#define IM_REASSIGN_BLOCK_CMD                0x1c18 /* in case of error */
+
 /*values to set bits in the enable word of SCB */
 #define IM_READ_CONTROL              0x8000
 #define IM_REPORT_TSB_ONLY_ON_ERROR  0x4000
@@ -286,10 +566,41 @@
 /*subsystem uses interrupt request level 14 */
 #define IM_IRQ  14
 
-/*PS2 disk led is turned on/off by bits 6,7 of system control port */
-#define PS2_SYS_CTR  0x92
-#define PS2_DISK_LED_ON()   outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR)
-#define PS2_DISK_LED_OFF()  outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR)
+/*--------------------------------------------------------------------*/
+/*
+	The model 95 doesn't have a standard activity light.  Instead it
+	has a row of LEDs on the front.  We use the last one as the activity
+	indicator if we think we're on a model 95.  I suspect the model id
+	check will be either too narrow or too general, and some machines
+	won't have an activity indicator.  Oh well...
+
+	The regular PS/2 disk led is turned on/off by bits 6,7 of system
+	control port.
+*/
+
+/* LED display-port (actually, last LED on display) */
+#define MOD95_LED_PORT	   0x108
+/* system-control-register of PS/2s with diskindicator */
+#define PS2_SYS_CTR        0x92
+
+/* The SCSI-ID(!) of the accessed SCSI-device is shown on PS/2-95 machines' LED
+   displays. ldn is no longer displayed here, because the ldn mapping is now 
+   done dynamically and the ldn <-> pun,lun maps can be looked-up at boottime 
+   or during uptime in /proc/scsi/ibmmca/<host_no> in case of trouble, 
+   interest, debugging or just for having fun. The left number gives the
+   host-adapter number and the right shows the accessed SCSI-ID. */
+
+#define PS2_DISK_LED_ON(ad,id) {\
+	if( machine_id == 0xf8 ) { outb((char)(id+48), MOD95_LED_PORT ); \
+        outb((char)(ad+48), MOD95_LED_PORT+1); } \
+	else outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR); \
+}
+
+#define PS2_DISK_LED_OFF() {\
+	if( machine_id == 0xf8 ) { outb( ' ', MOD95_LED_PORT ); \
+        outb(' ', MOD95_LED_PORT+1); } \
+	else outb(inb(PS2_SYS_CTR) | 0x3f, PS2_SYS_CTR); \
+}
 
 /*--------------------------------------------------------------------*/
 
@@ -315,7 +626,8 @@
   S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
 
-/*max number of logical devices (can be up to 15) */
+/* Max number of logical devices (can be up from 0 to 14).  15 is the address
+of the adapter itself. */
 #define MAX_LOG_DEV  15
 
 /*local data for a logical device */
@@ -324,50 +636,108 @@
     struct im_scb scb;
     struct im_tsb tsb;
     struct im_sge sge[16];
-    Scsi_Cmnd *cmd;
-    int is_disk;
+    Scsi_Cmnd *cmd;  /* SCSI-command that is currently in progress */
+     
+    int device_type; /* type of the SCSI-device. See include/scsi/scsi.h
+		        for interpreation of the possible values */
     int block_length;
   };
 
+/* statistics of the driver during operations (for proc_info) */
+struct Driver_Statistics
+   {
+      /* SCSI statistics on the adapter */
+      int ldn_access[MAX_LOG_DEV+1];         /* total accesses on a ldn */
+      int ldn_read_access[MAX_LOG_DEV+1];    /* total read-access on a ldn */
+      int ldn_write_access[MAX_LOG_DEV+1];   /* total write-access on a ldn */
+      int total_accesses;                    /* total accesses on all ldns */
+      int total_interrupts;                  /* total interrupts (should be
+						same as total_accesses) */
+      /* dynamical assignment statistics */
+      int total_scsi_devices;                /* number of physical pun,lun */
+      int dyn_flag;                          /* flag showing dynamical mode */
+      int dynamical_assignments;             /* number of remappings of ldns */
+      int ldn_assignments[MAX_LOG_DEV+1];    /* number of remappings of each
+					        ldn */
+   };
+
 /* data structure for each host adapter */
 struct ibmmca_hostdata
-  {
-    /* array of logical devices */
+{
+  /* array of logical devices */
     struct logical_device _ld[MAX_LOG_DEV];
-    /* array to convert (pun, lun) into logical device number */
+  /* array to convert (pun, lun) into logical device number */
     unsigned char _get_ldn[8][8];
-    /* used only when checking logical devices */
+  /*array that contains the information about the physical SCSI-devices
+       attached to this host adapter */
+    unsigned char _get_scsi[8][8];
+  /* used only when checking logical devices */
     int _local_checking_phase_flag;
     int _got_interrupt;
     int _stat_result;
-    /* reset status (used only when doing reset) */
+  /* reset status (used only when doing reset) */
     int _reset_status;
-  };
-
-/* reset status values */
-#define IM_RESET_NOT_IN_PROGRESS   0
-#define IM_RESET_IN_PROGRESS       1
-#define IM_RESET_FINISHED_OK       2
-#define IM_RESET_FINISHED_FAIL     3
+  /* code of the last SCSI command (needed for panic info) */
+    int _last_scsi_command;
+  /* counter that points on next reassignable ldn for dynamical remapping */
+  /* The default value is 7, that is the first reassignable number in
+     the list on startup. */
+    int _next_ldn;
+  /* Statistics for this IBM-SCSI-host */
+    struct Driver_Statistics _IBM_DS;
+};
 
 /* macros to access host data structure */
 #define HOSTDATA(shpnt) ((struct ibmmca_hostdata *) shpnt->hostdata)
 #define subsystem_pun (shpnt->this_id)
 #define ld (HOSTDATA(shpnt)->_ld)
 #define get_ldn (HOSTDATA(shpnt)->_get_ldn)
+#define get_scsi (HOSTDATA(shpnt)->_get_scsi)
 #define local_checking_phase_flag (HOSTDATA(shpnt)->_local_checking_phase_flag)
 #define got_interrupt (HOSTDATA(shpnt)->_got_interrupt)
 #define stat_result (HOSTDATA(shpnt)->_stat_result)
 #define reset_status (HOSTDATA(shpnt)->_reset_status)
+#define last_scsi_command (HOSTDATA(shpnt)->_last_scsi_command)
+#define next_ldn (HOSTDATA(shpnt)->_next_ldn)
+#define IBM_DS (HOSTDATA(shpnt)->_IBM_DS)
+
+/* Define a arbitrary number as subsystem-marker-type. This number is, as 
+   described in the SCSI-Standard, not occupied by other device-types. */
+#define TYPE_IBM_SCSI_ADAPTER   0x2F
+
+/* Define 0xFF for no device type, because this type is not defined within
+   the SCSI-standard, therefore, it can be used and should not cause any
+   harm. */
+#define TYPE_NO_DEVICE          0xFF
+
+/* define medium-changer. If this is not defined previously, define 
+   this type here. */
+#ifndef TYPE_MEDIUM_CHANGER
+#define TYPE_MEDIUM_CHANGER     0x08
+#endif
 
-/*--------------------------------------------------------------------*/
+/* define operations for immediate_assign */
+#define SET_LDN        0
+#define REMOVE_LDN     1
+
+/* reset status flag contents */
+#define IM_RESET_NOT_IN_PROGRESS   0
+#define IM_RESET_IN_PROGRESS       1
+#define IM_RESET_FINISHED_OK       2
+#define IM_RESET_FINISHED_FAIL     3
+
+/*-----------------------------------------------------------------------*/
 
 /* if this is nonzero, ibmmcascsi option has been passed to the kernel */
-static int io_port[IM_MAX_HOSTS] = { 0 };
-static int scsi_id[IM_MAX_HOSTS] = { 7 };
+static int io_port[IM_MAX_HOSTS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+static int scsi_id[IM_MAX_HOSTS] = { 7, 7, 7, 7, 7, 7, 7, 7 };
 
+/* fill module-parameters only, when this define is present.
+   (that is kernel >=2.1.0) */
+#ifdef MODULE_PARM
 MODULE_PARM(io_port, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i");
-MODULE_PARM(scsi_id, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i");
+MODULE_PARM(scsi_id, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i"); 
+#endif
 
 /*counter of concurrent disk read/writes, to turn on/off disk led */
 static int disk_rw_in_progress = 0;
@@ -375,26 +745,34 @@
 /* host information */
 static int found = 0;
 static struct Scsi_Host *hosts[IM_MAX_HOSTS+1] = { NULL };
+/*-----------------------------------------------------------------------*/
 
-/*--------------------------------------------------------------------*/
-
-/*local functions */
-static void interrupt_handler (int irq, void *dev_id,
-			       struct pt_regs *regs);
-static void issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg,
-		       unsigned char attn_reg);
+/*local functions in forward declaration */
+static void interrupt_handler (int irq, void *dev_id, struct pt_regs *regs);
+static void issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg, 
+                       unsigned char attn_reg);
 static void internal_done (Scsi_Cmnd * cmd);
 static void check_devices (struct Scsi_Host *shpnt);
-static int device_exists (struct Scsi_Host *shpnt, int ldn, int *is_disk,
-			  int *block_length);
-static struct Scsi_Host *ibmmca_register(Scsi_Host_Template * template,
+static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun, 
+                            unsigned int lun, unsigned int ldn, 
+                            unsigned int operation);
+static int device_inquiry(struct Scsi_Host *shpnt, int ldn, 
+                          unsigned char *buf);
+static char *ti_p(int value);
+static char *ti_l(int value);
+static int device_exists (struct Scsi_Host *shpnt, int ldn, int *block_length,
+                          int *device_type);
+static struct Scsi_Host *ibmmca_register(Scsi_Host_Template * template, 
 					 int port, int id);
 
+/* local functions needed for proc_info */
+static int ldn_access_load(struct Scsi_Host *shpnt, int ldn);
+static int ldn_access_total_read_write(struct Scsi_Host *shpnt);
+
 /*--------------------------------------------------------------------*/
 
 static void 
-interrupt_handler (int irq, void *dev_id,
-		   struct pt_regs *regs)
+interrupt_handler (int irq, void *dev_id, struct pt_regs *regs)
 {
   int i = 0;
   struct Scsi_Host *shpnt;
@@ -402,21 +780,24 @@
   unsigned int cmd_result;
   unsigned int ldn;
 
-  do shpnt = hosts[i++];
+  /* search for one adapter-response on shared interrupt */
+  do
+    shpnt = hosts[i++];
   while (shpnt && !(inb(IM_STAT_REG) & IM_INTR_REQUEST));
+
+  /* return if some other device on this IRQ caused the interrupt */
   if (!shpnt) return;
 
   /*get command result and logical device */
-  intr_reg = inb(IM_INTR_REG);
+  intr_reg = inb (IM_INTR_REG);
   cmd_result = intr_reg & 0xf0;
   ldn = intr_reg & 0x0f;
 
   /*must wait for attention reg not busy, then send EOI to subsystem */
-  while (1)
-    {
+  while (1) {
       cli ();
-      if (!(inb (IM_STAT_REG) & IM_BUSY))
-	break;
+      if (!(inb (IM_STAT_REG) & IM_BUSY)) 
+        break;
       sti ();
     }
   outb (IM_EOI | ldn, IM_ATTN_REG);
@@ -424,17 +805,24 @@
 
   /*these should never happen (hw fails, or a local programming bug) */
   if (cmd_result == IM_ADAPTER_HW_FAILURE)
-    panic ("IBM MCA SCSI: subsystem hardware failure.\n");
+    panic ("IBM MCA SCSI: subsystem hardware failure. Last SCSI_CMD=0x%X. \n",
+	   last_scsi_command);
   if (cmd_result == IM_CMD_ERROR)
-    panic ("IBM MCA SCSI: command error.\n");
+    panic ("IBM MCA SCSI: command error. Last SCSI_CMD=0x%X. \n",
+	   last_scsi_command);
   if (cmd_result == IM_SOFTWARE_SEQUENCING_ERROR)
-    panic ("IBM MCA SCSI: software sequencing error.\n");
+    panic ("IBM MCA SCSI: software sequencing error. Last SCSI_CMD=0x%X. \n",
+	   last_scsi_command);
 
+  /* if no panic appeared, increase the interrupt-counter */
+  IBM_DS.total_interrupts++;
+   
   /*only for local checking phase */
   if (local_checking_phase_flag)
     {
       stat_result = cmd_result;
       got_interrupt = 1;
+      reset_status = IM_RESET_FINISHED_OK;
       return;
     }
 
@@ -454,6 +842,9 @@
 		}
 	      else
 		{
+		  /*reset disk led counter, turn off disk led */
+		  disk_rw_in_progress = 0;
+		  PS2_DISK_LED_OFF ();
 		  reset_status = IM_RESET_FINISHED_OK;
 		}
 	      return;
@@ -464,12 +855,12 @@
 
 #ifdef IM_DEBUG_TIMEOUT
       {
-	static int count = 0;
+        static int count = 0;
 
-	if (++count == IM_DEBUG_TIMEOUT) {
-	  printk("IBM MCA SCSI: Ignoring interrupt.\n");
-	  return;
-	}
+        if (++count == IM_DEBUG_TIMEOUT) {
+          printk("IBM MCA SCSI: Ignoring interrupt.\n");
+          return;
+        }
       }
 #endif
 
@@ -481,25 +872,28 @@
 
 #ifdef IM_DEBUG_INT
       printk("cmd=%02x ireg=%02x ds=%02x cs=%02x de=%02x ce=%02x\n", 
-	     cmd->cmnd[0], intr_reg, 
-	     ld[ldn].tsb.dev_status, ld[ldn].tsb.cmd_status, 
-	     ld[ldn].tsb.dev_error, ld[ldn].tsb.cmd_error);
+         cmd->cmnd[0], intr_reg, 
+         ld[ldn].tsb.dev_status, ld[ldn].tsb.cmd_status, 
+         ld[ldn].tsb.dev_error, ld[ldn].tsb.cmd_error); 
 #endif
 
-      /*if this is end of disk read/write, may turn off PS/2 disk led */
-      if (ld[ldn].is_disk)
-	{
+      /*if this is end of media read/write, may turn off PS/2 disk led */
+      if ((ld[ldn].device_type!=TYPE_NO_LUN)&&
+	  (ld[ldn].device_type!=TYPE_NO_DEVICE))
+	{ /* only access this, if there was a valid device addressed */
 	  switch (cmd->cmnd[0])
 	    {
 	    case READ_6:
 	    case WRITE_6:
 	    case READ_10:
 	    case WRITE_10:
+	    case READ_12:
+	    case WRITE_12:
 	      if (--disk_rw_in_progress == 0)
 		PS2_DISK_LED_OFF ();
 	    }
 	}
-
+       
       /*write device status into cmd->result, and call done function */
       if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE)
 	cmd->result = ld[ldn].tsb.dev_status & 0x1e;
@@ -512,8 +906,8 @@
 /*--------------------------------------------------------------------*/
 
 static void 
-issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg,
-	   unsigned char attn_reg)
+issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg, 
+           unsigned char attn_reg)
 {
   /*must wait for attention reg not busy */
   while (1)
@@ -540,51 +934,23 @@
 
 /*--------------------------------------------------------------------*/
 
-static int
-ibmmca_getinfo (char *buf, int slot, void *dev)
+static int ibmmca_getinfo (char *buf, int slot, void *dev)
 {
   struct Scsi_Host *shpnt = dev;
   int len = 0;
 
   len += sprintf (buf + len, "Subsystem PUN: %d\n", subsystem_pun);
-  len += sprintf (buf + len, "I/O base address: 0x%x\n", shpnt->io_port);
+  len += sprintf (buf + len, "I/O base address: 0x%x\n", IM_CMD_REG);
   return len;
 }
 
 /*--------------------------------------------------------------------*/
 
-static void 
-check_devices(struct Scsi_Host *shpnt)
-{
-  int is_disk, block_length;
-  int ldn;
-  int num_ldn = 0;
-
-  /* check ldn's from 0 to MAX_LOG_DEV to find which devices exist */
-  for (ldn = 0; ldn < MAX_LOG_DEV; ldn++)
-    {
-      if (device_exists(shpnt, ldn, &is_disk, &block_length))
-	{
-	  printk("IBM MCA SCSI: logical device found at ldn=%d.\n", ldn);
-	  ld[ldn].is_disk = is_disk;
-	  ld[ldn].block_length = block_length;
-	  get_ldn[num_ldn / 8][num_ldn % 8] = ldn;
-	  num_ldn++;
-	}
-    }
-
-  return;
-}
-
-/*--------------------------------------------------------------------*/
-
-static int 
-device_exists(struct Scsi_Host *shpnt, int ldn, int *is_disk,
-	      int *block_length)
-{
+/* SCSI-SCB-command for device_inquiry */
+static int device_inquiry(struct Scsi_Host *shpnt, int ldn, unsigned char *buf)
+{   
   struct im_scb scb;
   struct im_tsb tsb;
-  unsigned char buf[256];
   int retries;
 
   for (retries = 0; retries < 3; retries++)
@@ -592,7 +958,6 @@
       /*fill scb with inquiry command */
       scb.command = IM_DEVICE_INQUIRY_CMD;
       scb.enable = IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
-      /* I think this virt_to_bus is needed.. ??? AC */
       scb.sys_buf_adr = virt_to_bus(buf);
       scb.sys_buf_length = 255;
       scb.tsb_adr = virt_to_bus(&tsb);
@@ -611,19 +976,373 @@
   /*if all three retries failed, return "no device at this ldn" */
   if (retries >= 3)
     return 0;
+  else
+    return 1;
+}
+
+/* SCSI-immediate-command for assign. This functions maps/unmaps specific
+   ldn-numbers on SCSI (PUN,LUN). It is needed for presetting of the
+   subsystem and for dynamical remapping od ldns. */
+static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun, 
+                            unsigned int lun, unsigned int ldn, 
+                            unsigned int operation)
+{
+   int retries;
+   unsigned long imm_command;
+
+   for (retries=0; retries<3; retries ++)
+     {	
+        imm_command = inl(IM_CMD_REG);
+        imm_command &= (unsigned long)(0xF8000000); /* keep reserved bits */
+        imm_command |= (unsigned long)(IM_ASSIGN_IMM_CMD);
+        imm_command |= (unsigned long)((lun & 7) << 24);
+        imm_command |= (unsigned long)((operation & 1) << 23);
+        imm_command |= (unsigned long)((pun & 7) << 20);
+        imm_command |= (unsigned long)((ldn & 15) << 16);
+	
+        got_interrupt = 0;
+        issue_cmd (shpnt, (unsigned long)(imm_command), IM_IMM_CMD | 0xf);
+        while (!got_interrupt)
+           barrier ();
+	       
+        /*if command succesful, break */
+        if (stat_result == IM_IMMEDIATE_CMD_COMPLETED)
+           break;
+     }
+   
+   if (retries >= 3) 
+     return 0;
+   else
+     return 1;
+}
+
+/* type-interpreter for physical device numbers */
+static char *ti_p(int value)
+{
+   switch (value)
+     {
+	case TYPE_IBM_SCSI_ADAPTER: return("A"); break;
+	case TYPE_DISK:             return("D"); break;
+	case TYPE_TAPE:             return("T"); break;
+	case TYPE_PROCESSOR:        return("P"); break;
+	case TYPE_WORM:             return("W"); break;
+	case TYPE_ROM:              return("R"); break;
+	case TYPE_SCANNER:          return("S"); break;
+	case TYPE_MOD:              return("M"); break;
+        case TYPE_MEDIUM_CHANGER:   return("C"); break;
+	case TYPE_NO_LUN:           return("+"); break; /* show NO_LUN */
+        case TYPE_NO_DEVICE:
+	default:                    return("-"); break;
+     }
+   return("-");
+}
+
+/* type-interpreter for logical devices 
+   (A bit stupid, but it was necessary to get the '-' and the Hex-codes
+   into one type.) */
+static char *ti_l(int value)
+{
+   switch (value)
+     {
+	case 0:  return("0"); break; case 1:  return("1"); break;
+	case 2:  return("2"); break; case 3:  return("3"); break;
+	case 4:  return("4"); break; case 5:  return("5"); break;
+	case 6:  return("6"); break; case 7:  return("7"); break;
+	case 8:  return("8"); break; case 9:  return("9"); break;
+	case 10: return("a"); break; case 11: return("b"); break;
+        case 12: return("c"); break; case 13: return("d"); break;
+	case 14: return("e"); break; case 15: return("f"); break;
+	default: return("-"); break;
+     }
+   return("-");
+}
+
+/* 
+   The following routine probes the SCSI-devices in four steps:
+   1. The current ldn -> pun,lun mapping is removed on the SCSI-adapter.
+   2. ldn 0 is used to go through all possible combinations of pun,lun and
+      a device_inquiry is done to fiddle out whether there is a device
+      responding or not. This physical map is stored in get_scsi[][].
+   3. The 15 available ldns (0-14) are mapped to existing pun,lun.
+      If there are more devices than ldns, it stops at 14 for the boot
+      time. Dynamical remapping will be done in ibmmca_queuecommand.
+   4. If there are less than 15 valid pun,lun, the remaining ldns are
+      mapped to NON-existing pun,lun to satisfy the adapter. Information
+      about pun,lun -> ldn is stored as before in get_ldn[][].
+   This method leads to the result, that the SCSI-pun,lun shown to Linux
+   mid-level- and higher-level-drivers is exactly corresponding to the
+   physical reality on the SCSI-bus. Therefore, it is possible that users
+   of older releases of this driver have to rewrite their fstab-file, because
+   the /dev/sdXXX could have changed due to the right pun,lun report, now.
+   The assignment of ALL ldns avoids dynamical remapping by the adapter
+   itself.
+ */
+static void check_devices (struct Scsi_Host *shpnt)
+{
+  int id, lun, ldn;
+  unsigned char buf[256];
+  int count_devices = 0; /* local counter for connected device */
+   
+  /* assign default values to certain variables */
+  
+  IBM_DS.dyn_flag = 0; /* normally no need for dynamical ldn management */
+  next_ldn = 7; /* next ldn to be assigned is 7, because 0-6 is 'hardwired'*/
+  last_scsi_command = 0; /* emptify last SCSI-command storage */
+  
+  /* initialize the very important driver-informational arrays/structs */
+  memset (ld, 0, sizeof ld);
+  memset (get_ldn, TYPE_NO_DEVICE, sizeof get_ldn); /* this is essential ! */
+  memset (get_scsi, TYPE_NO_DEVICE, sizeof get_scsi); /* this is essential ! */
+
+  for (lun=0; lun<=7; lun++) /* mark the adapter at its pun on all luns*/
+    {
+      get_scsi[subsystem_pun][lun] = TYPE_IBM_SCSI_ADAPTER; 
+      get_ldn[subsystem_pun][lun] = MAX_LOG_DEV; /* make sure, the subsystem
+						    ldn is active for all
+						    luns. */
+    }
+
+  /* STEP 1: */
+  printk("IBM MCA SCSI: Removing current logical SCSI-device mapping.");
+  for (ldn=0; ldn<MAX_LOG_DEV; ldn++)
+    {
+#ifdef IM_DEBUG_PROBE
+      printk(".");
+#endif
+      immediate_assign(shpnt,0,0,ldn,REMOVE_LDN); /* remove ldn (wherever)*/
+    }
+
+  lun = 0;
+
+  /* STEP 2: */
+  printk("\nIBM MCA SCSI: Probing SCSI-devices.");
+  for (id=0; id<=7; id++)
+#ifdef CONFIG_SCSI_MULTI_LUN
+    for (lun=0; lun<=7; lun++)
+#endif
+      {
+#ifdef IM_DEBUG_PROBE
+	printk(".");
+#endif
+	if (id != subsystem_pun) 
+	  {            /* if pun is not the adapter: */
+	    immediate_assign(shpnt,id,lun,0,SET_LDN); /*set ldn=0 to pun,lun*/
+	    if (device_inquiry(shpnt, 0, buf)) /* probe device */
+	      {
+		get_scsi[id][lun]=(unsigned char)buf[0];  /* entry, even 
+							     for NO_LUN */
+		if (buf[0] != TYPE_NO_LUN)
+		  count_devices++; /* a existing device is found */
+	      }
+	    immediate_assign(shpnt,id,lun,0,REMOVE_LDN); /* remove ldn */
+	  }
+      }
+  
+  /* STEP 3: */
+  printk("\nIBM MCA SCSI: Mapping SCSI-devices.");
+   
+  ldn = 0;
+  lun = 0;
+
+#ifdef CONFIG_SCSI_MULTI_LUN   
+  for (lun=0; lun<=7 && ldn<MAX_LOG_DEV; lun++)
+#endif
+    for (id=0; id<=7 && ldn<MAX_LOG_DEV; id++)
+      {
+#ifdef IM_DEBUG_PROBE
+	printk(".");
+#endif
+	if (id != subsystem_pun)
+	  {
+	    if (get_scsi[id][lun] != TYPE_NO_LUN && 
+		get_scsi[id][lun] != TYPE_NO_DEVICE)
+	      {
+		/* Only map if accepted type. Always enter for 
+		   lun == 0 to get no gaps into ldn-mapping for ldn<7. */
+		immediate_assign(shpnt,id,lun,ldn,SET_LDN);
+		get_ldn[id][lun]=ldn; /* map ldn */
+		if (device_exists (shpnt, ldn, &ld[ldn].block_length,
+				   &ld[ldn].device_type))
+		  {
+#ifdef SCSI_IBMMCA_DEV_RESET
+		    int ticks;
+		    printk("(resetting)");
+		    ticks = IM_RESET_DELAY*HZ;
+		    reset_status = IM_RESET_IN_PROGRESS;
+		    issue_cmd (shpnt, IM_RESET_IMM_CMD, IM_IMM_CMD | ldn);
+		    while (reset_status == IM_RESET_IN_PROGRESS && --ticks) 
+		      {
+			udelay(1000000/HZ);
+			barrier();
+		      }
+		    /* if reset did not complete, just claim */
+		    if (!ticks) 
+		       {
+		          printk("IBM MCA SCSI: reset did not complete within %d seconds.\n",
+			         IM_RESET_DELAY);
+		          reset_status = IM_RESET_FINISHED_OK; 
+			                       /* did not work, finish */
+		       }
+#endif
+		    ldn++;
+		  }
+		else
+		  {
+		    /* device vanished, probably because we don't know how to
+		     * handle it or because it has problems */
+		     if (lun > 0)
+		       {
+			  /* remove mapping */
+			  get_ldn[id][lun]=TYPE_NO_DEVICE;
+			  immediate_assign(shpnt,0,0,ldn,REMOVE_LDN);
+		       }
+		     else ldn++;
+		  }
+	      }
+	   else if (lun == 0)
+	      {
+		 /* map lun == 0, even if no device exists */
+		 immediate_assign(shpnt,id,lun,ldn,SET_LDN);
+		 get_ldn[id][lun]=ldn; /* map ldn */
+		 ldn++;
+	      }
+	  }	 
+      }
+
+   /* STEP 4: */
+   
+   /* map remaining ldns to non-existing devices */
+   for (lun=1; lun<=7 && ldn<MAX_LOG_DEV; lun++)
+     for (id=0; id<=7 && ldn<MAX_LOG_DEV; id++)
+     {
+	if (get_scsi[id][lun] == TYPE_NO_LUN ||
+	    get_scsi[id][lun] == TYPE_NO_DEVICE)
+	  {
+	     /* Map remaining ldns only to NON-existing pun,lun
+	        combinations to make sure an inquiry will fail. 
+	        For MULTI_LUN, it is needed to avoid adapter autonome
+	        SCSI-remapping. */
+	     immediate_assign(shpnt,id,lun,ldn,SET_LDN);
+	     get_ldn[id][lun]=ldn;
+	     ldn++;
+	  }
+     }	
+	
+   printk("\n");
+
+#ifdef IM_DEBUG_PROBE
+   /* Show the physical and logical mapping during boot. */
+   printk("IBM MCA SCSI: Determined SCSI-device-mapping:\n");
+   printk("    Physical SCSI-Device Map               Logical SCSI-Device Map\n");
+   printk("ID\\LUN  0  1  2  3  4  5  6  7       ID\\LUN  0  1  2  3  4  5  6  7\n");
+   for (id=0; id<=7; id++)
+     {
+       printk("%2d     %2s %2s %2s %2s %2s %2s %2s %2s",
+	      id, ti_p(get_scsi[id][0]), ti_p(get_scsi[id][1]), 
+	      ti_p(get_scsi[id][2]), ti_p(get_scsi[id][3]), 
+	      ti_p(get_scsi[id][4]), ti_p(get_scsi[id][5]), 
+	      ti_p(get_scsi[id][6]), ti_p(get_scsi[id][7]));
+       printk("       %2d     %2s %2s %2s %2s %2s %2s %2s %2s\n",
+	      id, ti_l(get_ldn[id][0]), ti_l(get_ldn[id][1]), 
+	      ti_l(get_ldn[id][2]), ti_l(get_ldn[id][3]), 
+	      ti_l(get_ldn[id][4]), ti_l(get_ldn[id][5]),
+	      ti_l(get_ldn[id][6]), ti_l(get_ldn[id][7]));
+     }
+#endif
 
+   /* assign total number of found SCSI-devices to the statistics struct */
+   IBM_DS.total_scsi_devices = count_devices;
+    
+   /* decide for output in /proc-filesystem, if the configuration of
+      SCSI-devices makes dynamical reassignment of devices necessary */
+   if (count_devices>=MAX_LOG_DEV) 
+     IBM_DS.dyn_flag = 1; /* dynamical assignment is necessary */
+   else 
+     IBM_DS.dyn_flag = 0; /* dynamical assignment is not necessary */
+
+   /* If no SCSI-devices are assigned, return 1 in order to cause message. */
+   if (ldn == 0)
+     printk("IBM MCA SCSI: Warning: No SCSI-devices found/assignable!\n");
+
+  /* reset the counters for statistics on the current adapter */
+  IBM_DS.total_accesses = 0;
+  IBM_DS.total_interrupts = 0;
+  IBM_DS.dynamical_assignments = 0;
+  memset (IBM_DS.ldn_access, 0x0, sizeof (IBM_DS.ldn_access));
+  memset (IBM_DS.ldn_read_access, 0x0, sizeof (IBM_DS.ldn_read_access));
+  memset (IBM_DS.ldn_write_access, 0x0, sizeof (IBM_DS.ldn_write_access));
+  memset (IBM_DS.ldn_assignments, 0x0, sizeof (IBM_DS.ldn_assignments));
+   
+  return;
+}
+
+/*--------------------------------------------------------------------*/
+
+static int 
+device_exists (struct Scsi_Host *shpnt, int ldn, int *block_length, 
+	       int *device_type)
+{
+  struct im_scb scb;
+  struct im_tsb tsb;
+  unsigned char buf[256];
+  int retries;
+
+  /* if no valid device found, return immediately with 0 */
+  if (!(device_inquiry(shpnt, ldn, buf))) return 0;
+   
   /*if device is CD_ROM, assume block size 2048 and return */
   if (buf[0] == TYPE_ROM)
     {
-      *is_disk = 0;
+      *device_type = TYPE_ROM;
+      *block_length = 2048; /* (standard blocksize for yellow-/red-book) */
+      return 1;
+    }
+  
+  if (buf[0] == TYPE_WORM) /* CD-burner, WORM, Linux handles this as CD-ROM 
+			      therefore, the block_length is also 2048. */
+    {
+      *device_type = TYPE_WORM;
       *block_length = 2048;
       return 1;
     }
-
-  /*if device is disk, use "read capacity" to find its block size */
+   
+  /* if device is disk, use "read capacity" to find its block size */
   if (buf[0] == TYPE_DISK)
     {
-      *is_disk = 1;
+      *device_type = TYPE_DISK;
+
+      for (retries = 0; retries < 3; retries++)
+	{
+	  /*fill scb with read capacity command */
+	  scb.command = IM_READ_CAPACITY_CMD;
+	  scb.enable = IM_READ_CONTROL;
+	  scb.sys_buf_adr = virt_to_bus(buf);
+	  scb.sys_buf_length = 8;
+	  scb.tsb_adr = virt_to_bus(&tsb);
+
+	  /*issue scb to passed ldn, and busy wait for interrupt */
+	  got_interrupt = 0;
+	  issue_cmd (shpnt, virt_to_bus(&scb), IM_SCB | ldn);
+	  while (!got_interrupt)
+	    barrier ();
+
+	  /*if got capacity, get block length and return one device found */
+	  if (stat_result == IM_SCB_CMD_COMPLETED)
+	    {
+	      *block_length = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24);
+	      return 1;
+	    }
+	}
+
+      /*if all three retries failed, return "no device at this ldn" */
+      if (retries >= 3)
+	return 0;
+    }
+
+  /* if this is a magneto-optical drive, treat it like a harddisk */
+  if (buf[0] == TYPE_MOD)
+    {
+      *device_type = TYPE_MOD;
 
       for (retries = 0; retries < 3; retries++)
 	{
@@ -651,9 +1370,39 @@
       /*if all three retries failed, return "no device at this ldn" */
       if (retries >= 3)
 	return 0;
+    }   
+   
+  if (buf[0] == TYPE_TAPE) /* TAPE-device found */
+    {
+      *device_type = TYPE_TAPE;
+      *block_length = 0; /* not in use (setting by mt and mtst in op.) */
+      return 1;   
     }
 
-  /*for now, ignore tape and other devices - return 0 */
+  if (buf[0] == TYPE_PROCESSOR) /* HP-Scanners, diverse SCSI-processing units*/
+    {
+      *device_type = TYPE_PROCESSOR;
+      *block_length = 0; /* they set their stuff on drivers */
+      return 1;
+    }
+  
+  if (buf[0] == TYPE_SCANNER) /* other SCSI-scanners */
+    {
+      *device_type = TYPE_SCANNER;
+      *block_length = 0; /* they set their stuff on drivers */
+      return 1;
+    }
+
+  if (buf[0] == TYPE_MEDIUM_CHANGER) /* Medium-Changer */
+    {
+      *device_type = TYPE_MEDIUM_CHANGER;
+      *block_length = 0; /* One never knows, what to expect on a medium
+			    changer device. */
+      return 1;
+    }
+
+  /* Up to now, no SCSI-devices that are known up to kernel 2.1.31 are
+     ignored! MO-drives are now supported and treated as harddisk. */   
   return 0;
 }
 
@@ -664,19 +1413,17 @@
 void 
 ibmmca_scsi_setup (char *str, int *ints)
 {
-  int i;
+   int i;
 
-  for (i = 0; i < IM_MAX_HOSTS && i < ints[0]; i++)
-    {
+   for (i = 0; i < IM_MAX_HOSTS && i < ints[0]; i++)
       io_port[i] = ints[i+1];
-    }
 }
 
 #endif
 
 /*--------------------------------------------------------------------*/
 
-int 
+int
 ibmmca_detect (Scsi_Host_Template * template)
 {
   struct Scsi_Host *shpnt;
@@ -697,11 +1444,11 @@
   /* if ibmmcascsi setup option was passed to kernel, return "found" */
   for (i = 0; i < IM_MAX_HOSTS; i++)
     if (io_port[i] > 0 && scsi_id[i] >= 0 && scsi_id[i] < 8)
-    {
+      {
       printk("IBM MCA SCSI: forced detection, io=0x%x, scsi id=%d.\n",
-	      io_port[i], scsi_id[i]);
+              io_port[i], scsi_id[i]);
       ibmmca_register(template, io_port[i], scsi_id[i]);
-    }
+      }
   if (found) return found;
 
   /* first look for the SCSI integrated on the motherboard */
@@ -712,13 +1459,13 @@
       port = IM_IO_PORT + ((pos2 & 0x0e) << 2);
       id = (pos3 & 0xe0) >> 5;
       printk("IBM MCA SCSI: integrated SCSI found, io=0x%x, scsi id=%d.\n",
-	      port, id);
+              port, id);
       if ((shpnt = ibmmca_register(template, port, id)))
-	{
-	  mca_set_adapter_name(MCA_INTEGSCSI, "PS/2 Integrated SCSI");
-	  mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo,
-				 shpnt);
-	}
+        {
+          mca_set_adapter_name(MCA_INTEGSCSI, "PS/2 Integrated SCSI");
+          mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo,
+                                 shpnt);
+        }
     }
 
   /* now look for other adapters */
@@ -727,22 +1474,22 @@
     {
       slot = 0;
       while ((slot = mca_find_adapter(subsys_list[i].mca_id, slot))
-	     != MCA_NOTFOUND)
-	{
-	  pos2 = mca_read_stored_pos(slot, 2);
-	  pos3 = mca_read_stored_pos(slot, 3);
-	  port = IM_IO_PORT + ((pos2 & 0x0e) << 2);
-	  id = (pos3 & 0xe0) >> 5;
-	  printk ("IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d.\n",
-		  subsys_list[i].description, slot + 1, port, id);
-	  if ((shpnt = ibmmca_register(template, port, id)))
-	    {
-	      mca_set_adapter_name (slot, subsys_list[i].description);
-	      mca_set_adapter_procfn (slot, (MCA_ProcFn) ibmmca_getinfo,
-				      shpnt);
-	    }
-	  slot++;
-	}
+             != MCA_NOTFOUND)
+        {
+          pos2 = mca_read_stored_pos(slot, 2);
+          pos3 = mca_read_stored_pos(slot, 3);
+          port = IM_IO_PORT + ((pos2 & 0x0e) << 2);
+          id = (pos3 & 0xe0) >> 5;
+          printk ("IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d.\n",
+                  subsys_list[i].description, slot + 1, port, id);
+          if ((shpnt = ibmmca_register(template, port, id)))
+            {
+              mca_set_adapter_name (slot, subsys_list[i].description);
+              mca_set_adapter_procfn (slot, (MCA_ProcFn) ibmmca_getinfo,
+                                      shpnt);
+            }
+          slot++;
+        }
     }
 
   if (!found) {
@@ -753,8 +1500,6 @@
   return found;
 }
 
-/*--------------------------------------------------------------------*/
-
 static struct Scsi_Host *
 ibmmca_register(Scsi_Host_Template * template, int port, int id)
 {
@@ -765,7 +1510,7 @@
   if (check_region(port, IM_N_IO_PORT))
     {
       printk("IBM MCA SCSI: Unable to get I/O region 0x%x-0x%x.\n",
-	port, port + IM_N_IO_PORT);
+        port, port + IM_N_IO_PORT);
       return NULL;
     }
 
@@ -785,6 +1530,9 @@
   shpnt->io_port = port;
   shpnt->n_io_port = IM_N_IO_PORT;
   shpnt->this_id = id;
+
+  reset_status = IM_RESET_NOT_IN_PROGRESS;
+
   for (i = 0; i < 8; i++)
     for (j = 0; j < 8; j++)
       get_ldn[i][j] = MAX_LOG_DEV;
@@ -800,17 +1548,6 @@
 
 /*--------------------------------------------------------------------*/
 
-int
-ibmmca_release(struct Scsi_Host *shpnt)
-{
-  release_region(shpnt->io_port, shpnt->n_io_port);
-  if (!(--found))
-    free_irq(shpnt->irq, hosts);
-  return 0;
-}
-
-/*--------------------------------------------------------------------*/
-
 int 
 ibmmca_command (Scsi_Cmnd * cmd)
 {
@@ -823,21 +1560,134 @@
 
 /*--------------------------------------------------------------------*/
 
-int 
-ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+int
+ibmmca_release(struct Scsi_Host *shpnt)
+{
+  release_region(shpnt->io_port, shpnt->n_io_port);
+  if (!(--found))
+    free_irq(shpnt->irq, hosts);
+  return 0;
+}
+
+/*--------------------------------------------------------------------*/
+
+/* The following routine is the SCSI command queue. The old edition is
+   now improved by dynamical reassignment of ldn numbers that are 
+   currently not assigned. The mechanism works in a way, that first
+   the physical structure is checked. If at a certain pun,lun a device
+   should be present, the routine proceeds to the ldn check from
+   get_ldn. An answer of 0xff would show-up, that the aimed device is
+   currently not assigned any ldn. At this point, the dynamical 
+   remapping algorithm is called. It works in a way, that it goes in
+   cyclic order through the ldns from 7 to 14. If a ldn is assigned,
+   it takes 8 dynamical reassignment calls, until a device looses its
+   ldn again. With this method it is assured, that while doing 
+   intense I/O between up to eight devices, no dynamical remapping is
+   done there. ldns 0 through 6(!) are left untouched, which means, that
+   puns 0 through 7(!) on lun=0 are always accessible without remapping.
+   These ldns are statically assigned by this driver. The subsystem always 
+   occupies at least one pun, therefore 7 ldns (at lun=0) for other devices 
+   are sufficient. (The adapter uses always ldn=15, at whatever pun it is.) */
+int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
 {
   unsigned int ldn;
   unsigned int scsi_cmd;
   struct im_scb *scb;
   struct Scsi_Host *shpnt = cmd->host;
+  
+  int current_ldn;
+  int id,lun;
 
-  /*if (target,lun) unassigned, return error */
+  /*if (target,lun) is NO LUN or not existing at all, return error */
+  if ((get_scsi[cmd->target][cmd->lun] == TYPE_NO_LUN)||
+      (get_scsi[cmd->target][cmd->lun] == TYPE_NO_DEVICE))
+     {
+	cmd->result = DID_NO_CONNECT << 16;
+	done (cmd);
+	return 0;
+     }
+   
+  /*if (target,lun) unassigned, do further checks... */
   ldn = get_ldn[cmd->target][cmd->lun];
-  if (ldn >= MAX_LOG_DEV)
+  if (ldn >= MAX_LOG_DEV) /* on invalid ldn do special stuff */
     {
-      cmd->result = DID_NO_CONNECT << 16;
-      done (cmd);
-      return 0;
+      if (ldn > MAX_LOG_DEV) /* dynamical remapping if ldn unassigned */
+	 {
+	    current_ldn = next_ldn; /* stop-value for one circle */
+	    while (ld[next_ldn].cmd) /* search for a occupied, but not in */
+	      {                      /* command-processing ldn. */
+		 next_ldn ++;
+		 if (next_ldn>=MAX_LOG_DEV) 
+		   next_ldn = 7;
+		 if (current_ldn == next_ldn) /* One circle done ? */
+		   {         /* no non-processing ldn found */
+		      printk("IBM MCA SCSI: Cannot assign SCSI-device dynamically!\n");
+		      printk("              On ldn 7-14 SCSI-commands everywhere in progress.\n");
+		      printk("              Reporting DID_NO_CONNECT for device (%d,%d).\n",
+			     cmd->target, cmd->lun);
+		      cmd->result = DID_NO_CONNECT << 16;/* return no connect*/
+		      done (cmd);
+		      return 0;
+		   }
+	      }
+
+	    /* unmap non-processing ldn */
+	    for (id=0; id<=7; id ++)
+	      for (lun=0; lun<=7; lun++)
+	      {
+		 if (get_ldn[id][lun] == next_ldn)
+		   {
+		      get_ldn[id][lun] = TYPE_NO_DEVICE; /* unmap entry */
+		      goto DYN_ASSIGN;  /* jump out as fast as possible */
+		   }
+	      }
+
+DYN_ASSIGN:	    
+	    /* unassign found ldn (pun,lun does not matter for remove) */
+	    immediate_assign(shpnt,0,0,next_ldn,REMOVE_LDN);
+	    /* assign found ldn to aimed pun,lun */
+	    immediate_assign(shpnt,cmd->target,cmd->lun,next_ldn,SET_LDN);
+	    /* map found ldn to pun,lun */
+	    get_ldn[cmd->target][cmd->lun] = next_ldn;
+            /* change ldn to the right value, that is now next_ldn */
+	    ldn = next_ldn;
+	    /* set reduced interrupt_handler-mode for checking */
+	    local_checking_phase_flag = 1;
+	    /* get device information for ld[ldn] */
+	    if (device_exists (shpnt, ldn, &ld[ldn].block_length,
+			       &ld[ldn].device_type))
+	       {
+		 ld[ldn].cmd = 0; /* To prevent panic set 0, because
+				     devices that were not assigned,
+				     should have nothing in progress. */
+		  
+		 /* increase assignment counters for statistics in /proc */
+		 IBM_DS.dynamical_assignments++;
+		 IBM_DS.ldn_assignments[ldn]++;
+	       }
+	    else
+	         /* panic here, because a device, found at boottime has 
+		    vanished */
+	         panic("IBM MCA SCSI: ldn=0x%x, SCSI-device on (%d,%d) vanished!\n",
+		       ldn, cmd->target, cmd->lun);
+	    
+	    /* set back to normal interrupt_handling */
+	    local_checking_phase_flag = 0;
+	    
+	    /* Information on syslog terminal */
+	    printk("IBM MCA SCSI: ldn=0x%x dynamically reassigned to (%d,%d).\n",
+		   ldn, cmd->target, cmd->lun);
+	    
+	    /* increase next_ldn for next dynamical assignment */ 
+	    next_ldn ++;
+	    if (next_ldn>=MAX_LOG_DEV) next_ldn = 7;
+	 }
+      else
+	 {  /* wall against Linux accesses to the subsystem adapter */	 
+            cmd->result = DID_NO_CONNECT << 16;
+            done (cmd);
+            return 0;
+	 }
     }
 
   /*verify there is no command already in progress for this log dev */
@@ -875,71 +1725,149 @@
 
   /*fill scb information dependent on scsi command */
   scsi_cmd = cmd->cmnd[0];
+   
 #ifdef IM_DEBUG_CMD
   printk("issue scsi cmd=%02x to ldn=%d\n", scsi_cmd, ldn);
 #endif
+
+  /* for specific device debugging: */
+#ifdef IM_DEBUG_CMD_SPEC_DEV
+  if (ld[ldn].device_type==IM_DEBUG_CMD_DEVICE)
+     printk("(SCSI-device-type=0x%x) issue scsi cmd=%02x to ldn=%d\n", 
+	    ld[ldn].device_type, scsi_cmd, ldn);
+#endif
+
+  /* for possible panics store current command */
+  last_scsi_command = scsi_cmd; 
+   
+  /* update statistical info */
+  IBM_DS.total_accesses++;
+  IBM_DS.ldn_access[ldn]++;
+   
   switch (scsi_cmd)
     {
     case READ_6:
     case WRITE_6:
     case READ_10:
     case WRITE_10:
-      if (scsi_cmd == READ_6 || scsi_cmd == READ_10)
-	{
-	  scb->command = IM_READ_DATA_CMD;
-	  scb->enable |= IM_READ_CONTROL;
-	}
-      else
-	{
-	  scb->command = IM_WRITE_DATA_CMD;
-	}
-      if (scsi_cmd == READ_6 || scsi_cmd == WRITE_6)
-	{
-	  scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[3]) << 0) |
-	    (((unsigned) cmd->cmnd[2]) << 8) |
-	    ((((unsigned) cmd->cmnd[1]) & 0x1f) << 16);
-	  scb->u2.blk.count = (unsigned) cmd->cmnd[4];
-	}
-      else
-	{
-	  scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[5]) << 0) |
-	    (((unsigned) cmd->cmnd[4]) << 8) |
-	    (((unsigned) cmd->cmnd[3]) << 16) |
-	    (((unsigned) cmd->cmnd[2]) << 24);
-	  scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) |
-	    (((unsigned) cmd->cmnd[7]) << 8);
-	}
-      scb->u2.blk.length = ld[ldn].block_length;
-      if (ld[ldn].is_disk)
-	{
-	  if (++disk_rw_in_progress == 1)
-	    PS2_DISK_LED_ON ();
-	}
+    case READ_12:
+    case WRITE_12:       
+      /* statistics for proc_info */
+      if ((scsi_cmd == READ_6)||(scsi_cmd == READ_10)||(scsi_cmd == READ_12))
+	 IBM_DS.ldn_read_access[ldn]++; /* increase READ-access on ldn stat. */
+      else if ((scsi_cmd == WRITE_6)||(scsi_cmd == WRITE_10)||
+	       (scsi_cmd == WRITE_12))
+	 IBM_DS.ldn_write_access[ldn]++; /* increase write-count on ldn stat.*/
+
+      /* Distinguish between disk and other devices. Only disks (that are the
+	 most frequently accessed devices) should be supported by the 
+         IBM-SCSI-Subsystem commands. */
+      switch (ld[ldn].device_type)
+	 {
+	  case TYPE_DISK: /* for harddisks enter here ... */
+	  case TYPE_MOD:  /* ... try it also for MO-drives (send flames as */
+			  /* you like, if this won't work.) */
+           if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || 
+	       scsi_cmd == READ_12)
+	     {
+	       scb->command = IM_READ_DATA_CMD;
+	       scb->enable |= IM_READ_CONTROL;
+	     }
+           else
+	     {
+	       scb->command = IM_WRITE_DATA_CMD;
+	     }
+           if (scsi_cmd == READ_6 || scsi_cmd == WRITE_6)
+	     {
+	       scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[3]) << 0) |
+	         (((unsigned) cmd->cmnd[2]) << 8) |
+	         ((((unsigned) cmd->cmnd[1]) & 0x1f) << 16);
+	       scb->u2.blk.count = (unsigned) cmd->cmnd[4];
+	     }
+           else
+	     {
+	       scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[5]) << 0) |
+	         (((unsigned) cmd->cmnd[4]) << 8) |
+	         (((unsigned) cmd->cmnd[3]) << 16) |
+	         (((unsigned) cmd->cmnd[2]) << 24);
+	       scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) |
+	         (((unsigned) cmd->cmnd[7]) << 8);
+	     }
+           scb->u2.blk.length = ld[ldn].block_length;
+	   if (++disk_rw_in_progress == 1)
+	      PS2_DISK_LED_ON (shpnt->host_no, cmd->target);
+	  break;
+	    
+	  /* for other devices, enter here. Other types are not known by
+	     Linux! TYPE_NO_LUN is forbidden as valid device. */
+          case TYPE_ROM:
+	  case TYPE_TAPE:
+	  case TYPE_PROCESSOR:
+	  case TYPE_WORM:
+	  case TYPE_SCANNER:
+	  case TYPE_MEDIUM_CHANGER:
+	  
+	   /* If there is a sequential-device, IBM recommends to use
+	      IM_OTHER_SCSI_CMD_CMD instead of subsystem READ/WRITE. 
+	      Good/modern CD-ROM-drives are capable of
+	      reading sequential AND random-access. This leads to the problem,
+	      that random-accesses are covered by the subsystem, but 
+	      sequentials are not, as like for tape-drives. Therefore, it is
+	      the easiest way to use IM_OTHER_SCSI_CMD_CMD for all read-ops
+	      on CD-ROM-drives in order not to run into timing problems and
+	      to have a stable state. In addition, data-access on CD-ROMs
+	      works faster like that. Strange, but obvious. */
+	    
+           scb->command = IM_OTHER_SCSI_CMD_CMD;
+	   if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || 
+	       scsi_cmd == READ_12) /* enable READ */
+              scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
+	   else
+	      scb->enable |= IM_SUPRESS_EXCEPTION_SHORT; /* assume WRITE */
+	    
+           scb->u1.scsi_cmd_length = cmd->cmd_len;
+           memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+	    
+	   /* Read/write on this non-disk devices is also displayworthy, 
+	      so flash-up the LED/display. */
+	   if (++disk_rw_in_progress == 1)
+	      PS2_DISK_LED_ON (shpnt->host_no, cmd->target);
+	 break;
+	 }
       break;
-
     case INQUIRY:
       scb->command = IM_DEVICE_INQUIRY_CMD;
-      scb->enable |= IM_READ_CONTROL |
-	IM_SUPRESS_EXCEPTION_SHORT;
+      scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
       break;
 
     case READ_CAPACITY:
       scb->command = IM_READ_CAPACITY_CMD;
       scb->enable |= IM_READ_CONTROL;
-      /*the length of system memory buffer must be exactly 8 bytes */
+      /* the length of system memory buffer must be exactly 8 bytes */
       if (scb->sys_buf_length >= 8)
 	scb->sys_buf_length = 8;
       break;
 
+    /* Commands that need read-only-mode (system <- device): */
     case REQUEST_SENSE:
       scb->command = IM_REQUEST_SENSE_CMD;
       scb->enable |= IM_READ_CONTROL;
       break;
-
+       
+    /* Commands that need write-only-mode (system -> device): */
+    case MODE_SELECT:
+    case MODE_SELECT_10:
+      scb->command = IM_OTHER_SCSI_CMD_CMD;      
+      scb->enable |= IM_SUPRESS_EXCEPTION_SHORT; /*Select needs WRITE-enabled*/
+      scb->u1.scsi_cmd_length = cmd->cmd_len;
+      memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+      break;
+            
+    /* For other commands, read-only is useful. Most other commands are 
+       running without an input-data-block. */
     default:
       scb->command = IM_OTHER_SCSI_CMD_CMD;
-      scb->enable |= IM_READ_CONTROL |
-	IM_SUPRESS_EXCEPTION_SHORT;
+      scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
       scb->u1.scsi_cmd_length = cmd->cmd_len;
       memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
       break;
@@ -958,10 +1886,10 @@
   /* The code below doesn't work right now, so we tell the upper layer
      that we can't abort. This eventually causes a reset.
      */
-  return SCSI_ABORT_SNOOZE;
+  return SCSI_ABORT_SNOOZE ;
 
 #if 0
-  struct Scsi_Host *shpnt = cmd->host;
+  struct Scsi_host *shpnt = cmd->host;
   unsigned int ldn;
   void (*saved_done) (Scsi_Cmnd *);
 
@@ -986,7 +1914,7 @@
   saved_done = cmd->scsi_done;
   cmd->scsi_done = internal_done;
   cmd->SCp.Status = 0;
-  issue_cmd (shpnt, IM_ABORT_IMM_CMD, IM_IMM_CMD | ldn);
+  issue_cmd (shpnt, T_IMM_CMD, IM_IMM_CMD | ldn);
   while (!cmd->SCp.Status)
     barrier ();
 
@@ -1004,7 +1932,7 @@
 
 /*--------------------------------------------------------------------*/
 
-int 
+int
 ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags)
 {
   struct Scsi_Host *shpnt = cmd->host;
@@ -1024,15 +1952,14 @@
     udelay(1000000/HZ);
     barrier();
   }
-
   /* if reset did not complete, just return an error*/
   if (!ticks) {
     printk("IBM MCA SCSI: reset did not complete within %d seconds.\n",
-	   IM_RESET_DELAY);
+           IM_RESET_DELAY);
     reset_status = IM_RESET_FINISHED_FAIL;
     return SCSI_RESET_ERROR;
   }
-  
+
   /* if reset failed, just return an error */
   if (reset_status == IM_RESET_FINISHED_FAIL) {
     printk("IBM MCA SCSI: reset failed.\n");
@@ -1045,13 +1972,13 @@
     int i;
     for (i = 0; i < MAX_LOG_DEV; i++)
       {
-	Scsi_Cmnd *cmd = ld[i].cmd;
-	if (cmd && cmd->scsi_done)
-	  {
-	    ld[i].cmd = 0;
-	    cmd->result = DID_RESET;
-	    (cmd->scsi_done) (cmd);
-	  }
+        Scsi_Cmnd *cmd = ld[i].cmd;
+        if (cmd && cmd->scsi_done)
+          {
+            ld[i].cmd = 0;
+            cmd->result = DID_RESET;
+            (cmd->scsi_done) (cmd);
+          }
       }
   }
   return SCSI_RESET_SUCCESS;
@@ -1082,7 +2009,114 @@
   return 0;
 }
 
-/*--------------------------------------------------------------------*/
+/* calculate percentage of total accesses on a ldn */
+static int ldn_access_load(struct Scsi_Host *shpnt, int ldn)
+{
+   if (IBM_DS.total_accesses == 0) return (0);
+   if (IBM_DS.ldn_access[ldn] == 0) return (0);
+   return((int)(((float)IBM_DS.ldn_access[ldn]/(float)IBM_DS.total_accesses)*(float)100.000));
+}
+
+/* calculate total amount of r/w-accesses */
+static int ldn_access_total_read_write(struct Scsi_Host *shpnt)
+{
+   int a = 0;
+   int i;
+   
+   for (i=0; i<=MAX_LOG_DEV; i++)
+     a+=IBM_DS.ldn_read_access[i]+IBM_DS.ldn_write_access[i];
+   return(a);
+}
+
+/* routine to display info in the proc-fs-structure (a deluxe feature) */
+int ibmmca_proc_info (char *buffer, char **start, off_t offset, int length,
+		      int hostno, int inout)
+{
+   int len=0;
+   int i,id;
+   struct Scsi_Host *shpnt;
+
+   for (i = 0; hosts[i] && hosts[i]->host_no != hostno; i++);
+   shpnt = hosts[i];
+   if (!shpnt) {
+       len += sprintf(buffer+len, "\nCan't find adapter for host number %d\n", hostno);
+       return len;
+   }
+
+   cli();
+
+   len += sprintf(buffer+len, "\n             IBM-SCSI-Subsystem-Linux-Driver, Version %s\n\n\n",
+		  IBMMCA_SCSI_DRIVER_VERSION);
+   len += sprintf(buffer+len, " SCSI Access-Statistics:\n");
+#ifdef CONFIG_SCSI_MULTI_LUN
+   len += sprintf(buffer+len, "               Multiple LUN probing.....: Yes\n");
+#else
+   len += sprintf(buffer+len, "               Multiple LUN probing.....: No\n");
+#endif
+   len += sprintf(buffer+len, "               This Hostnumber..........: %d\n",
+		  hostno);
+   len += sprintf(buffer+len, "               Base I/O-Port............: 0x%x\n",
+		  IM_CMD_REG);
+   len += sprintf(buffer+len, "               (Shared) IRQ.............: %d\n",
+		  IM_IRQ);
+   len += sprintf(buffer+len, "               Total Interrupts.........: %d\n",
+		  IBM_DS.total_interrupts);
+   len += sprintf(buffer+len, "               Total SCSI Accesses......: %d\n",
+		  IBM_DS.total_accesses);
+   len += sprintf(buffer+len, "                 Total SCSI READ/WRITE..: %d\n",
+		  ldn_access_total_read_write(shpnt));
+   len += sprintf(buffer+len, "                 Total SCSI other cmds..: %d\n\n",
+		  IBM_DS.total_accesses - ldn_access_total_read_write(shpnt));
+   
+   len += sprintf(buffer+len, " Logical-Device-Number (LDN) Access-Statistics:\n");
+   len += sprintf(buffer+len, "         LDN | Accesses [%%] |   READ    |   WRITE   | ASSIGNMENTS\n");
+   len += sprintf(buffer+len, "        -----|--------------|-----------|-----------|--------------\n");
+   for (i=0; i<=MAX_LOG_DEV; i++)
+      len += sprintf(buffer+len, "         %2X  |    %3d       |  %8d |  %8d | %8d\n",
+		     i, ldn_access_load(shpnt, i), IBM_DS.ldn_read_access[i],
+		     IBM_DS.ldn_write_access[i], IBM_DS.ldn_assignments[i]);
+   len += sprintf(buffer+len, "        -----------------------------------------------------------\n\n");
+   
+   len += sprintf(buffer+len, " Dynamical-LDN-Assignment-Statistics:\n");
+   len += sprintf(buffer+len, "               Number of physical SCSI-devices..: %d (+ Adapter)\n",
+		  IBM_DS.total_scsi_devices);
+   len += sprintf(buffer+len, "               Dynamical Assignment necessaray..: %s\n", 
+		  IBM_DS.dyn_flag ? "Yes" : "No ");
+   len += sprintf(buffer+len, "               Next LDN to be assigned..........: 0x%x\n",
+		  next_ldn);
+   len += sprintf(buffer+len, "               Dynamical assignments done yet...: %d\n",
+		  IBM_DS.dynamical_assignments);
+
+   len += sprintf(buffer+len, "\n Current SCSI-Device-Mapping:\n");
+   len += sprintf(buffer+len, "        Physical SCSI-Device Map               Logical SCSI-Device Map\n");
+   len += sprintf(buffer+len, "    ID\\LUN  0  1  2  3  4  5  6  7       ID\\LUN  0  1  2  3  4  5  6  7\n");
+   for (id=0; id<=7; id++)
+     {
+	len += sprintf(buffer+len, "    %2d     %2s %2s %2s %2s %2s %2s %2s %2s",
+	       id, ti_p(get_scsi[id][0]), ti_p(get_scsi[id][1]), 
+	       ti_p(get_scsi[id][2]), ti_p(get_scsi[id][3]), 
+	       ti_p(get_scsi[id][4]), ti_p(get_scsi[id][5]), 
+	       ti_p(get_scsi[id][6]), ti_p(get_scsi[id][7]));
+	len += sprintf(buffer+len, "       %2d     %2s %2s %2s %2s %2s %2s %2s %2s\n",
+	       id, ti_l(get_ldn[id][0]), ti_l(get_ldn[id][1]), 
+	       ti_l(get_ldn[id][2]), ti_l(get_ldn[id][3]), 
+	       ti_l(get_ldn[id][4]), ti_l(get_ldn[id][5]),
+	       ti_l(get_ldn[id][6]), ti_l(get_ldn[id][7]));
+     }
+   
+   len += sprintf(buffer+len, "(A = IBM-Subsystem, D = Harddisk, T = Tapedrive, P = Processor, W = WORM,\n");
+   len += sprintf(buffer+len, " R = CD-ROM, S = Scanner, M = MO-Drive, C = Medium-Changer, + = unprovided LUN,\n");
+   len += sprintf(buffer+len, " - = nothing found)\n\n");
+   
+   *start = buffer + offset;
+   len -= offset;
+   if (len > length) 
+     len = length;
+   
+   sti();
+   
+   return len;
+}
 
 #ifdef MODULE
 /* Eventually this will go into an include file, but this will be later */
@@ -1091,3 +2125,4 @@
 #include "scsi_module.c"
 #endif
 
+/*--------------------------------------------------------------------*/
diff -u --recursive --new-file v2.1.58/linux/drivers/scsi/ibmmca.h linux/drivers/scsi/ibmmca.h
--- v2.1.58/linux/drivers/scsi/ibmmca.h	Fri Apr  4 08:52:23 1997
+++ linux/drivers/scsi/ibmmca.h	Fri Oct 17 13:59:30 1997
@@ -1,12 +1,12 @@
-
 #ifndef _IBMMCA_H
 #define _IBMMCA_H
 
-/*
+/* 
  * Low Level Driver for the IBM Microchannel SCSI Subsystem
  */
 
 /*services provided to the higher level of Linux SCSI driver */
+int ibmmca_proc_info (char *, char **, off_t, int, int, int);
 int ibmmca_detect (Scsi_Host_Template *);
 int ibmmca_release (struct Scsi_Host *);
 int ibmmca_command (Scsi_Cmnd *);
@@ -21,9 +21,9 @@
 /*initialization for Scsi_host_template type */
 #define IBMMCA {                                      \
           NULL,                 /*next*/              \
-          NULL,                 /*module*/	      \
+          NULL,                 /*usage_count*/       \
           &proc_scsi_ibmmca,    /*proc_dir*/          \
-          NULL,                 /*proc info fn*/      \
+          ibmmca_proc_info,     /*proc info fn*/      \
           "IBMMCA",             /*name*/              \
           ibmmca_detect,        /*detect fn*/         \
           ibmmca_release,       /*release fn*/        \
diff -u --recursive --new-file v2.1.58/linux/fs/ncpfs/dir.c linux/fs/ncpfs/dir.c
--- v2.1.58/linux/fs/ncpfs/dir.c	Thu Sep 11 09:02:23 1997
+++ linux/fs/ncpfs/dir.c	Thu Oct 16 08:10:14 1997
@@ -216,7 +216,7 @@
 	int result = 0;
 	int i = 0;
 	int index = 0;
-	struct inode *inode = file->f_dentry->d_inode;
+	struct inode *inode = filp->f_dentry->d_inode;
 	struct ncp_dirent *entry = NULL;
 	struct ncp_server *server = NCP_SERVER(inode);
 	struct ncp_inode_info *dir = NCP_INOP(inode);
diff -u --recursive --new-file v2.1.58/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.1.58/linux/fs/nfs/dir.c	Wed Oct 15 16:04:23 1997
+++ linux/fs/nfs/dir.c	Wed Oct 15 21:08:46 1997
@@ -180,7 +180,7 @@
 			goto again;
 		}
 
-		if (ismydir && cache->mtime != NFS_OLDMTIME(inode))
+		if (ismydir && cache->mtime != inode->i_mtime)
 			cache->valid = 0;
 
 		if (!cache->valid || cache->age < dead) {
@@ -207,7 +207,7 @@
 			if (j < cache->size - 1) {
 				index = j + 1;
 				entry = this_ent + 3;
-			} else if (*(this_ent+2) >> 16) {
+			} else if (*(this_ent+2) & (1 << 15)) {
 				/* eof */
 				return 0;
 			}
@@ -253,7 +253,7 @@
 		cache->valid = 1;
 		entry = cache->entry + (index = 0);
 	}
-	cache->mtime = NFS_OLDMTIME(inode);
+	cache->mtime = inode->i_mtime;
 	cache->age = jiffies;
 
 	/*
@@ -926,99 +926,6 @@
 		/* Update the dcache */
 		d_move(old_dentry, new_dentry);
 	}
-	return error;
-}
-
-/*
- * Many nfs protocol calls return the new file attributes after
- * an operation.  Here we update the inode to reflect the state
- * of the server's inode.
- */
-
-int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
-{
-	int error = -EIO;
-
-	dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n",
-		 inode->i_dev, inode->i_ino, inode->i_count);
-
-	if (!inode || !fattr) {
-		printk("nfs_refresh_inode: inode or fattr is NULL\n");
-		goto out;
-	}
-	if (inode->i_ino != fattr->fileid) {
-		printk("nfs_refresh_inode: inode number mismatch\n");
-		goto out;
-	}
-	/*
-	 * Check whether the mode has been set, as we only want to
-	 * do this once. (We don't allow inodes to change types.)
-	 */
-	if (inode->i_mode == 0) {
-		inode->i_mode = fattr->mode;
-		if (S_ISREG(inode->i_mode))
-			inode->i_op = &nfs_file_inode_operations;
-		else if (S_ISDIR(inode->i_mode))
-			inode->i_op = &nfs_dir_inode_operations;
-		else if (S_ISLNK(inode->i_mode))
-			inode->i_op = &nfs_symlink_inode_operations;
-		else if (S_ISCHR(inode->i_mode)) {
-			inode->i_op = &chrdev_inode_operations;
-			inode->i_rdev = to_kdev_t(fattr->rdev);
-		} else if (S_ISBLK(inode->i_mode)) {
-			inode->i_op = &blkdev_inode_operations;
-			inode->i_rdev = to_kdev_t(fattr->rdev);
-		} else if (S_ISFIFO(inode->i_mode))
-			init_fifo(inode);
-		else
-			inode->i_op = NULL;
-	} else if ((inode->i_mode & S_IFMT) == (fattr->mode & S_IFMT)) {
-		inode->i_mode = fattr->mode;
-	} else {
-		mode_t old_mode;
-		/*
-		 * Big trouble! The inode has become a different object.
-		 */
-#if 1
-printk("nfs_refresh_inode: mode changed, %07o to %07o\n",
-inode->i_mode, fattr->mode);
-#endif
-		old_mode = inode->i_mode; /* save mode */
-		make_bad_inode(inode);
-		inode->i_mode = old_mode; /* restore mode */
-		inode->i_nlink = 0;
-		/*
-		 * No need to worry about unhashing the dentry, as the
-		 * lookup validation will know that the inode is bad.
-		 * (But we do want to invalidate the caches.)
-		 */
-		invalidate_inode_pages(inode);
-		nfs_invalidate_dircache(inode);
-		goto out;
-	}
-
-	inode->i_nlink = fattr->nlink;
-	inode->i_uid = fattr->uid;
-	inode->i_gid = fattr->gid;
-
-	/* Size changed from outside: invalidate caches on next read */
-	if (inode->i_size != fattr->size) {
-		dfprintk(PAGECACHE, "NFS:      cacheinv(%x/%ld)\n",
-					inode->i_dev, inode->i_ino);
-		NFS_CACHEINV(inode);
-	}
-	if (NFS_OLDMTIME(inode) != fattr->mtime.seconds) {
-		dfprintk(PAGECACHE, "NFS:      mtime change on %x/%ld\n",
-					inode->i_dev, inode->i_ino);
-		NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
-	}
-	inode->i_size = fattr->size;
-	inode->i_blocks = fattr->blocks;
-	inode->i_atime = fattr->atime.seconds;
-	inode->i_mtime = fattr->mtime.seconds;
-	inode->i_ctime = fattr->ctime.seconds;
-	error = 0;
-out:
 	return error;
 }
 
diff -u --recursive --new-file v2.1.58/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v2.1.58/linux/fs/nfs/inode.c	Wed Oct 15 16:04:23 1997
+++ linux/fs/nfs/inode.c	Wed Oct 15 21:08:46 1997
@@ -33,6 +33,7 @@
 #include <asm/uaccess.h>
 
 #define NFSDBG_FACILITY		NFSDBG_VFS
+#define NFS_PARANOIA 1
 
 extern void nfs_invalidate_dircache_sb(struct super_block *);
 
@@ -72,6 +73,7 @@
 	inode->i_rdev = 0;
 	inode->i_op = NULL;
 	NFS_CACHEINV(inode);
+	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
 }
 
 static void
@@ -356,14 +358,48 @@
 		printk("nfs_fhget: iget failed\n");
 		return NULL;
 	}
-	if (inode->i_dev == sb->s_dev) {
-		if (inode->i_ino != fattr->fileid) {
-			printk("nfs_fhget: unexpected inode from iget\n");
-			return inode;
-		}
-		*NFS_FH(inode) = *fhandle;
-		nfs_refresh_inode(inode, fattr);
+#ifdef NFS_PARANOIA
+if (inode->i_dev != sb->s_dev)
+printk("nfs_fhget: impossible\n");
+#endif
+
+	if (inode->i_ino != fattr->fileid) {
+		printk("nfs_fhget: unexpected inode from iget\n");
+		return inode;
+	}
+
+	/*
+	 * Check whether the mode has been set, as we only want to
+	 * do this once. (We don't allow inodes to change types.)
+	 */
+	if (inode->i_mode == 0) {
+		inode->i_mode = fattr->mode;
+		if (S_ISREG(inode->i_mode))
+			inode->i_op = &nfs_file_inode_operations;
+		else if (S_ISDIR(inode->i_mode))
+			inode->i_op = &nfs_dir_inode_operations;
+		else if (S_ISLNK(inode->i_mode))
+			inode->i_op = &nfs_symlink_inode_operations;
+		else if (S_ISCHR(inode->i_mode)) {
+			inode->i_op = &chrdev_inode_operations;
+			inode->i_rdev = to_kdev_t(fattr->rdev);
+		} else if (S_ISBLK(inode->i_mode)) {
+			inode->i_op = &blkdev_inode_operations;
+			inode->i_rdev = to_kdev_t(fattr->rdev);
+		} else if (S_ISFIFO(inode->i_mode))
+			init_fifo(inode);
+		else
+			inode->i_op = NULL;
+		/*
+		 * Preset the size and mtime, as there's no need
+		 * to invalidate the caches.
+		 */ 
+		inode->i_size  = fattr->size;
+		inode->i_mtime = fattr->mtime.seconds;
+		NFS_OLDMTIME(inode) = fattr->mtime.seconds;
 	}
+	*NFS_FH(inode) = *fhandle;
+	nfs_refresh_inode(inode, fattr);
 	dprintk("NFS: fhget(%x/%ld ct=%d)\n",
 		inode->i_dev, inode->i_ino,
 		inode->i_count);
@@ -378,6 +414,17 @@
 	struct nfs_fattr fattr;
 	int error;
 
+	/*
+	 * Make sure the inode is up-to-date.
+	 */
+	error = nfs_revalidate(inode);
+	if (error) {
+#ifdef NFS_PARANOIA
+printk("nfs_notify_change: revalidate failed, error=%d\n", error);
+#endif
+		goto out;
+	}
+
 	sattr.mode = (u32) -1;
 	if (attr->ia_valid & ATTR_MODE) 
 		sattr.mode = attr->ia_mode;
@@ -390,7 +437,6 @@
 	if (attr->ia_valid & ATTR_GID)
 		sattr.gid = attr->ia_gid;
 
-
 	sattr.size = (u32) -1;
 	if ((attr->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode))
 		sattr.size = attr->ia_size;
@@ -408,11 +454,25 @@
 	}
 
 	error = nfs_proc_setattr(NFS_SERVER(inode), NFS_FH(inode),
-		&sattr, &fattr);
-	if (!error) {
+				&sattr, &fattr);
+	if (error)
+		goto out;
+	/*
+	 * If we changed the size or mtime, update the inode
+	 * now to avoid invalidating the page cache.
+	 */
+	if (sattr.size != (u32) -1) {
+		if (sattr.size != fattr.size)
+			printk("nfs_notify_change: sattr=%d, fattr=%d??\n",
+				sattr.size, fattr.size);
 		nfs_truncate_dirty_pages(inode, sattr.size);
-		nfs_refresh_inode(inode, &fattr);
+		inode->i_size  = sattr.size;
+		inode->i_mtime = fattr.mtime.seconds;
 	}
+	if (sattr.mtime.seconds != (u32) -1)
+		inode->i_mtime = fattr.mtime.seconds;
+	error = nfs_refresh_inode(inode, &fattr);
+out:
 	return error;
 }
 
@@ -428,54 +488,152 @@
 /*
  * This function is called whenever some part of NFS notices that
  * the cached attributes have to be refreshed.
- *
- * This is a bit tricky because we have to make sure all dirty pages
- * have been sent off to the server before calling invalidate_inode_pages.
- * To make sure no other process adds more write requests while we try
- * our best to flush them, we make them sleep during the attribute refresh.
- *
- * A very similar scenario holds for the dir cache.
  */
 int
 _nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
 {
 	struct nfs_fattr fattr;
-	int		 status;
+	int		 status = 0;
 
 	if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
-		return 0;
+		goto out;
 
 	dfprintk(PAGECACHE, "NFS: revalidating %x/%ld inode\n",
 			inode->i_dev, inode->i_ino);
-	NFS_READTIME(inode) = jiffies;
-	if ((status = nfs_proc_getattr(server, NFS_FH(inode), &fattr)) < 0)
+	status = nfs_proc_getattr(server, NFS_FH(inode), &fattr);
+	if (status) {
+#ifdef NFS_PARANOIA
+printk("nfs_revalidate_inode: getattr failed, error=%d\n", status);
+#endif
 		goto done;
+	}
 
-	nfs_refresh_inode(inode, &fattr);
-	if (fattr.mtime.seconds != NFS_OLDMTIME(inode)) {
-		if (!S_ISDIR(inode->i_mode)) {
-			/* This sends off all dirty pages off to the server.
-			 * Note that this function must not sleep. */
-			nfs_invalidate_pages(inode);
-			invalidate_inode_pages(inode);
-		} else {
-			nfs_invalidate_dircache(inode);
-		}
-
-		NFS_OLDMTIME(inode)  = fattr.mtime.seconds;
-		NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
-	} else {
+	status = nfs_refresh_inode(inode, &fattr);
+	if (status)
+		goto done;
+	if (fattr.mtime.seconds == NFS_OLDMTIME(inode)) {
 		/* Update attrtimeo value */
 		if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
 			NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode);
 	}
-	status = 0;
+	NFS_OLDMTIME(inode) = fattr.mtime.seconds;
 
 done:
 	dfprintk(PAGECACHE,
 		"NFS: inode %x/%ld revalidation complete (status %d).\n",
 				inode->i_dev, inode->i_ino, status);
+out:
 	return status;
+}
+
+/*
+ * Many nfs protocol calls return the new file attributes after
+ * an operation.  Here we update the inode to reflect the state
+ * of the server's inode.
+ *
+ * This is a bit tricky because we have to make sure all dirty pages
+ * have been sent off to the server before calling invalidate_inode_pages.
+ * To make sure no other process adds more write requests while we try
+ * our best to flush them, we make them sleep during the attribute refresh.
+ *
+ * A very similar scenario holds for the dir cache.
+ */
+int
+nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
+{
+	int invalid = 0;
+	int error = -EIO;
+
+	dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n",
+		 inode->i_dev, inode->i_ino, inode->i_count);
+
+	if (!inode || !fattr) {
+		printk("nfs_refresh_inode: inode or fattr is NULL\n");
+		goto out;
+	}
+	if (inode->i_ino != fattr->fileid) {
+		printk("nfs_refresh_inode: inode number mismatch\n");
+		goto out;
+	}
+
+	/*
+	 * Make sure the inode's type hasn't changed.
+	 */
+	if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
+		goto out_changed;
+
+	/*
+	 * If the size or mtime changed from outside, we want
+	 * to invalidate the local caches immediately.
+	 */
+	if (inode->i_size != fattr->size) {
+#ifdef NFS_DEBUG_VERBOSE
+printk("NFS: size change on %x/%ld\n", inode->i_dev, inode->i_ino);
+#endif
+		invalid = 1;
+	}
+	if (inode->i_mtime != fattr->mtime.seconds) {
+#ifdef NFS_DEBUG_VERBOSE
+printk("NFS: mtime change on %x/%ld\n", inode->i_dev, inode->i_ino);
+#endif
+		invalid = 1;
+	}
+
+	inode->i_mode = fattr->mode;
+	inode->i_nlink = fattr->nlink;
+	inode->i_uid = fattr->uid;
+	inode->i_gid = fattr->gid;
+
+	inode->i_size = fattr->size;
+	inode->i_blocks = fattr->blocks;
+	inode->i_atime = fattr->atime.seconds;
+	inode->i_mtime = fattr->mtime.seconds;
+	inode->i_ctime = fattr->ctime.seconds;
+	/*
+	 * Update the read time so we don't revalidate too often.
+	 */
+	NFS_READTIME(inode) = jiffies;
+	error = 0;
+	if (invalid)
+		goto out_invalid;
+out:
+	return error;
+
+out_changed:
+	/*
+	 * Big trouble! The inode has become a different object.
+	 */
+#ifdef NFS_PARANOIA
+printk("nfs_refresh_inode: mode changed, %07o to %07o\n",
+inode->i_mode, fattr->mode);
+#endif
+	fattr->mode = inode->i_mode; /* save mode */
+	make_bad_inode(inode);
+	inode->i_mode = fattr->mode; /* restore mode */
+	inode->i_nlink = 0;
+	/*
+	 * No need to worry about unhashing the dentry, as the
+	 * lookup validation will know that the inode is bad.
+	 * (But we fall through to invalidate the caches.)
+	 */
+
+out_invalid:
+	/*
+	 * Invalidate the local caches
+	 */
+#ifdef NFS_DEBUG_VERBOSE
+printk("nfs_refresh_inode: invalidating %ld pages\n", inode->i_nrpages);
+#endif
+	if (!S_ISDIR(inode->i_mode)) {
+		/* This sends off all dirty pages off to the server.
+		 * Note that this function must not sleep. */
+		nfs_invalidate_pages(inode);
+		invalidate_inode_pages(inode);
+	} else
+		nfs_invalidate_dircache(inode);
+	NFS_CACHEINV(inode);
+	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+	goto out;
 }
 
 /*
diff -u --recursive --new-file v2.1.58/linux/fs/nfs/write.c linux/fs/nfs/write.c
--- v2.1.58/linux/fs/nfs/write.c	Tue Sep 23 16:48:49 1997
+++ linux/fs/nfs/write.c	Wed Oct 15 21:08:46 1997
@@ -196,22 +196,44 @@
 			clear_bit(PG_uptodate, &page->flags);
 			goto io_error;
 		}
+		if (result != wsize)
+			printk("NFS: short write, wsize=%u, result=%d\n",
+			wsize, result);
 		refresh = 1;
 		buffer  += wsize;
 		offset  += wsize;
 		written += wsize;
 		count   -= wsize;
+		/*
+		 * If we've extended the file, update the inode
+		 * now so we don't invalidate the cache.
+		 */
+		if (offset > inode->i_size)
+			inode->i_size = offset;
 	} while (count);
 
 io_error:
+	/* N.B. do we want to refresh if there was an error?? (fattr valid?) */
 	if (refresh) {
 		/* See comments in nfs_wback_result */
+		/* N.B. I don't think this is right -- sync writes in order */
 		if (fattr.size < inode->i_size)
 			fattr.size = inode->i_size;
+		if (fattr.mtime.seconds < inode->i_mtime)
+			printk("nfs_writepage_sync: prior time??\n");
 		/* Solaris 2.5 server seems to send garbled
 		 * fattrs occasionally */
-		if (inode->i_ino == fattr.fileid)
+		if (inode->i_ino == fattr.fileid) {
+			/*
+			 * We expect the mtime value to change, and
+			 * don't want to invalidate the caches.
+			 */
+			inode->i_mtime = fattr.mtime.seconds;
 			nfs_refresh_inode(inode, &fattr);
+		} 
+		else
+			printk("nfs_writepage_sync: inode %ld, got %u?\n",
+				inode->i_ino, fattr.fileid);
 	}
 
 	nfs_unlock_page(page);
@@ -327,7 +349,7 @@
 
 	wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_USER);
 	if (!wreq)
-		return NULL;
+		goto out_fail;
 	memset(wreq, 0, sizeof(*wreq));
 
 	task = &wreq->wb_task;
@@ -336,11 +358,8 @@
 	task->tk_action = nfs_wback_lock;
 
 	rpcauth_lookupcred(task);	/* Obtain user creds */
-	if (task->tk_status < 0) {
-		rpc_release_task(task);
-		kfree(wreq);
-		return NULL;
-	}
+	if (task->tk_status < 0)
+		goto out_req;
 
 	/* Put the task on inode's writeback request list. */
 	wreq->wb_inode  = inode;
@@ -357,6 +376,12 @@
 		rpc_wake_up_next(&write_queue);
 
 	return wreq;
+
+out_req:
+	rpc_release_task(task);
+	kfree(wreq);
+out_fail:
+	return NULL;
 }
 
 /*
@@ -423,6 +448,8 @@
 	}
 	remove_wait_queue(&page->wait, &wait);
 	current->state = TASK_RUNNING;
+if (atomic_read(&page->count) == 1)
+printk("NFS: lost a page\n");
 	atomic_dec(&page->count);
 	return retval;
 }
@@ -808,15 +835,29 @@
 		}
 		clear_bit(PG_uptodate, &page->flags);
 	} else if (!WB_CANCELLED(req)) {
+		struct nfs_fattr *fattr = req->wb_fattr;
 		/* Update attributes as result of writeback. 
 		 * Beware: when UDP replies arrive out of order, we
 		 * may end up overwriting a previous, bigger file size.
 		 */
-		if (req->wb_fattr->size < inode->i_size)
-			req->wb_fattr->size = inode->i_size;
-		/* possible Solaris 2.5 server bug workaround */
-		if (inode->i_ino == req->wb_fattr->fileid)
-			nfs_refresh_inode(inode, req->wb_fattr);
+		if (fattr->mtime.seconds >= inode->i_mtime) {
+			if (fattr->size < inode->i_size)
+				fattr->size = inode->i_size;
+
+			/* possible Solaris 2.5 server bug workaround */
+			if (inode->i_ino == fattr->fileid) {
+				/*
+				 * We expect these values to change, and
+				 * don't want to invalidate the caches.
+				 */
+				inode->i_size  = fattr->size;
+				inode->i_mtime = fattr->mtime.seconds;
+				nfs_refresh_inode(inode, fattr);
+			}
+			else
+				printk("nfs_wback_result: inode %ld, got %u?\n",
+					inode->i_ino, fattr->fileid);
+		}
 	}
 
 	rpc_release_task(task);
diff -u --recursive --new-file v2.1.58/linux/fs/super.c linux/fs/super.c
--- v2.1.58/linux/fs/super.c	Wed Oct 15 16:04:23 1997
+++ linux/fs/super.c	Thu Oct 16 08:11:24 1997
@@ -840,6 +840,13 @@
 	int retval;
 	struct vfsmount *vfsmnt;
 	
+	/*
+	 * Invalidate the inodes, as some mount options may be changed.
+	 * N.B. If we are changing media, we should check the return
+	 * from invalidate_inodes ... can't allow _any_ open files.
+	 */
+	invalidate_inodes(sb);
+
 	if (!(flags & MS_RDONLY) && sb->s_dev && is_read_only(sb->s_dev))
 		return -EACCES;
 		/*flags |= MS_RDONLY;*/
@@ -870,8 +877,14 @@
 		struct super_block * sb = dentry->d_inode->i_sb;
 
 		retval = -EINVAL;
-		if (dentry == sb->s_root)
+		if (dentry == sb->s_root) {
+			/*
+			 * Shrink the dcache and sync the device.
+			 */
+			shrink_dcache_sb(sb);
+			fsync_dev(sb->s_dev);
 			retval = do_remount_sb(sb, flags, data);
+		}
 		dput(dentry);
 	}
 	return retval;
diff -u --recursive --new-file v2.1.58/linux/include/linux/ioport.h linux/include/linux/ioport.h
--- v2.1.58/linux/include/linux/ioport.h	Mon Apr 14 16:28:26 1997
+++ linux/include/linux/ioport.h	Fri Oct 17 14:02:01 1997
@@ -30,7 +30,7 @@
 
 #define HAVE_AUTOIRQ
 extern void *irq2dev_map[16];		/* Use only if you own the IRQ. */
-extern int autoirq_setup(int waittime);
+extern void autoirq_setup(int waittime);
 extern int autoirq_report(int waittime);
 
 #endif	/* _LINUX_PORTIO_H */
diff -u --recursive --new-file v2.1.58/linux/include/linux/kernel.h linux/include/linux/kernel.h
--- v2.1.58/linux/include/linux/kernel.h	Sun Sep  7 13:10:43 1997
+++ linux/include/linux/kernel.h	Fri Oct 17 15:43:49 1997
@@ -33,6 +33,12 @@
 # define ATTRIB_NORET  __attribute__((noreturn))
 # define NORET_AND     noreturn,
 
+#ifdef __i386__
+#define FASTCALL(x)	x __attribute__((regparm(3)))
+#else
+#define FASTCALL(x)	x
+#endif
+
 extern void math_error(void);
 NORET_TYPE void panic(const char * fmt, ...)
 	__attribute__ ((NORET_AND format (printf, 1, 2)));
diff -u --recursive --new-file v2.1.58/linux/include/linux/mm.h linux/include/linux/mm.h
--- v2.1.58/linux/include/linux/mm.h	Wed Oct 15 16:04:24 1997
+++ linux/include/linux/mm.h	Fri Oct 17 15:49:57 1997
@@ -241,7 +241,7 @@
  */
 #define __get_free_page(priority) __get_free_pages((priority),0,0)
 #define __get_dma_pages(priority, order) __get_free_pages((priority),(order),1)
-extern unsigned long __get_free_pages(int priority, unsigned long gfporder, int dma);
+extern unsigned long FASTCALL(__get_free_pages(int priority, unsigned long gfporder, int dma));
 
 extern inline unsigned long get_free_page(int priority)
 {
@@ -256,8 +256,8 @@
 /* memory.c & swap.c*/
 
 #define free_page(addr) free_pages((addr),0)
-extern void free_pages(unsigned long addr, unsigned long order);
-extern void __free_page(struct page *);
+extern void FASTCALL(free_pages(unsigned long addr, unsigned long order));
+extern void FASTCALL(__free_page(struct page *));
 
 extern void show_free_areas(void);
 extern unsigned long put_dirty_page(struct task_struct * tsk,unsigned long page,
diff -u --recursive --new-file v2.1.58/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.1.58/linux/include/linux/sched.h	Wed Sep 24 20:05:48 1997
+++ linux/include/linux/sched.h	Fri Oct 17 15:49:57 1997
@@ -439,11 +439,11 @@
 
 #define CURRENT_TIME (xtime.tv_sec)
 
-extern void sleep_on(struct wait_queue ** p);
-extern void interruptible_sleep_on(struct wait_queue ** p);
-extern void wake_up(struct wait_queue ** p);
-extern void wake_up_interruptible(struct wait_queue ** p);
-extern void wake_up_process(struct task_struct * tsk);
+extern void FASTCALL(sleep_on(struct wait_queue ** p));
+extern void FASTCALL(interruptible_sleep_on(struct wait_queue ** p));
+extern void FASTCALL(wake_up(struct wait_queue ** p));
+extern void FASTCALL(wake_up_interruptible(struct wait_queue ** p));
+extern void FASTCALL(wake_up_process(struct task_struct * tsk));
 
 extern void notify_parent(struct task_struct * tsk, int signal);
 extern void force_sig(unsigned long sig,struct task_struct * p);
diff -u --recursive --new-file v2.1.58/linux/include/linux/swap.h linux/include/linux/swap.h
--- v2.1.58/linux/include/linux/swap.h	Thu Jul 17 10:06:09 1997
+++ linux/include/linux/swap.h	Fri Oct 17 15:49:15 1997
@@ -73,7 +73,7 @@
 extern struct swap_info_struct swap_info[];
 void si_swapinfo(struct sysinfo *);
 unsigned long get_swap_page(void);
-extern void swap_free(unsigned long);
+extern void FASTCALL(swap_free(unsigned long));
 
 /*
  * vm_ops not present page codes for shared memory.
diff -u --recursive --new-file v2.1.58/linux/kernel/sched.c linux/kernel/sched.c
--- v2.1.58/linux/kernel/sched.c	Wed Sep 24 20:05:48 1997
+++ linux/kernel/sched.c	Fri Oct 17 13:26:57 1997
@@ -104,7 +104,7 @@
 
 static inline void add_to_runqueue(struct task_struct * p)
 {
-	if (p->counter > current->counter + 3)
+	if (p->policy != SCHED_OTHER || p->counter > current->counter + 3)
 		need_resched = 1;
 	nr_running++;
 	(p->prev_run = init_task.prev_run)->next_run = p;
@@ -649,14 +649,14 @@
 }
 
 
-static inline void __sleep_on(struct wait_queue **p, int state)
+static void FASTCALL(__sleep_on(struct wait_queue **p, int state));
+static void __sleep_on(struct wait_queue **p, int state)
 {
 	unsigned long flags;
-	struct wait_queue wait = { current, NULL };
+	struct wait_queue wait;
 
-	if (!p)
-		return;
 	current->state = state;
+	wait.task = current;
 	write_lock_irqsave(&waitqueue_lock, flags);
 	__add_wait_queue(p, &wait);
 	write_unlock(&waitqueue_lock);
