diff -urN linux-2.0.15/CREDITS linux-2.0.18/CREDITS
--- linux-2.0.15/CREDITS	Mon Aug 26 22:06:02 1996
+++ linux-2.0.18/CREDITS	Sat Sep  7 09:53:02 1996
@@ -1273,11 +1273,11 @@
 S: USA
 
 N: Leo Spiekman
-E: spiekman@et.tudelft.nl
-W: http://dutettk.et.tudelft.nl/~spiekman
+E: leo@netlabs.net
+W: http://www.netlabs.net/hp/leo/
 D: Optics Storage 8000AT cdrom driver
-S: Utrecht
-S: The Netherlands
+S: Cliffwood, NJ 07721
+S: USA
 
 N: Henrik Storner
 E: storner@osiris.ping.dk
diff -urN linux-2.0.15/Documentation/Changes linux-2.0.18/Documentation/Changes
--- linux-2.0.15/Documentation/Changes	Mon Aug 26 22:06:05 1996
+++ linux-2.0.18/Documentation/Changes	Sat Sep  7 09:53:07 1996
@@ -20,20 +20,21 @@
 
    Para aquellos que prefieran una version en castellano de este
 documento, consultad la traduccion de Alfredo Sanjuan en
-http://slug.ctv.es/~alfredo/Cambios.html.
+http://slug.ctv.es/~alfredo/Cambios.html (Spanish translation).
 
    Akik magyarul szeretnenek olvasni az uj kernellel kapcsolatos
 valtozasokrol, az alabbi cimen megtalaljak Nyitrai Tamas forditasat:
-http://www.datanet.hu/generations/linux/newkernel.html.
+http://www.datanet.hu/generations/linux/newkernel.html (Hungarian
+translation).
 
-   Tamas also maintains a version of this file in English at
-http://www.datanet.hu/generations/linux/Changes.html.
+   Tamas also maintains a version of this file at
+http://www.datanet.hu/generations/linux/Changes.html (English).
 
    For people who prefer Japanese (thanks to Mitsuhiro Kojima): Kono
 bunshou no nihongo ban wa
 http://jf.gee.kyoto-u.ac.jp/JF/v2.0/Changes-2.0.html ni arimasu.
 
-Last updated: August 18, 1996.
+Last updated: September 02, 1996.
 Current Author: Chris Ricker (gt1355b@prism.gatech.edu).
 
 Current Releases
@@ -152,7 +153,7 @@
 
    On the other hand, if you're using a gcc patched for Pentium
 optimization and are getting these errors, downgrade to a standard GNU
-gcc before assuming your hardware (or the kernel) is to blame..
+gcc before assuming your hardware (or the kernel) is to blame.
 
    On a related note, if you get random OOPses that don't seem to be
 related to anything and you have a motherboard with APM support, try
@@ -349,8 +350,10 @@
    Frame relay support for Linux is now available as well.  Currently,
 only Sangoma cards are supported, but the interface is such that others
 will be as drivers become available.  To use this, grab
-ftp://ftp.invlogic.com/linux/frad-0.15.tgz and
-ftp://ftp.invlogic.com/linux/routing.tgz.
+ftp://linux.invlogic.com/pub/fr/frad-0.15.tgz (soon to be
+frad-0.20.tgz).  Another package of interest is
+ftp://linux.invlogic.com/pub/routing/routing.tgz (which allows Linux to
+make routing decisions based on packet source).
 
 Networking
 ==========
@@ -678,11 +681,14 @@
 ftp://ftp.redhat.com/pub/current/i386/updates/2.0-kernel/ and its
 mirrors.
 
-   For others, especially those of you running Slackware 3.0, David
-Bourgin has put together a Slackware-compatible package of everything
+   For others, David Bourgin has put together a package of everything
 necessary to quickly and easily upgrade to 2.0.x.  See
 ftp://ftp.wsc.com/pub/freeware/linux/update.linux/ for more information
-and the files.
+and the files.  This package also includes many bug-fixes, including
+one for a recently discovered bug in sendmail-8.7.5 (just look at
+/pub/freeware/linux/update.linux/updat2-0.addon1/sendmail-8.7.5a.tar.gz
+if you only need the bug fix).  There's also an alternate lightweight
+termcap in the same directory that works well for many people.
 
 Please send info about any other packages that 2.0.x "broke" or about
 any new features of 2.0.x that require extra or new packages for use to
diff -urN linux-2.0.15/Documentation/cdrom/cdrom-standard.tex linux-2.0.18/Documentation/cdrom/cdrom-standard.tex
--- linux-2.0.15/Documentation/cdrom/cdrom-standard.tex	Fri Aug 30 13:00:24 1996
+++ linux-2.0.18/Documentation/cdrom/cdrom-standard.tex	Sat Aug 24 12:26:38 1996
@@ -1,5 +1,5 @@
 \documentclass{article}
-\def\version{$Id: cdrom-standard.tex,v 0.8 1996/08/10 10:57:16 david Exp $}
+\def\version{$Id: cdrom-standard.tex,v 1.1 1996/08/24 12:11:12 root Exp $}
 
 \evensidemargin=0pt
 \oddsidemargin=0pt
diff -urN linux-2.0.15/Documentation/devices.tex linux-2.0.18/Documentation/devices.tex
--- linux-2.0.15/Documentation/devices.tex	Mon Aug 26 22:06:08 1996
+++ linux-2.0.18/Documentation/devices.tex	Thu Sep  5 17:45:58 1996
@@ -1104,7 +1104,7 @@
 commercial interface by P\&E.
 
 \begin{devicelist}
-\major{54}{Electrocardiognosis Holter serial card}
+\major{54}{}{char }{Electrocardiognosis Holter serial card}
 	\minor{0}{/dev/holter0}{First Holter port}
 	\minor{1}{/dev/holter1}{Second Holter port}
 	\minor{2}{/dev/holter2}{Third Holter port}
diff -urN linux-2.0.15/Documentation/oops-tracing.txt linux-2.0.18/Documentation/oops-tracing.txt
--- linux-2.0.15/Documentation/oops-tracing.txt	Mon Aug 26 22:06:22 1996
+++ linux-2.0.18/Documentation/oops-tracing.txt	Sat Sep  7 09:53:08 1996
@@ -70,3 +70,81 @@
 
 		Linus
 
+
+---------------------------------------------------------------------------
+Notes on Oops tracing with klogd:
+
+In order to help Linus and the other kernel developers there has been
+substantial support incorporated into klogd for processing protection
+faults.  In order to have full support for address resolution at least
+version 1.3-pl3 of the sysklogd package should be used.
+
+When a protection fault occurs the klogd daemon automatically
+translates important addresses in the kernel log messages to their
+symbolic equivalents.  This translated kernel message is then
+forwarded through whatever reporting mechanism klogd is using.  The
+protection fault message can be simply cut out of the message files
+and forwarded to the kernel developers.
+
+Two types of address resolution are performed by klogd.  The first is
+static translation and the second is dynamic translation.  Static
+translation uses the System.map file in much the same manner that
+ksymoops does.  In order to do static translation the klogd daemon
+must be able to find a system map file at daemon initialization time.
+See the klogd man page for information on how klogd searches for map
+files.
+
+Dynamic address translation is important when kernel loadable modules
+are being used.  Since memory for kernel modules is allocated from the
+kernel's dynamic memory pools there are no fixed locations for either
+the start of the module or for functions and symbols in the module.
+
+The kernel supports system calls which allow a program to determine
+which modules are loaded and their location in memory.  Using these
+system calls the klogd daemon builds a symbol table which can be used
+to debug a protection fault which occurs in a loadable kernel module.
+
+At the very minimum klogd will provide the name of the module which
+generated the protection fault.  There may be additional symbolic
+information available if the developer of the loadable module chose to
+export symbol information from the module.
+
+Since the kernel module environment can be dynamic there must be a
+mechanism for notifying the klogd daemon when a change in module
+environment occurs.  There are command line options available which
+allow klogd to signal the currently executing daemon that symbol
+information should be refreshed.  See the klogd manual page for more
+information.
+
+A patch is included with the sysklogd distribution which modifies the
+modules-2.0.0 package to automatically signal klogd whenever a module
+is loaded or unloaded.  Applying this patch provides essentially
+seamless support for debugging protection faults which occur with
+kernel loadable modules.
+
+The following is an example of a protection fault in a loadable module
+processed by klogd:
+---------------------------------------------------------------------------
+Aug 29 09:51:01 blizard kernel: Unable to handle kernel paging request at virtual address f15e97cc
+Aug 29 09:51:01 blizard kernel: current->tss.cr3 = 0062d000, ^Pr3 = 0062d000
+Aug 29 09:51:01 blizard kernel: *pde = 00000000
+Aug 29 09:51:01 blizard kernel: Oops: 0002
+Aug 29 09:51:01 blizard kernel: CPU:    0
+Aug 29 09:51:01 blizard kernel: EIP:    0010:[oops:_oops+16/3868]
+Aug 29 09:51:01 blizard kernel: EFLAGS: 00010212
+Aug 29 09:51:01 blizard kernel: eax: 315e97cc   ebx: 003a6f80   ecx: 001be77b   edx: 00237c0c
+Aug 29 09:51:01 blizard kernel: esi: 00000000   edi: bffffdb3   ebp: 00589f90   esp: 00589f8c
+Aug 29 09:51:01 blizard kernel: ds: 0018   es: 0018   fs: 002b   gs: 002b   ss: 0018
+Aug 29 09:51:01 blizard kernel: Process oops_test (pid: 3374, process nr: 21, stackpage=00589000)
+Aug 29 09:51:01 blizard kernel: Stack: 315e97cc 00589f98 0100b0b4 bffffed4 0012e38e 00240c64 003a6f80 00000001 
+Aug 29 09:51:01 blizard kernel:        00000000 00237810 bfffff00 0010a7fa 00000003 00000001 00000000 bfffff00 
+Aug 29 09:51:01 blizard kernel:        bffffdb3 bffffed4 ffffffda 0000002b 0007002b 0000002b 0000002b 00000036 
+Aug 29 09:51:01 blizard kernel: Call Trace: [oops:_oops_ioctl+48/80] [_sys_ioctl+254/272] [_system_call+82/128] 
+Aug 29 09:51:01 blizard kernel: Code: c7 00 05 00 00 00 eb 08 90 90 90 90 90 90 90 90 89 ec 5d c3 
+---------------------------------------------------------------------------
+
+Dr. G.W. Wettstein           Oncology Research Div. Computing Facility
+Roger Maris Cancer Center    INTERNET: greg@wind.rmcc.com
+820 4th St. N.
+Fargo, ND  58122
+Phone: 701-234-7556
diff -urN linux-2.0.15/Makefile linux-2.0.18/Makefile
--- linux-2.0.15/Makefile	Mon Aug 26 22:06:02 1996
+++ linux-2.0.18/Makefile	Sat Sep  7 09:53:05 1996
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 0
-SUBLEVEL = 15
+SUBLEVEL = 18
 
 ARCH = m68k
 
diff -urN linux-2.0.15/arch/i386/boot/compressed/Makefile linux-2.0.18/arch/i386/boot/compressed/Makefile
--- linux-2.0.15/arch/i386/boot/compressed/Makefile	Wed Jul 24 13:07:25 1996
+++ linux-2.0.18/arch/i386/boot/compressed/Makefile	Sat Sep  7 09:53:12 1996
@@ -58,7 +58,7 @@
 	  $(ENCAPS) $(TARGET) piggy.o $$tmppiggy.gz $(INPUT_DATA) $(INPUT_LEN); \
 	else \
 	  echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \
-	  ld -m elf_i386 -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 -T $$tmppiggy.lnk; \
+	  $(LD) -m elf_i386 -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 -T $$tmppiggy.lnk; \
 	fi; \
 	rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk
 else
diff -urN linux-2.0.15/arch/i386/mm/fault.c linux-2.0.18/arch/i386/mm/fault.c
--- linux-2.0.15/arch/i386/mm/fault.c	Sun Apr 28 10:24:39 1996
+++ linux-2.0.18/arch/i386/mm/fault.c	Sat Sep  7 09:53:14 1996
@@ -87,7 +87,7 @@
 	if (error_code & 1) {
 #ifdef TEST_VERIFY_AREA
 		if (regs->cs == KERNEL_CS)
-			printk("WP fault at %08x\n", regs->eip);
+			printk("WP fault at %08lx\n", regs->eip);
 #endif
 		do_wp_page(current, vma, address, error_code & 2);
 		return;
diff -urN linux-2.0.15/arch/i386/mm/init.c linux-2.0.18/arch/i386/mm/init.c
--- linux-2.0.15/arch/i386/mm/init.c	Wed Apr 17 23:30:48 1996
+++ linux-2.0.18/arch/i386/mm/init.c	Sat Sep  7 09:53:15 1996
@@ -198,7 +198,7 @@
 			address += PAGE_SIZE;
 		}
 	}
-	flush_tlb();
+	local_flush_tlb();
 	return free_area_init(start_mem, end_mem);
 }
 
@@ -272,10 +272,10 @@
 /* test if the WP bit is honoured in supervisor mode */
 	if (wp_works_ok < 0) {
 		pg0[0] = pte_val(mk_pte(0, PAGE_READONLY));
-		flush_tlb();
+		local_flush_tlb();
 		__asm__ __volatile__("movb 0,%%al ; movb %%al,0": : :"ax", "memory");
 		pg0[0] = 0;
-		flush_tlb();
+		local_flush_tlb();
 		if (wp_works_ok < 0)
 			wp_works_ok = 0;
 	}
diff -urN linux-2.0.15/arch/m68k/Makefile linux-2.0.18/arch/m68k/Makefile
--- linux-2.0.15/arch/m68k/Makefile	Mon Aug 26 22:07:21 1996
+++ linux-2.0.18/arch/m68k/Makefile	Sat Sep  7 22:19:52 1996
@@ -21,7 +21,7 @@
 # set up for cross compiling
 COMPILE_ARCH = $(shell uname -m)
 ifneq ($(COMPILE_ARCH),$(ARCH))
-	CROSSDIR=/usr/$(ARCH)-linux
+	CROSSDIR=/usr/$(ARCH)-linux/bin
 	CC := $(CROSSDIR)/$(CC)
 	AS := $(CROSSDIR)/$(AS)
 	LD := $(CROSSDIR)/$(LD)
diff -urN linux-2.0.15/arch/m68k/amiga/amifb.c linux-2.0.18/arch/m68k/amiga/amifb.c
--- linux-2.0.15/arch/m68k/amiga/amifb.c	Tue Aug 27 22:36:10 1996
+++ linux-2.0.18/arch/m68k/amiga/amifb.c	Mon Sep  2 21:58:12 1996
@@ -1801,7 +1801,6 @@
 	if (amifb_resolver){
 		custom.dmacon = DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
 				DMAF_BLITTER | DMAF_SPRITE;
-		custom.intena = IF_VERTB | IF_COPER | IF_BLIT;
 		return NULL;
 	}
 #endif
diff -urN linux-2.0.15/arch/m68k/atari/atafb.c linux-2.0.18/arch/m68k/atari/atafb.c
--- linux-2.0.15/arch/m68k/atari/atafb.c	Mon Aug 26 22:07:28 1996
+++ linux-2.0.18/arch/m68k/atari/atafb.c	Thu Sep  5 17:46:07 1996
@@ -84,10 +84,6 @@
 static int ovsc_offset=0, ovsc_addlen=0;
 int        ovsc_switchmode=0;
 
-#ifdef ATAFB_FALCON
-static int pwrsave = 0;	/* use VESA suspend mode instead of blanking only? */
-#endif
-
 static struct atari_fb_par {
 	unsigned long screen_base;
 	int vyres;
@@ -807,7 +803,7 @@
 	int sync_mask;		/* or-mask for hw.falcon.sync to set this clock */
 	int control_mask;	/* ditto, for hw.falcon.vid_control */
 }
-f25  = {25175000, 39722, 18, 0, 42, 0x0, VCO_CLOCK25},
+f25  = {25175000, 39721, 18, 0, 42, 0x0, VCO_CLOCK25},
 f32  = {32000000, 31250, 18, 0, 42, 0x0, 0},
 fext = {       0,     0, 18, 0, 42, 0x1, 0};
 
@@ -993,6 +989,10 @@
 	else if (yres_virtual < yres)
 		yres_virtual = yres;
 
+	/* backward bug-compatibility */
+	if (var->pixclock > 1)
+		var->pixclock -= 1;
+
 	par->hw.falcon.line_width = bpp * xres / 16;
 	par->hw.falcon.line_offset = bpp * (xres_virtual - xres) / 16;
 
@@ -1657,8 +1657,6 @@
 		vdb = current_par.VFT + 1;
 		/* ... and blank all lines */
 		hbe = current_par.HHT + 2;
-		if (pwrsave && mon_type == F_MON_VGA)
-			hss = current_par.HHT + 2;
 	}
 	/* use VESA suspend modes on VGA monitors */
 	if (mon_type == F_MON_VGA) {
@@ -1739,11 +1737,13 @@
 		fix->type_aux=0;
 		fix->visual=FB_VISUAL_MONO10;
 	}
-	fix->xpanstep = 0;
-	if (ATARIHW_PRESENT(EXTD_SHIFTER))
+	if (ATARIHW_PRESENT(EXTD_SHIFTER)) {
+		fix->xpanstep = 16;
 		fix->ypanstep = 1;
-	else
+	} else {
+		fix->xpanstep = 0;
 		fix->ypanstep = 0;
+	}
 	fix->ywrapstep = 0;
 	fix->line_length = 0;
 	for (i=0; i<arraysize(fix->reserved); i++)
@@ -2278,11 +2278,13 @@
 static int pan_display( struct fb_var_screeninfo *var,
                         struct atari_fb_par *par )
 {
-	if (var->xoffset)
+	if (!ATARIHW_PRESENT(EXTD_SHIFTER) && var->xoffset)
 		return -EINVAL;
-	par->screen_base
-		= screen_base + (var->yoffset * disp[currcon].var.xres_virtual
-						 * disp[currcon].var.bits_per_pixel / 8);
+	else
+		var->xoffset = up(var->xoffset, 16);
+	par->screen_base = screen_base +
+	        (var->yoffset * disp[currcon].var.xres_virtual + var->xoffset)
+	        * disp[currcon].var.bits_per_pixel / 8;
 	if (fbhw->set_screen_base)
 		fbhw->set_screen_base (par->screen_base);
 	else
@@ -3029,12 +3031,10 @@
 	else if (!strncmp(this_opt,"internal:",9))
 		strcpy(int_str, this_opt+9);
 #ifdef ATAFB_FALCON
-	else if (!strcmp(this_opt, "pwrsave"))
-		pwrsave = 1;
 	else if (!strncmp(this_opt, "eclock:", 7)) {
 		fext.f = simple_strtoul(this_opt+7, NULL, 10);
 		/* external pixelclock in kHz --> ps */
-		fext.t = (2000000000UL/fext.f+1)/2;
+		fext.t = 1000000000/fext.f;
 		fext.f *= 1000;
 	}
 	else if (!strncmp(this_opt, "monitorcap:", 11))
diff -urN linux-2.0.15/arch/m68k/atari/ataints.c linux-2.0.18/arch/m68k/atari/ataints.c
--- linux-2.0.15/arch/m68k/atari/ataints.c	Mon Aug 26 22:07:28 1996
+++ linux-2.0.18/arch/m68k/atari/ataints.c	Sun Sep  8 14:17:17 1996
@@ -24,6 +24,11 @@
  *  Total rewrite of Atari interrupt handling, for new scheme see comments
  *  below.
  *
+ * 1996-09-03 lars brinkhoff <f93labr@dd.chalmers.se>:
+ *  Added new function atari_unregister_vme_int(), and
+ *  modified atari_register_vme_int() as well as IS_VALID_INTNO()
+ *  to work with it.
+ *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file COPYING in the main directory of this archive
  * for more details.
@@ -130,11 +135,11 @@
 static struct irqparam irq_param[NUM_INT_SOURCES];
 
 /*
- * Counter for next free interrupt vector number
+ * Bitmap for free interrupt vector numbers
  * (new vectors starting from 0x70 can be allocated by
  * atari_register_vme_int())
  */
-static int next_free_vme_vec = VME_SOURCE_BASE;
+static int free_vme_vec_bitmap = 0;
 
 /* check for valid int number (complex, sigh...) */
 #define	IS_VALID_INTNO(n)											\
@@ -147,8 +152,9 @@
 	  /* SCC ok if present and number even */						\
 	  ((n) >= SCC_SOURCE_BASE && (n) < VME_SOURCE_BASE &&			\
 	   !((n) & 1) && ATARIHW_PRESENT(SCC)) ||						\
-	  /* greater numbers ok if less than #registered VME vectors */	\
-	  ((n) >= VME_SOURCE_BASE && (n) < next_free_vme_vec)))
+	  /* greater numbers ok if they are registered VME vectors */		\
+	  ((n) >= VME_SOURCE_BASE && (n) < VME_SOURCE_BASE + VME_MAX_SOURCES && \
+		  free_vme_vec_bitmap & (1 << ((n) - VME_SOURCE_BASE)))))
 
 
 /*
@@ -291,7 +297,7 @@
 	lea	%%a0@(%%d0:l:8),%%a0
 	pea 	%%sp@			/* push frame address */
 	movel	%%a0@(4),%%sp@-		/* push handler data */
-	bset	#24,%%d0		/* set MACHSPEC bit */
+	bset	#28,%%d0		/* set MACHSPEC bit */
 	movel	%%d0,%%sp@-		/* push int number */
 	movel	%%a0@,%%a0
 	jsr	%%a0@			/* and call the handler */
@@ -572,14 +578,28 @@
 
 unsigned long atari_register_vme_int(void)
 {
-	unsigned long source;
+	int i;
+
+	for(i = 0; i < 32; i++)
+		if((free_vme_vec_bitmap & (1 << i)) == 0)
+			break;
 	
-	if (next_free_vme_vec == NUM_ATARI_SOURCES)
+	if(i == 16)
 		return 0;
 
-	source = next_free_vme_vec | IRQ_MACHSPEC;
-	next_free_vme_vec++;
-	return source;
+	free_vme_vec_bitmap |= 1 << i;
+	return (VME_SOURCE_BASE + i) | IRQ_MACHSPEC;
+}
+
+
+void atari_unregister_vme_int(unsigned long irq)
+{
+	irq &= ~IRQ_MACHSPEC;
+	
+	if(irq >= VME_SOURCE_BASE && irq < VME_SOURCE_BASE + VME_MAX_SOURCES) {
+		irq -= VME_SOURCE_BASE;
+		free_vme_vec_bitmap &= ~(1 << irq);
+	}
 }
 
 
diff -urN linux-2.0.15/arch/m68k/atari/ksyms.c linux-2.0.18/arch/m68k/atari/ksyms.c
--- linux-2.0.15/arch/m68k/atari/ksyms.c	Mon Aug 26 22:07:30 1996
+++ linux-2.0.18/arch/m68k/atari/ksyms.c	Sun Sep  8 14:31:11 1996
@@ -12,6 +12,7 @@
 
 	X(is_medusa),
 	X(atari_register_vme_int),
+	X(atari_unregister_vme_int),
 	X(stdma_lock),
 	X(stdma_release),
 	X(stdma_others_waiting),
diff -urN linux-2.0.15/arch/m68k/atari/stdma.c linux-2.0.18/arch/m68k/atari/stdma.c
--- linux-2.0.15/arch/m68k/atari/stdma.c	Mon Aug 26 22:07:30 1996
+++ linux-2.0.18/arch/m68k/atari/stdma.c	Sat Sep  7 09:53:18 1996
@@ -133,7 +133,7 @@
 
 int stdma_others_waiting(void)
 {
-	return stdma_wait != NULL;
+	return waitqueue_active(&stdma_wait);
 }
 
 
diff -urN linux-2.0.15/arch/m68k/boot/Makefile linux-2.0.18/arch/m68k/boot/Makefile
--- linux-2.0.15/arch/m68k/boot/Makefile	Mon Aug 26 22:07:31 1996
+++ linux-2.0.18/arch/m68k/boot/Makefile	Mon Sep  2 16:15:55 1996
@@ -6,14 +6,16 @@
 # for more details.
 
 ifdef CONFIG_AMIGA
+AMIGA_BOOTSTRAP = amiga_bootstrap
 AMIGA_BOOTOBJS := amiga/bootstrap.o amiga/linuxboot.o
-AMIGA_HOSTCC = $(HOSTCC) -b amigados
-AMIGA_HOSTFLAGS=-m68030 -O2 -Wall -noixemul -Dlinux -DUSE_RAMDISK \
+AMIGA_HOSTCC = m68k-cbm-amigados-gcc -I$(TOPDIR)/include
+AMIGA_HOSTFLAGS=-m68030 -O2 -Wall -Dlinux -DUSE_RAMDISK \
 		-DMODIFY_BOOTINFO -DRESET_BOARDS
 
 endif
 
 ifdef CONFIG_ATARI
+ATARI_BOOTSTRAP = atari_bootstrap
 ATARI_BOOTOBJS := atari/bootstrap.o
 ATARI_HOSTCC = m68k-mint-gcc -I$(TOPDIR)/include
 ATARI_HOSTFLAGS = -m68030 -m68881 -Dlinux -O2 -Wall
@@ -43,7 +45,7 @@
 
 ifdef CONFIG_AMIGA
 amiga_bootstrap: $(AMIGA_BOOTOBJS)
-	$(AMIGA_HOSTCC) $(AMIGA_HOSTFLAGS) -o $@ -s $(AMIGA_BOOTOBJS)
+	$(AMIGA_HOSTCC) $(AMIGA_HOSTFLAGS) -o $@ -noixemul $(AMIGA_BOOTOBJS)
 	chmod +x $@
 	rm -f ../../../bootstrap
 	ln $@ ../../../bootstrap
@@ -54,6 +56,8 @@
 
 $(ATARI_BOOTOBJS): %.o: %.c
 	$(ATARI_HOSTCC) $(ATARI_HOSTFLAGS) -c $< -o $@
+
+bootstrap: $(AMIGA_BOOTSTRAP) $(ATARI_BOOTSTRAP)
 
 clean:
 	rm -f *.o amiga/*.o atari/*.o amiga_bootstrap atari_bootstrap
diff -urN linux-2.0.15/arch/m68k/config.in linux-2.0.18/arch/m68k/config.in
--- linux-2.0.15/arch/m68k/config.in	Fri Aug 30 12:46:11 1996
+++ linux-2.0.18/arch/m68k/config.in	Sun Sep  8 22:04:05 1996
@@ -128,7 +128,7 @@
 bool 'A3000 WD33C93A support' CONFIG_A3000_SCSI
 bool 'A2091 WD33C93A support' CONFIG_A2091_SCSI
 bool 'GVP Series II WD33C93A support' CONFIG_GVP11_SCSI
-#bool 'CyberStorm SCSI support' CONFIG_CYBERSTORM_SCSI
+bool 'CyberStorm SCSI support' CONFIG_CYBERSTORM_SCSI
 fi
 if [ "$CONFIG_ATARI" = "y" ]; then
 dep_tristate 'Atari native SCSI support' CONFIG_ATARI_SCSI $CONFIG_SCSI
diff -urN linux-2.0.15/arch/m68k/console/Makefile linux-2.0.18/arch/m68k/console/Makefile
--- linux-2.0.15/arch/m68k/console/Makefile	Wed Aug 28 22:36:00 1996
+++ linux-2.0.18/arch/m68k/console/Makefile	Sat Sep  7 22:47:28 1996
@@ -6,12 +6,19 @@
 # unless it's something special (ie not a .c file).
 #
 
+GSPA = gspa
+GSPH2C = gspahextoc
+
 L_TARGET = console.a
 L_OBJS = fbcon.o fonts.o font_8x16.o font_8x8.o pearl_8x8.o
 M_OBJS =
 
 ifdef CONFIG_AMIGA_GSP
-L_OBJS := $(L_OBJS) gspcon.o
+L_OBJS := $(L_OBJS) gspcon.o gspcore.o
 endif
 
 include $(TOPDIR)/Rules.make
+
+gspcore.c: gspcore.gsp
+	$(GSPA) $< > $*.hex
+	$(GSPH2C) $*.hex > gspcore.c
diff -urN linux-2.0.15/arch/m68k/defconfig linux-2.0.18/arch/m68k/defconfig
--- linux-2.0.15/arch/m68k/defconfig	Mon Aug 26 22:47:43 1996
+++ linux-2.0.18/arch/m68k/defconfig	Sun Sep  8 15:05:01 1996
@@ -62,7 +62,7 @@
 # CONFIG_MD_LINEAR is not set
 # CONFIG_MD_STRIPED is not set
 CONFIG_BLK_DEV_RAM=y
-# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_BLK_DEV_INITRD=y
 
 #
 # Networking options
@@ -118,6 +118,7 @@
 CONFIG_A3000_SCSI=y
 # CONFIG_A2091_SCSI is not set
 # CONFIG_GVP11_SCSI is not set
+# CONFIG_CYBERSTORM_SCSI is not set
 # CONFIG_ATARI_SCSI is not set
 
 #
diff -urN linux-2.0.15/arch/m68k/kernel/console.c linux-2.0.18/arch/m68k/kernel/console.c
--- linux-2.0.15/arch/m68k/kernel/console.c	Mon Aug 26 22:08:04 1996
+++ linux-2.0.18/arch/m68k/kernel/console.c	Thu Sep  5 17:46:13 1996
@@ -539,7 +539,7 @@
  */
 static void gotoxy(int currcons, int new_x, int new_y)
 {
-	int max_y;
+	int min_y, max_y;
 
 	if (new_x < 0)
 		x = 0;
@@ -549,21 +549,28 @@
 		else
 			x = new_x;
  	if (decom) {
-		new_y += top;
+		min_y = top;
 		max_y = bottom;
-	} else
+	} else {
+		min_y = 0;
 		max_y = rows;
-	if (new_y < 0)
-		y = 0;
+	}
+	if (new_y < min_y)
+		y = min_y;
+	else if (new_y >= max_y)
+		y = max_y - 1;
 	else
-		if (new_y >= max_y)
-			y = max_y - 1;
-		else
-			y = new_y;
+		y = new_y;
 	pos = video_mem_start + y * cols + x;
 	need_wrap = 0;
 }
 
+/* for absolute user moves, when decom is set */
+static void gotoxay(int currcons, int new_x, int new_y)
+{
+	gotoxy(currcons, new_x, decom ? (top+new_y) : new_y);
+}
+
 static void hide_cursor(int currcons)
 {
 	sw->con_cursor(vc_cons[currcons].d,CM_ERASE);
@@ -1146,7 +1153,7 @@
 				break;
 			case 6:			/* Origin relative/absolute */
 				decom = on_off;
-				gotoxy(currcons,0,0);
+				gotoxay(currcons,0,0);
 				break;
 			case 7:			/* Autowrap on/off */
 				decawm = on_off;
@@ -1856,12 +1863,12 @@
 				continue;
 			    case 'd':
 				if (par[0]) par[0]--;
-				gotoxy(currcons,x,par[0]);
+				gotoxay(currcons,x,par[0]);
 				continue;
 			    case 'H': case 'f':
 				if (par[0]) par[0]--;
 				if (par[1]) par[1]--;
-				gotoxy(currcons,par[1],par[0]);
+				gotoxay(currcons,par[1],par[0]);
 				continue;
 			    case 'J':
 				csi_J(currcons,par[0]);
@@ -1912,7 +1919,7 @@
 				    par[1] <= rows) {
 					top=par[0]-1;
 					bottom=par[1];
-					gotoxy(currcons,0,0);
+					gotoxay(currcons,0,0);
 				}
 				continue;
 			    case 's':
diff -urN linux-2.0.15/arch/m68k/kernel/entry.S linux-2.0.18/arch/m68k/kernel/entry.S
--- linux-2.0.15/arch/m68k/kernel/entry.S	Fri Aug 30 11:15:18 1996
+++ linux-2.0.18/arch/m68k/kernel/entry.S	Mon Sep  2 22:57:45 1996
@@ -205,9 +205,9 @@
 	jbsr	SYMBOL_NAME(send_sig)
 	addql	#8,%sp
 	addql	#4,%sp
+	movel	SYMBOL_NAME(current_set),%a0
 
 1:
-	movel	SYMBOL_NAME(current_set),%a0
 	tstl	%a0@(LTASK_STATE)	| state
 	jne	SYMBOL_NAME(reschedule)
 	tstl	%a0@(LTASK_COUNTER)	| counter
@@ -257,22 +257,24 @@
 1:
 	movel	SYMBOL_NAME(intr_count),%d1
 	subql	#1,%d1
-	jne	3f
+	jne	4f
 	bfextu  %sp@(LSR){#5,#3},%d0    | Check for nested interrupt.
 #if MAX_NOINT_IPL > 0
 	cmpiw	#MAX_NOINT_IPL,%d0
 #endif
-	jhi	3f
-2:	jbsr	SYMBOL_NAME(do_bottom_half)
-
+	jhi	4f
+2:
 	movel	SYMBOL_NAME(bh_active),%d0
 	andl	SYMBOL_NAME(bh_mask),%d0
-	jne	2b
+	jne	3f
 
 	clrl	SYMBOL_NAME(intr_count)	| deliver signals, reschedule etc..
 	jra	SYMBOL_NAME(ret_from_exception)
-
-3:	movel	%d1,SYMBOL_NAME(intr_count)
+3:	
+	jbsr	SYMBOL_NAME(do_bottom_half)
+	jbra	2b
+4:
+	movel	%d1,SYMBOL_NAME(intr_count)
 	RESTORE_ALL
 
 
@@ -360,7 +362,7 @@
 
 #if defined(CONFIG_M68060)
 #if !defined(CONFIG_M68060_ONLY)
-	btst	#3,SYMBOL_NAME(boot_info)+LBI_CPU+3
+	btst	#3,SYMBOL_NAME(boot_info)+BI_cputype+3
 	beqs	1f
 #endif
 	/* The 060 FPU keeps status in bits 15-8 of the first longword */
@@ -432,7 +434,7 @@
 
 #if defined(CONFIG_M68060)
 #if !defined(CONFIG_M68060_ONLY)
-	btst	#3,SYMBOL_NAME(boot_info)+LBI_CPU+3
+	btst	#3,SYMBOL_NAME(boot_info)+BI_cputype+3
 	beqs	1f
 #endif
 	/* The 060 FPU keeps status in bits 15-8 of the first longword */
diff -urN linux-2.0.15/arch/m68k/kernel/head.S linux-2.0.18/arch/m68k/kernel/head.S
--- linux-2.0.15/arch/m68k/kernel/head.S	Mon Aug 26 22:08:04 1996
+++ linux-2.0.18/arch/m68k/kernel/head.S	Tue Sep  3 20:06:54 1996
@@ -748,7 +748,8 @@
 	andl	#0xff000000,%d0 /* logical address base */
 	orw	#TTR_ENABLE+TTR_KERNELMODE+_PAGE_NOCACHE_S,%d0
 	.long	0x4e7b0004	/* movec %d0,%itt0 */
-2:	.word	0xf518		/* pflusha */
+2:	nop
+	.word	0xf518		/* pflusha */
 	.long	0x4e7bd807	/* movec %a5,%srp */
 	.long	0x4e7bd806	/* movec %a5,%urp */
 	movel	#TC_ENABLE+TC_PAGE4K,%d0
diff -urN linux-2.0.15/arch/m68k/kernel/ksyms.c linux-2.0.18/arch/m68k/kernel/ksyms.c
--- linux-2.0.15/arch/m68k/kernel/ksyms.c	Mon Aug 26 22:08:05 1996
+++ linux-2.0.18/arch/m68k/kernel/ksyms.c	Thu Sep  5 22:12:59 1996
@@ -46,7 +46,6 @@
 	X(dump_fpu),
 	X(dump_thread),
 	X(strnlen),
-	X(ramdisk_length),
 
 	/* The following are special because they're not called
 	   explicitly (the C compiler generates them).  Fortunately,
diff -urN linux-2.0.15/arch/m68k/lib/Makefile linux-2.0.18/arch/m68k/lib/Makefile
--- linux-2.0.15/arch/m68k/lib/Makefile	Mon Aug 26 22:08:08 1996
+++ linux-2.0.18/arch/m68k/lib/Makefile	Mon Sep  2 09:17:23 1996
@@ -3,6 +3,6 @@
 #
 
 L_TARGET = lib.a
-L_OBJS  = ashrdi3.o checksum.o memcpy.o memcmp.o
+L_OBJS  = ashrdi3.o checksum.o memcpy.o memcmp.o memset.o
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.0.15/arch/m68k/lib/memset.c linux-2.0.18/arch/m68k/lib/memset.c
--- linux-2.0.15/arch/m68k/lib/memset.c	Thu Jan  1 00:00:00 1970
+++ linux-2.0.18/arch/m68k/lib/memset.c	Tue Sep  3 21:38:57 1996
@@ -0,0 +1,68 @@
+#include <linux/types.h>
+
+void * memset(void * s, int c, size_t count)
+{
+  void *xs = s;
+  size_t temp, temp1;
+
+  if (!count)
+    return xs;
+  c &= 0xff;
+  c |= c << 8;
+  c |= c << 16;
+  if ((long) s & 1)
+    {
+      char *cs = s;
+      *cs++ = c;
+      s = cs;
+      count--;
+    }
+  if (count > 2 && (long) s & 2)
+    {
+      short *ss = s;
+      *ss++ = c;
+      s = ss;
+      count -= 2;
+    }
+  temp = count >> 2;
+  if (temp)
+    {
+      long *ls = s;
+
+      __asm__ __volatile__("movel %1,%2\n\t"
+			   "andw  #7,%2\n\t"
+			   "lsrl  #3,%1\n\t"
+			   "negw  %2\n\t"
+			   "jmp   %%pc@(2f,%2:w:2)\n\t"
+			   "1:\t"
+			   "movel %3,%0@+\n\t"
+			   "movel %3,%0@+\n\t"
+			   "movel %3,%0@+\n\t"
+			   "movel %3,%0@+\n\t"
+			   "movel %3,%0@+\n\t"
+			   "movel %3,%0@+\n\t"
+			   "movel %3,%0@+\n\t"
+			   "movel %3,%0@+\n\t"
+			   "2:\t"
+			   "dbra  %1,1b\n\t"
+			   "clrw  %1\n\t"
+			   "subql #1,%1\n\t"
+			   "jpl   1b\n\t"
+			   : "=a" (ls), "=d" (temp), "=&d" (temp1)
+			   : "d" (c), "0" (ls), "1" (temp)
+			   );
+      s = ls;
+    }
+  if (count & 2)
+    {
+      short *ss = s;
+      *ss++ = c;
+      s = ss;
+    }
+  if (count & 1)
+    {
+      char *cs = s;
+      *cs = c;
+    }
+  return xs;
+}
diff -urN linux-2.0.15/arch/m68k/mm/init.c linux-2.0.18/arch/m68k/mm/init.c
--- linux-2.0.15/arch/m68k/mm/init.c	Wed Aug 28 19:58:47 1996
+++ linux-2.0.18/arch/m68k/mm/init.c	Thu Sep  5 22:12:05 1996
@@ -400,34 +400,6 @@
 	printk ("before free_area_init\n");
 #endif
 
-#ifdef CONFIG_BLK_DEV_RAM
-#ifndef CONFIG_BLK_DEV_INITRD
-	/*
-	 * Since the initialization of the ramdisk's has been changed
-	 * so it fits the new driver initialization scheme, we have to
-	 * make room for our preloaded image here, instead of doing it
-	 * in rd_init() as we cannot kmalloc() a block large enough
-	 * for the image.
-	 */
-
-	ramdisk_length = boot_info.ramdisk_size * 1024;
-
-	if ((ramdisk_length > 0) && (ROOT_DEV == 0)) {
-	  char *rdp;	     /* current location of ramdisk */
-
-	  rd_start = (char *) start_mem;
-
-	  /* get current address of ramdisk */
-	  rdp = (char *)mm_phys_to_virt (boot_info.ramdisk_addr);
-
-	  /* copy the ram disk image */
-	  memcpy (rd_start, rdp, ramdisk_length);
-	  start_mem += ramdisk_length;
-	  rd_doload = 1;     /* tell rd_load to load this thing */
-	}
-#endif
-#endif
-
 	return free_area_init (start_mem, end_mem);
 }
 
diff -urN linux-2.0.15/arch/m68k/mm/memory.c linux-2.0.18/arch/m68k/mm/memory.c
--- linux-2.0.15/arch/m68k/mm/memory.c	Mon Aug 26 22:08:11 1996
+++ linux-2.0.18/arch/m68k/mm/memory.c	Tue Sep  3 21:44:14 1996
@@ -425,6 +425,7 @@
 /* push page defined by virtual address in both caches */
 #define	pushv040(vaddr) __asm__ __volatile__ ("movel %0,%/a0\n\t"\
 					      /* ptestr (a0) */\
+					      "nop\n\t"\
 					      ".word 0xf568\n\t"\
 					      /* movec mmusr,d0 */\
 					      ".long 0x4e7a0805\n\t"\
diff -urN linux-2.0.15/arch/m68k/tools/amiga/dmesg.c linux-2.0.18/arch/m68k/tools/amiga/dmesg.c
--- linux-2.0.15/arch/m68k/tools/amiga/dmesg.c	Mon Aug 26 22:08:11 1996
+++ linux-2.0.18/arch/m68k/tools/amiga/dmesg.c	Thu Sep  5 17:46:16 1996
@@ -7,10 +7,6 @@
  *		       (Geert.Uytterhoeven@cs.kuleuven.ac.be)
  *
  *
- *  Compilation (under AmigaOS):
- *
- *	gcc -o dmesg dmesg.c -noixemul -idirafter INCLUDE: -Wall -s -O3
- *
  *  Usage:
  *
  *	dmesg
diff -urN linux-2.0.15/drivers/block/cmd640.c linux-2.0.18/drivers/block/cmd640.c
--- linux-2.0.15/drivers/block/cmd640.c	Mon Aug 26 22:09:15 1996
+++ linux-2.0.18/drivers/block/cmd640.c	Thu Sep  5 17:46:23 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/cmd640.c	Version 1.01  Aug 12, 1996
+ *  linux/drivers/block/cmd640.c	Version 1.02  Sep 01, 1996
  *
  *  Copyright (C) 1995-1996  Linus Torvalds & authors (see below)
  */
@@ -94,9 +94,12 @@
  *  Version 1.00	Mmm.. cannot depend on PCMD_ENA in all systems
  *  Version 1.01	slow/fast devsel can be selected with "hdparm -p6/-p7"
  *			 ("fast" is necessary for 32bit I/O in some systems)
+ *  Version 1.02	fix bug that resulted in slow "setup times"
+ *			 (patch courtesy of Zoltan Hidvegi)
  */
 
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
+#define CMD640_PREFETCH_MASKS 1
 
 #include <linux/config.h>
 #include <linux/types.h>
@@ -408,9 +411,11 @@
 		drive->no_io_32bit = 1;
 		drive->io_32bit = 0;
 	} else {
+#if CMD640_PREFETCH_MASKS
 		drive->no_unmask = 1;
-		drive->no_io_32bit = 0;
 		drive->unmask = 0;
+#endif
+		drive->no_io_32bit = 0;
 	}
 }
 
@@ -454,8 +459,10 @@
 	cli();
 	b = get_cmd640_reg(reg);
 	if (mode) {	/* want prefetch on? */
+#if CMD640_PREFETCH_MASKS
 		drive->no_unmask = 1;
 		drive->unmask = 0;
+#endif
 		drive->no_io_32bit = 0;
 		b &= ~prefetch_masks[index];	/* enable prefetch */
 	} else {
@@ -556,9 +563,10 @@
 	 * Convert setup_count to internal chipset representation
 	 */
 	switch (setup_count) {
-		case 4:	 setup_count = 0x00;
-		case 3:	 setup_count = 0x80;
-		case 2:	 setup_count = 0x40;
+		case 4:	 setup_count = 0x00; break;
+		case 3:	 setup_count = 0x80; break;
+		case 1:
+		case 2:	 setup_count = 0x40; break;
 		default: setup_count = 0xc0; /* case 5 */
 	}
 
diff -urN linux-2.0.15/drivers/block/floppy.c linux-2.0.18/drivers/block/floppy.c
--- linux-2.0.15/drivers/block/floppy.c	Fri Aug 30 10:39:32 1996
+++ linux-2.0.18/drivers/block/floppy.c	Thu Sep  5 17:46:26 1996
@@ -4067,12 +4067,16 @@
 	if (fd_request_irq()) {
 		DPRINT("Unable to grab IRQ%d for the floppy driver\n",
 			FLOPPY_IRQ);
+		MOD_DEC_USE_COUNT;
+		usage_count--;
 		return -1;
 	}
 	if (fd_request_dma()) {
 		DPRINT("Unable to grab DMA%d for the floppy driver\n",
 			FLOPPY_DMA);
 		fd_free_irq();
+		MOD_DEC_USE_COUNT;
+		usage_count--;
 		return -1;
 	}
 	for (fdc = 0; fdc < N_FDC; fdc++)
@@ -4098,7 +4102,6 @@
 		return;
 	}
 	INT_ON;
-	MOD_DEC_USE_COUNT;
 	fd_disable_dma();
 	fd_free_dma();
 	fd_disable_irq();
@@ -4131,6 +4134,7 @@
 	if (floppy_tq.sync)
 		printk("task queue still active\n");
 #endif
+	MOD_DEC_USE_COUNT;
 }
 
 
diff -urN linux-2.0.15/drivers/block/rd.c linux-2.0.18/drivers/block/rd.c
--- linux-2.0.15/drivers/block/rd.c	Mon Aug 26 22:09:23 1996
+++ linux-2.0.18/drivers/block/rd.c	Thu Sep  5 22:00:10 1996
@@ -54,14 +54,6 @@
 #include <asm/system.h>
 #include <asm/segment.h>
 
-#if defined(__mc68000__)
-#include <asm/setup.h>
-
-extern unsigned long ramdisk_length;
-
-#define RAMDISK_MINOR	1
-#endif
-
 extern void wait_for_keypress(void);
 
 /*
@@ -111,10 +103,6 @@
 #endif
 #endif
 
-#if defined(__mc68000__) && !defined (CONFIG_BLK_DEV_INITRD)
-char *rd_start = NULL;
-#endif
-
 /*
  *  Basically, my strategy here is to set up a buffer-head which can't be
  *  deleted, and make that my Ramdisk.  If the request is outside of the
@@ -144,17 +132,6 @@
 		goto repeat;
 	}
 
-#if defined (__mc68000__) && !defined (CONFIG_BLK_DEV_INITRD)
-	if (minor == RAMDISK_MINOR && rd_start) {
-		if (CURRENT->cmd == READ)
-			memcpy(CURRENT->buffer, rd_start + offset, len);
-		else if (CURRENT->cmd == WRITE)
-			memcpy(rd_start + offset, CURRENT->buffer, len);
-		end_request(1);
-		goto repeat;
-	}
-#endif
-
 	/*
 	 * If we're reading, fill the buffer with 0's.  This is okay since
          * we're using protected buffers which should never get freed...
@@ -303,10 +280,6 @@
 		rd_length[i] = (rd_size * 1024);
 		rd_blocksizes[i] = 1024;
 	}
-#if defined(__mc68000__) && !defined (CONFIG_BLK_DEV_INITRD)
-	if (rd_start)
-		rd_length[RAMDISK_MINOR] = ramdisk_length;
-#endif
 
 	blksize_size[MAJOR_NR] = rd_blocksizes;
 
@@ -541,63 +514,6 @@
 	if (rd_doload == 0)
 		return;
 	
-#if defined (__mc68000__) && !defined (CONFIG_BLK_DEV_INITRD)
-	if (ROOT_DEV == 0)
-	{
-		int nblocks;
-		struct super_block {
-			union
-			{
-			  char minix [sizeof (struct minix_super_block)];
-			  char ext2 [sizeof (struct ext2_super_block)];
-			} record;
-		} sb;
-		struct minix_super_block *minixsb =
-			(struct minix_super_block *)&sb;
-		struct ext2_super_block *ext2sb =
-			(struct ext2_super_block *)&sb;
-
-		*((struct super_block *) &sb) =
-			*((struct super_block *)(rd_start + BLOCK_SIZE));
-
-		/* Try Minix */
-		nblocks = -1;
-		if (minixsb->s_magic == MINIX_SUPER_MAGIC ||
-		    minixsb->s_magic == MINIX_SUPER_MAGIC2) {
-		  printk("RAMDISK: Minix filesystem found\n");
-		  nblocks = minixsb->s_nzones << minixsb->s_log_zone_size;
-		}
-		
-		/* Try ext2 */
-		if (nblocks == -1 && (ext2sb->s_magic == EXT2_SUPER_MAGIC))
-	        {
-			printk("RAMDISK: Ext2 filesystem found\n");
-			nblocks = ext2sb->s_blocks_count;
-			printk ("There are %d groups (%u %u %u)\n",
-				(ext2sb->s_blocks_count -
-				 ext2sb->s_first_data_block +
-				 ext2sb->s_blocks_per_group - 1) /
-				ext2sb->s_blocks_per_group,
-				ext2sb->s_blocks_count,
-				ext2sb->s_first_data_block,
-				ext2sb->s_blocks_per_group);
-		}
-		
-		if (nblocks > (rd_length[RAMDISK_MINOR] >> BLOCK_SIZE_BITS)) {
-			printk("RAMDISK: image too big! (%d/%d blocks)\n",
-			       nblocks,
-			       rd_length[RAMDISK_MINOR] >> BLOCK_SIZE_BITS);
-			return;
-		}
-		printk("RAMDISK: Loading %d blocks into RAM disk", nblocks);
-		printk("\ndone\n");
-		/* We loaded the file system image. Prepare for mounting it. */
-		invalidate_buffers(ROOT_DEV);
-		ROOT_DEV = MKDEV(MEM_MAJOR, RAMDISK_MINOR);
-		return;
-	}
-#endif
-
 	if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR) return;
 
 	if (rd_prompt) {
diff -urN linux-2.0.15/drivers/block/xd.c linux-2.0.18/drivers/block/xd.c
--- linux-2.0.15/drivers/block/xd.c	Mon Aug 26 22:09:24 1996
+++ linux-2.0.18/drivers/block/xd.c	Sat Sep  7 09:53:28 1996
@@ -752,6 +752,8 @@
 void cleanup_module(void)
 {
 	unregister_blkdev(MAJOR_NR, "xd");
+	free_irq(xd_irq, NULL);
+	free_dma(xd_dma);
 }
 #endif /* MODULE */
 
diff -urN linux-2.0.15/drivers/cdrom/sbpcd.c linux-2.0.18/drivers/cdrom/sbpcd.c
--- linux-2.0.15/drivers/cdrom/sbpcd.c	Mon Aug 26 22:09:31 1996
+++ linux-2.0.18/drivers/cdrom/sbpcd.c	Thu Sep  5 17:46:32 1996
@@ -13,7 +13,7 @@
  *             labelled E2550UA or MK4015 or 2800F).
  */
 
-#define VERSION "v4.5 Eberhard Moenkeberg <emoenke@gwdg.de>"
+#define VERSION "v4.6 Eberhard Moenkeberg <emoenke@gwdg.de>"
 
 /*   Copyright (C) 1993, 1994, 1995  Eberhard Moenkeberg <emoenke@gwdg.de>
  *
@@ -293,11 +293,15 @@
  *       ll_rw_blk.c).
  *       "Check media change" without touching any drive.
  *
+ *  4.6  Use a semaphore to synchronize multi-activity; elaborated by Rob
+ *       Riggs <rriggs@tesser.com>. At the moment, we simply block "read"
+ *       against "ioctl" and vice versa. This could be refined further, but
+ *       I guess with almost no performance increase.
+ *       Experiments to speed up the CD-55A; again with help of Rob Riggs
+ *       (to be true, he gave both, idea & code. ;-)
  *
- *  TODO
  *
- *     synchronize multi-activity
- *        (data + audio + ioctl + disk change, multiple drives)
+ *  TODO
  *     implement "read all subchannel data" (96 bytes per frame)
  *
  *     special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine
@@ -571,9 +575,8 @@
 static struct wait_queue *sbp_waitq = NULL;
 #endif FUTURE
 
-/*==========================================================================*/
-#define SBP_BUFFER_FRAMES 8 /* driver's own read_ahead, data mode */
-/*==========================================================================*/
+static int teac=SBP_TEAC_SPEED;
+static int buffers=SBP_BUFFER_FRAMES;
 
 static u_char family0[]="MATSHITA"; /* MKE CR-521, CR-522, CR-523 */
 static u_char family1[]="CR-56";    /* MKE CR-562, CR-563 */
@@ -592,8 +595,11 @@
 static u_char xa_head_buf[CD_XA_HEAD];
 static u_char xa_tail_buf[CD_XA_TAIL];
 
+#if OLD_BUSY
 static volatile u_char busy_data=0;
 static volatile u_char busy_audio=0; /* true semaphores would be safer */
+#endif OLD_BUSY
+static struct semaphore ioctl_read_sem = MUTEX;
 static u_long timeout;
 static volatile u_char timed_out_delay=0;
 static volatile u_char timed_out_data=0;
@@ -828,6 +834,8 @@
 	sti();
 }
 /*==========================================================================*/
+#define RETURN_UP(rc) {up(&ioctl_read_sem); return(rc);}
+/*==========================================================================*/
 /*
  *  convert logical_block_address to m-s-f_number (3 bytes only)
  */
@@ -960,6 +968,57 @@
 	sbp_sleep(15*HZ/10);
 	for (i=maxtim_data;i!=0;i--) inb(CDi_status);
 }
+/*====================================================================*/
+/*
+ * CDi status loop for Teac CD-55A (Rob Riggs)
+ *
+ * This is needed because for some strange reason
+ * the CD-55A can take a real long time to give a
+ * status response. This seems to happen after we
+ * issue a READ command where a long seek is involved.
+ *
+ * I tried to ensure that we get max throughput with
+ * minimal busy waiting. We busy wait at first, then
+ * "switch gears" and start sleeping. We sleep for
+ * longer periods of time the longer we wait.
+ *
+ */
+static int CDi_stat_loop_T(void)
+{
+	int	i, gear=1;
+	u_long  timeout_1, timeout_2, timeout_3, timeout_4;
+
+	timeout_1 = jiffies + HZ / 50;  /* sbp_sleep(0) for a short period */
+	timeout_2 = jiffies + HZ / 5;	/* nap for no more than 200ms */
+	timeout_3 = jiffies + 5 * HZ;	/* sleep for up to 5s */
+	timeout_4 = jiffies + 45 * HZ;	/* long sleep for up to 45s. */
+	do
+          {
+            i = inb(CDi_status);
+            if (!(i&s_not_data_ready)) return (i);
+            if (!(i&s_not_result_ready)) return (i);
+            switch(gear)
+              {
+              case 4:
+                sbp_sleep(HZ);
+                if (jiffies > timeout_4) gear++;
+                msg(DBG_TEA, "CDi_stat_loop_T: long sleep active.\n");
+                break;
+              case 3:
+                sbp_sleep(HZ/10);
+                if (jiffies > timeout_3) gear++;
+                break;
+              case 2:
+                sbp_sleep(HZ/100);
+                if (jiffies > timeout_2) gear++;
+                break;
+              case 1:
+                sbp_sleep(0);
+                if (jiffies > timeout_1) gear++;
+              }
+          } while (gear < 5);
+	return -1;
+}
 /*==========================================================================*/
 static int CDi_stat_loop(void)
 {
@@ -1290,8 +1349,20 @@
 	for (ntries=CMDT_TRIES;ntries>0;ntries--)
 	{
 		if (drvcmd[0]==CMDT_READ_VER) sbp_sleep(HZ); /* fixme */
+#if 01
 		OUT(CDo_sel_i_d,1);
-		i=inb(CDi_status);
+#endif 01
+		if (teac==2)
+                  {
+                    if ((i=CDi_stat_loop_T()) == -1) break;
+                  }
+		else
+                  {
+#if 0
+                    OUT(CDo_sel_i_d,1);
+#endif 0
+                    i=inb(CDi_status);
+                  }
 		if (!(i&s_not_data_ready)) /* f.e. CMDT_DISKINFO */
 		{
 			OUT(CDo_sel_i_d,1);
@@ -1384,7 +1455,8 @@
 			return (-D_S[d].error_state-400);
 		}
 		if (drvcmd[0]==CMDT_READ) return (0); /* handled elsewhere */
-		sbp_sleep(HZ/10);
+		if ((teac==0)||(ntries<(CMDT_TRIES-5))) sbp_sleep(HZ/10);
+		else sbp_sleep(HZ/100);
 		if (ntries>(CMDT_TRIES-50)) continue;
 		msg(DBG_TEA,"cmd_out_T: next CMDT_TRIES (%02X): %d.\n", drvcmd[0], ntries-1);
 	}
@@ -3343,7 +3415,7 @@
 		}
 		else if (famV_drive)
 		  {
-		    if (j==100) D_S[d].drv_type=drv_at;
+		    if ((j==100)||(j==150)) D_S[d].drv_type=drv_at;
 		    ask_mail(); /* hopefully we get some feedback by this */
 		  }
 	}
@@ -3716,7 +3788,7 @@
 		return (i);
 	}
 	if (D_S[d].f_multisession) D_S[d].sbp_bufsiz=1;  /* possibly a weird PhotoCD */
-	else D_S[d].sbp_bufsiz=SBP_BUFFER_FRAMES;
+	else D_S[d].sbp_bufsiz=buffers;
 	i=cc_ReadTocEntry(D_S[d].n_first_track);
 	if (i<0)
 	{
@@ -3878,16 +3950,17 @@
 		msg(DBG_INF, "ioctl: bad device: %04X\n", inode->i_rdev);
 		return (-ENXIO);             /* no such drive */
 	}
+	down(&ioctl_read_sem);
 	if (d!=i) switch_drive(i);
 	
 #if 0
 	st=GetStatus();
-	if (st<0) return (-EIO);
+	if (st<0) RETURN_UP(-EIO);
 	
 	if (!toc_valid)
 	{
 		i=DiskInfo();
-		if (i<0) return (-EIO);	/* error reading TOC */
+		if (i<0) RETURN_UP(-EIO);	/* error reading TOC */
 	}
 #endif
 	
@@ -3895,9 +3968,9 @@
 	switch (cmd) 		/* Sun-compatible */
 	{
 	case DDIOCSDBG:		/* DDI Debug */
-		if (!suser()) return (-EPERM);
+		if (!suser()) RETURN_UP(-EPERM);
 		i=sbpcd_dbg_ioctl(arg,1);
-		return (i);
+		RETURN_UP(i);
 		
 	case CDROMPAUSE:     /* Pause the drive */
 		msg(DBG_IOC,"ioctl: CDROMPAUSE entered.\n");
@@ -3910,19 +3983,19 @@
 		case audio_playing:
 			if (famL_drive) i=cc_ReadSubQ();
 			else i=cc_Pause_Resume(1);
-			if (i<0) return (-EIO);
+			if (i<0) RETURN_UP(-EIO);
 			if (famL_drive) i=cc_Pause_Resume(1);
 			else i=cc_ReadSubQ();
-			if (i<0) return (-EIO);
+			if (i<0) RETURN_UP(-EIO);
 			D_S[d].pos_audio_start=D_S[d].SubQ_run_tot;
 			D_S[d].audio_state=audio_pausing;
-			return (0);
+			RETURN_UP(0);
 		case audio_pausing:
 			i=cc_Seek(D_S[d].pos_audio_start,1);
-			if (i<0) return (-EIO);
-			return (0);
+			if (i<0) RETURN_UP(-EIO);
+			RETURN_UP(0);
 		default:
-			return (-EINVAL);
+			RETURN_UP(-EINVAL);
 		}
 		
 	case CDROMRESUME: /* resume paused audio play */
@@ -3935,26 +4008,26 @@
 			i=cc_PlayAudio(D_S[d].pos_audio_start,
 				       D_S[d].pos_audio_end);
 		else i=cc_Pause_Resume(3);
-		if (i<0) return (-EIO);
+		if (i<0) RETURN_UP(-EIO);
 		D_S[d].audio_state=audio_playing;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMPLAYMSF:
 		msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n");
 #if SAFE_MIXED
-		if (D_S[d].has_data>1) return (-EBUSY);
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
 #endif SAFE_MIXED
 		if (D_S[d].audio_state==audio_playing)
 		{
 			i=cc_Pause_Resume(1);
-			if (i<0) return (-EIO);
+			if (i<0) RETURN_UP(-EIO);
 			i=cc_ReadSubQ();
-			if (i<0) return (-EIO);
+			if (i<0) RETURN_UP(-EIO);
 			D_S[d].pos_audio_start=D_S[d].SubQ_run_tot;
 			i=cc_Seek(D_S[d].pos_audio_start,1);
 		}
 		st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_msf));
-		if (st) return (st);
+		if (st) RETURN_UP(st);
 		memcpy_fromfs(&msf, (void *) arg, sizeof(struct cdrom_msf));
 		/* values come as msf-bin */
 		D_S[d].pos_audio_start = (msf.cdmsf_min0<<16) |
@@ -3971,33 +4044,36 @@
 			msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i);
 			DriveReset();
 			D_S[d].audio_state=0;
-			return (-EIO);
+			RETURN_UP(-EIO);
 		}
 		D_S[d].audio_state=audio_playing;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMPLAYTRKIND: /* Play a track.  This currently ignores index. */
 		msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n");
 #if SAFE_MIXED
-		if (D_S[d].has_data>1) return (-EBUSY);
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
 #endif SAFE_MIXED
 		if (D_S[d].audio_state==audio_playing)
 		{
 			msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n");
-			return (0);
-			return (-EINVAL);
+#if 1
+			RETURN_UP(0); /* just let us play on */
+#else
+			RETURN_UP(-EINVAL); /* play on, but say "error" */
+#endif
 		}
 		st=verify_area(VERIFY_READ,(void *) arg,sizeof(struct cdrom_ti));
 		if (st<0)
 		{
 			msg(DBG_IOX,"CDROMPLAYTRKIND: verify_area error.\n");
-			return (st);
+			RETURN_UP(st);
 		}
 		memcpy_fromfs(&ti,(void *) arg,sizeof(struct cdrom_ti));
 		msg(DBG_IOX,"ioctl: trk0: %d, ind0: %d, trk1:%d, ind1:%d\n",
 		    ti.cdti_trk0,ti.cdti_ind0,ti.cdti_trk1,ti.cdti_ind1);
-		if (ti.cdti_trk0<D_S[d].n_first_track) return (-EINVAL);
-		if (ti.cdti_trk0>D_S[d].n_last_track) return (-EINVAL);
+		if (ti.cdti_trk0<D_S[d].n_first_track) RETURN_UP(-EINVAL);
+		if (ti.cdti_trk0>D_S[d].n_last_track) RETURN_UP(-EINVAL);
 		if (ti.cdti_trk1<ti.cdti_trk0) ti.cdti_trk1=ti.cdti_trk0;
 		if (ti.cdti_trk1>D_S[d].n_last_track) ti.cdti_trk1=D_S[d].n_last_track;
 		D_S[d].pos_audio_start=D_S[d].TocBuffer[ti.cdti_trk0].address;
@@ -4008,28 +4084,29 @@
 			msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i);
 			DriveReset();
 			D_S[d].audio_state=0;
-			return (-EIO);
+			RETURN_UP(-EIO);
 		}
 		D_S[d].audio_state=audio_playing;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMREADTOCHDR:        /* Read the table of contents header */
 		msg(DBG_IOC,"ioctl: CDROMREADTOCHDR entered.\n");
 		tochdr.cdth_trk0=D_S[d].n_first_track;
 		tochdr.cdth_trk1=D_S[d].n_last_track;
 		st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_tochdr));
-		if (st) return (st);
+		if (st) RETURN_UP(st);
 		memcpy_tofs((void *) arg, &tochdr, sizeof(struct cdrom_tochdr));
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
 		msg(DBG_IOC,"ioctl: CDROMREADTOCENTRY entered.\n");
 		st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_tocentry));
-		if (st) return (st);
+		if (st) RETURN_UP(st);
 		memcpy_fromfs(&tocentry, (void *) arg, sizeof(struct cdrom_tocentry));
 		i=tocentry.cdte_track;
 		if (i==CDROM_LEADOUT) i=D_S[d].n_last_track+1;
-		else if (i<D_S[d].n_first_track||i>D_S[d].n_last_track) return (-EINVAL);
+		else if (i<D_S[d].n_first_track||i>D_S[d].n_last_track)
+                  RETURN_UP(-EINVAL);
 		tocentry.cdte_adr=D_S[d].TocBuffer[i].ctl_adr&0x0F;
 		tocentry.cdte_ctrl=(D_S[d].TocBuffer[i].ctl_adr>>4)&0x0F;
 		tocentry.cdte_datamode=D_S[d].TocBuffer[i].format;
@@ -4041,68 +4118,68 @@
 		}
 		else if (tocentry.cdte_format==CDROM_LBA) /* blk required */
 			tocentry.cdte_addr.lba=msf2blk(D_S[d].TocBuffer[i].address);
-		else return (-EINVAL);
+		else RETURN_UP(-EINVAL);
 		memcpy_tofs((void *) arg, &tocentry, sizeof(struct cdrom_tocentry));
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMRESET:      /* hard reset the drive */
 		msg(DBG_IOC,"ioctl: CDROMRESET entered.\n");
 		i=DriveReset();
 		D_S[d].audio_state=0;
-		return (i);
+		RETURN_UP(i);
 		
 	case CDROMSTOP:      /* Spin down the drive */
 		msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n");
 #if SAFE_MIXED
-		if (D_S[d].has_data>1) return (-EBUSY);
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
 #endif SAFE_MIXED
 		i=cc_Pause_Resume(1);
 		D_S[d].audio_state=0;
-		return (i);
+		RETURN_UP(i);
 		
 	case CDROMSTART:  /* Spin up the drive */
 		msg(DBG_IOC,"ioctl: CDROMSTART entered.\n");
 		cc_SpinUp();
 		D_S[d].audio_state=0;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMEJECT:
 		msg(DBG_IOC,"ioctl: CDROMEJECT entered.\n");
 		if (fam0_drive) return (0);
-		if (D_S[d].open_count>1) return (-EBUSY);
+		if (D_S[d].open_count>1) RETURN_UP(-EBUSY);
 		i=UnLockDoor();
 		D_S[d].open_count=-9; /* to get it locked next time again */
 		i=cc_SpinDown();
 		msg(DBG_IOX,"ioctl: cc_SpinDown returned %d.\n", i);
 		msg(DBG_TEA,"ioctl: cc_SpinDown returned %d.\n", i);
-		if (i<0) return (-EIO);
+		if (i<0) RETURN_UP(-EIO);
 		D_S[d].CD_changed=0xFF;
 		D_S[d].diskstate_flags=0;
 		D_S[d].audio_state=0;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMEJECT_SW:
 		msg(DBG_IOC,"ioctl: CDROMEJECT_SW entered.\n");
-		if (fam0_drive) return (0);
+		if (fam0_drive) RETURN_UP(0);
 		D_S[d].f_eject=arg;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMVOLCTRL:   /* Volume control */
 		msg(DBG_IOC,"ioctl: CDROMVOLCTRL entered.\n");
 		st=verify_area(VERIFY_READ,(void *) arg,sizeof(volctrl));
-		if (st) return (st);
+		if (st) RETURN_UP(st);
 		memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl));
 		D_S[d].vol_chan0=0;
 		D_S[d].vol_ctrl0=volctrl.channel0;
 		D_S[d].vol_chan1=1;
 		D_S[d].vol_ctrl1=volctrl.channel1;
 		i=cc_SetVolume();
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMVOLREAD:   /* read Volume settings from drive */
 		msg(DBG_IOC,"ioctl: CDROMVOLREAD entered.\n");
 		st=verify_area(VERIFY_WRITE,(void *)arg,sizeof(volctrl));
-		if (st) return (st);
+		if (st) RETURN_UP(st);
 		st=cc_GetVolume();
 		if (st<0) return (st);
 		volctrl.channel0=D_S[d].vol_ctrl0;
@@ -4110,15 +4187,16 @@
 		volctrl.channel2=0;
 		volctrl.channel2=0;
 		memcpy_tofs((void *)arg,&volctrl,sizeof(volctrl));
-		return (0);
+		RETURN_UP(0);
 
 	case CDROMSUBCHNL:   /* Get subchannel info */
 		msg(DBG_IOS,"ioctl: CDROMSUBCHNL entered.\n");
-		if ((st_spinning)||(!subq_valid)) { i=cc_ReadSubQ();
-						    if (i<0) return (-EIO);
-					    }
+		if ((st_spinning)||(!subq_valid)) {
+			i=cc_ReadSubQ();
+			if (i<0) RETURN_UP(-EIO);
+		}
 		st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl));
-		if (st)	return (st);
+		if (st)	RETURN_UP(st);
 		memcpy_fromfs(&SC, (void *) arg, sizeof(struct cdrom_subchnl));
 		switch (D_S[d].audio_state)
 		{
@@ -4156,27 +4234,27 @@
 		    SC.cdsc_adr,SC.cdsc_ctrl,
 		    SC.cdsc_trk,SC.cdsc_ind,
 		    SC.cdsc_absaddr,SC.cdsc_reladdr);
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMREADMODE1:
 		msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n");
 #if SAFE_MIXED
-		if (D_S[d].has_data>1) return (-EBUSY);
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
 #endif SAFE_MIXED
 		cc_ModeSelect(CD_FRAMESIZE);
 		cc_ModeSense();
 		D_S[d].mode=READ_M1;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMREADMODE2: /* not usable at the moment */
 		msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n");
 #if SAFE_MIXED
-		if (D_S[d].has_data>1) return (-EBUSY);
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
 #endif SAFE_MIXED
 		cc_ModeSelect(CD_FRAMESIZE_RAW1);
 		cc_ModeSense();
 		D_S[d].mode=READ_M2;
-		return (0);
+		RETURN_UP(0);
 		
 	case CDROMAUDIOBUFSIZ: /* configure the audio buffer size */
 		msg(DBG_IOC,"ioctl: CDROMAUDIOBUFSIZ entered.\n");
@@ -4193,11 +4271,11 @@
 			}
 			else msg(DBG_INF,"audio buffer size: %d frames.\n",D_S[d].sbp_audsiz);
 		}
-		return (D_S[d].sbp_audsiz);
+		RETURN_UP(D_S[d].sbp_audsiz);
 
 	case CDROMREADAUDIO:
 	{ /* start of CDROMREADAUDIO */
-		int i=0, j=0, frame, block;
+		int i=0, j=0, frame, block=0;
 		u_int try=0;
 		u_long timeout;
 		u_char *p;
@@ -4208,27 +4286,27 @@
 		int error_flag;
 		
 		msg(DBG_IOC,"ioctl: CDROMREADAUDIO entered.\n");
-		if (fam0_drive) return (-EINVAL);
-		if (famL_drive) return (-EINVAL);
-		if (famV_drive) return (-EINVAL);
-		if (famT_drive) return (-EINVAL);
+		if (fam0_drive) RETURN_UP(-EINVAL);
+		if (famL_drive) RETURN_UP(-EINVAL);
+		if (famV_drive) RETURN_UP(-EINVAL);
+		if (famT_drive) RETURN_UP(-EINVAL);
 #if SAFE_MIXED
-		if (D_S[d].has_data>1) return (-EBUSY);
+		if (D_S[d].has_data>1) RETURN_UP(-EBUSY);
 #endif SAFE_MIXED
-		if (D_S[d].aud_buf==NULL) return (-EINVAL);
+		if (D_S[d].aud_buf==NULL) RETURN_UP(-EINVAL);
 		i=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_read_audio));
-		if (i) return (i);
+		if (i) RETURN_UP(i);
 		memcpy_fromfs(&read_audio, (void *) arg, sizeof(struct cdrom_read_audio));
-		if (read_audio.nframes>D_S[d].sbp_audsiz) return (-EINVAL);
+		if (read_audio.nframes>D_S[d].sbp_audsiz) RETURN_UP(-EINVAL);
 		i=verify_area(VERIFY_WRITE, read_audio.buf,
 			      read_audio.nframes*CD_FRAMESIZE_RAW);
-		if (i) return (i);
+		if (i) RETURN_UP(i);
 		
 		if (read_audio.addr_format==CDROM_MSF) /* MSF-bin specification of where to start */
 			block=msf2lba(&read_audio.addr.msf.minute);
 		else if (read_audio.addr_format==CDROM_LBA) /* lba specification of where to start */
 			block=read_audio.addr.lba;
-		else return (-EINVAL);
+		else RETURN_UP(-EINVAL);
 #if 000
 		i=cc_SetSpeed(speed_150,0,0);
 		if (i) msg(DBG_AUD,"read_audio: SetSpeed error %d\n", i);
@@ -4236,8 +4314,10 @@
 		msg(DBG_AUD,"read_audio: lba: %d, msf: %06X\n",
 		    block, blk2msf(block));
 		msg(DBG_AUD,"read_audio: before cc_ReadStatus.\n");
+#if OLD_BUSY
 		while (busy_data) sbp_sleep(HZ/10); /* wait a bit */
 		busy_audio=1;
+#endif OLD_BUSY
 		error_flag=0;
 		for (data_tries=5; data_tries>0; data_tries--)
 		{
@@ -4400,43 +4480,45 @@
 		cc_ModeSelect(CD_FRAMESIZE);
 		cc_ModeSense();
 		D_S[d].mode=READ_M1;
+#if OLD_BUSY
 		busy_audio=0;
+#endif OLD_BUSY
 		if (data_tries == 0)
 		{
 			msg(DBG_AUD,"read_audio: failed after 5 tries.\n");
-			return (-8);
+			RETURN_UP(-EIO);
 		}
 		msg(DBG_AUD,"read_audio: successful return.\n");
-		return (0);
+		RETURN_UP(0);
 	} /* end of CDROMREADAUDIO */
 		
 	case CDROMMULTISESSION: /* tell start-of-last-session */
 		msg(DBG_IOC,"ioctl: CDROMMULTISESSION entered.\n");
 		st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_multisession));
-		if (st) return (st);
+		if (st) RETURN_UP(st);
 		memcpy_fromfs(&ms_info, (void *) arg, sizeof(struct cdrom_multisession));
 		if (ms_info.addr_format==CDROM_MSF) /* MSF-bin requested */
 			lba2msf(D_S[d].lba_multi,&ms_info.addr.msf.minute);
 		else if (ms_info.addr_format==CDROM_LBA) /* lba requested */
 			ms_info.addr.lba=D_S[d].lba_multi;
-		else return (-EINVAL);
+		else RETURN_UP(-EINVAL);
 		if (D_S[d].f_multisession) ms_info.xa_flag=1; /* valid redirection address */
 		else ms_info.xa_flag=0; /* invalid redirection address */
 		memcpy_tofs((void *) arg, &ms_info, sizeof(struct cdrom_multisession));
 		msg(DBG_MUL,"ioctl: CDROMMULTISESSION done (%d, %08X).\n",
 		    ms_info.xa_flag, ms_info.addr.lba);
-		return (0);
+		RETURN_UP(0);
 		
 	case BLKRASET:
-		if(!suser())  return -EACCES;
-		if(!(inode->i_rdev)) return -EINVAL;
-		if(arg > 0xff) return -EINVAL;
+		if(!suser()) RETURN_UP(-EACCES);
+		if(!(inode->i_rdev)) RETURN_UP(-EINVAL);
+		if(arg > 0xff) RETURN_UP(-EINVAL);
 		read_ahead[MAJOR(inode->i_rdev)] = arg;
-		return (0);
+		RETURN_UP(0);
 		
 	default:
 		msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd);
-		return (-EINVAL);
+		RETURN_UP(-EINVAL);
 	} /* end switch(cmd) */
 }
 /*==========================================================================*/
@@ -4489,6 +4571,7 @@
 static inline void sbpcd_end_request(struct request *req, int uptodate) {
 	req->next=CURRENT;
 	CURRENT=req;
+	up(&ioctl_read_sem);
 	end_request(uptodate);
 }
 /*==========================================================================*/
@@ -4528,6 +4611,7 @@
 	CURRENT=req->next;	/* task can fuck it up         GTL  */
 	sti();
 	
+	down(&ioctl_read_sem);
 	if (req->rq_status == RQ_INACTIVE)
 		sbpcd_end_request(req, 0);
 	if (req -> sector == -1)
@@ -4545,8 +4629,10 @@
 			kdevname(req->rq_dev));
 		goto err_done;
 	}
+#if OLD_BUSY
 	while (busy_audio) sbp_sleep(HZ); /* wait a bit */
 	busy_data=1;
+#endif OLD_BUSY
 	
 	if (D_S[i].audio_state==audio_playing) goto err_done;
 	if (d!=i) switch_drive(i);
@@ -4612,7 +4698,9 @@
 	}
 	
  err_done:
+#if OLD_BUSY
 	busy_data=0;
+#endif OLD_BUSY
 #ifdef DEBUG_GTL
 	printk(" do_sbpcd_request[%do](%p:%ld+%ld) end 4 (error), Time:%li\n",
 		xnr, req, req->sector, req->nr_sectors, jiffies);
@@ -4753,8 +4841,8 @@
 #if LONG_TIMING
 	max_latency=9*HZ;
 #else
-	if (D_S[d].f_multisession) max_latency=9*HZ;
-	else max_latency=3*HZ;
+	if (D_S[d].f_multisession) max_latency=15*HZ;
+	else max_latency=5*HZ;
 #endif
 	duration=jiffies;
 	for (frame=0;frame<D_S[d].sbp_read_frames&&!error_flag; frame++)
@@ -4855,9 +4943,16 @@
 		wait=8;
 		do
 		{
-			sbp_sleep(1);
-			OUT(CDo_sel_i_d,0);
-			i=inb(CDi_status);
+			if (teac==2)
+                          {
+                            if ((i=CDi_stat_loop_T()) == -1) break;
+                          }
+                        else
+                          {
+                            sbp_sleep(1);
+                            OUT(CDo_sel_i_d,0); 
+                            i=inb(CDi_status);
+                          } 
 			if (!(i&s_not_data_ready))
 			{
 				OUT(CDo_sel_i_d,1);
@@ -5018,22 +5113,26 @@
 	if (fp->f_mode & 2)
 		return -EROFS;
 	
+	down(&ioctl_read_sem);
 	switch_drive(i);
 	
 	i=cc_ReadError();
 	flags_cmd_out |= f_respo2;
-	cc_ReadStatus();                         /* command: give 1-byte status */
+	cc_ReadStatus(); /* command: give 1-byte status */
 	i=ResponseStatus();
 	if (famT_drive&&(i<0))
 	{
 		cc_DriveReset();
 		i=ResponseStatus();
+#if 0
+                sbp_sleep(HZ);
+#endif 0
 		i=ResponseStatus();
 	}
 	if (i<0)
 	{
 		msg(DBG_INF,"sbpcd_open: ResponseStatus timed out (%d).\n",i);
-		return (-EIO);                  /* drive doesn't respond */
+		RETURN_UP(-EIO);                  /* drive doesn't respond */
 	}
 	if (famT_drive)	msg(DBG_TEA,"sbpcd_open: ResponseStatus=%02X\n", i);
 	if (!st_door_closed)
@@ -5065,7 +5164,7 @@
 			cc_SpinDown(); /* eject tray */
 		}
 #endif
-		return (-ENXIO);
+		RETURN_UP(-ENXIO);
 	}
 	/*
 	 * try to keep an "open" counter here and lock the door if 0->1.
@@ -5092,7 +5191,7 @@
 #endif SAFE_MIXED
 	}
 	if (!st_spinning) cc_SpinUp();
-	return (0);
+	RETURN_UP(0);
 }
 /*==========================================================================*/
 /*
@@ -5108,6 +5207,7 @@
 		msg(DBG_INF, "release: bad device: %04X\n", ip->i_rdev);
 		return;
 	}
+	down(&ioctl_read_sem);
 	switch_drive(i);
 	/*
 	 * try to keep an "open" counter here and unlock the door if 1->0.
@@ -5132,6 +5232,7 @@
 #endif SAFE_MIXED
 		}
 	}
+	up(&ioctl_read_sem);
 }
 /*==========================================================================*/
 /*
@@ -5365,7 +5466,10 @@
 	}
 	
 	if (port_index>0)
-		msg(DBG_INF, "You should configure sbpcd.h for your hardware.\n");
+          {
+            msg(DBG_INF, "You should read linux/Documentation/cdrom/sbpcd\n");
+            msg(DBG_INF, "and then configure sbpcd.h for your hardware.\n");
+          }
 	check_datarate();
 	msg(DBG_INI,"check_datarate done.\n");
 	
@@ -5450,7 +5554,7 @@
 #endif MODULE
 	}
 	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-	read_ahead[MAJOR_NR] = SBP_BUFFER_FRAMES * (CD_FRAMESIZE / 512);
+	read_ahead[MAJOR_NR] = buffers * (CD_FRAMESIZE / 512);
 	
 	request_region(CDo_command,4,major_name);
 	
@@ -5466,7 +5570,7 @@
 		 */
 		D_S[j].aud_buf=NULL;
 		D_S[j].sbp_audsiz=0;
-		D_S[j].sbp_bufsiz=SBP_BUFFER_FRAMES;
+		D_S[j].sbp_bufsiz=buffers;
 		if (D_S[j].drv_type&drv_fam1)
 			if (READ_AUDIO>0) D_S[j].sbp_audsiz=READ_AUDIO;
 		D_S[j].sbp_buf=(u_char *) vmalloc(D_S[j].sbp_bufsiz*CD_FRAMESIZE);
@@ -5476,7 +5580,7 @@
 			return -EIO;
 		}
 #ifdef MODULE
-		msg(DBG_INF,"data buffer size: %d frames.\n",SBP_BUFFER_FRAMES);
+		msg(DBG_INF,"data buffer size: %d frames.\n",buffers);
 #endif MODULE
 		if (D_S[j].sbp_audsiz>0)
 		{
diff -urN linux-2.0.15/drivers/char/amiga_ser.c linux-2.0.18/drivers/char/amiga_ser.c
--- linux-2.0.15/drivers/char/amiga_ser.c	Mon Aug 26 22:09:39 1996
+++ linux-2.0.18/drivers/char/amiga_ser.c	Tue Sep  3 21:56:19 1996
@@ -8,6 +8,10 @@
  * - fixed reentrancy problem in ser_tx_int()
  * 
  * 27/04/96 - Jes Soerensen: Upgraded for Linux-1.3.x.
+ * 02/09/96 - Jes Soerensen: Moved the {request,free}_irq call for
+ *            AMIGA_VERTB interrupts into the init/deinit funtions as
+ *            there is no reason to service the ser_vbl_int when the
+ *            serial port is not in use.
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file COPYING in the main directory of this archive
@@ -137,11 +141,13 @@
 
 static __inline__ void check_modem_status(struct async_struct *info)
 {
-	unsigned char bits = ciab.pra & (SER_DCD | SER_CTS);
+	unsigned char bits;
 
 	if (!(info->flags & ASYNC_INITIALIZED))
 		return;
-  
+
+	bits = ciab.pra & (SER_DCD | SER_CTS);
+
 	if (bits ^ current_ctl_bits) {
 		if ((bits ^ current_ctl_bits) & SER_DCD) {
 			rs_dcd_changed(info, !(bits & SER_DCD));
@@ -157,7 +163,7 @@
 
 int amiga_serinit( void )
 {	
-    unsigned long flags;
+	unsigned long flags;
 	struct serial_struct req;
 	struct async_struct *info;
 	
@@ -181,7 +187,6 @@
     request_irq(IRQ_AMIGA_RBF, ser_rx_int, 0, "serial RX", info);
 
     amiga_info = info;
-    request_irq(IRQ_AMIGA_VERTB, ser_vbl_int, 0, "serial status", info);
 
     /* turn off Rx and Tx interrupts */
     custom.intena = IF_RBF | IF_TBE;
@@ -241,6 +246,8 @@
 
 static void ser_init( struct async_struct *info )
 {	
+	request_irq(IRQ_AMIGA_VERTB, ser_vbl_int, 0,
+		    "serial status", amiga_info);
 
 	/* enable both Rx and Tx interrupts */
 	custom.intena = IF_SETCLR | IF_RBF | IF_TBE;
@@ -288,6 +295,7 @@
     if (!leave_dtr)
 	ser_DTRoff();
 
+    free_irq(IRQ_AMIGA_VERTB, amiga_info);
     MOD_DEC_USE_COUNT;
 }
 
@@ -345,7 +353,7 @@
 	if (baud > 16) baud = 16;
 	realbaud = baud_table[baud];
 	if (realbaud)
-	    div = (amiga_colorclock+realbaud/2)/realbaud;
+	    div = (amiga_colorclock+realbaud/2)/realbaud-1;
     }
 
     if (div) {
@@ -445,7 +453,6 @@
 custom.intreq = IF_RBF | IF_TBE; /* clear pending interrupts */
 free_irq(IRQ_AMIGA_TBE, amiga_info);
 free_irq(IRQ_AMIGA_RBF, amiga_info);
-free_irq(IRQ_AMIGA_VERTB, amiga_info);
 }
 #endif
 
diff -urN linux-2.0.15/drivers/char/amigamouse.c linux-2.0.18/drivers/char/amigamouse.c
--- linux-2.0.15/drivers/char/amigamouse.c	Mon Aug 26 22:09:40 1996
+++ linux-2.0.18/drivers/char/amigamouse.c	Mon Sep  2 21:55:33 1996
@@ -26,6 +26,10 @@
  *   renamed this file mouse.c => busmouse.c
  *
  * Modified for use in the 1.3 kernels by Jes Sorensen.
+ *
+ * Moved the isr-allocation to the mouse_{open,close} calls, as there
+ *   is no reason to service the mouse in the vertical blank isr if
+ *   the mouse is not in use.             Jes Sorensen
  */
 
 #include <linux/module.h>
@@ -172,6 +176,7 @@
 	fasync_mouse(inode, file, 0);
 	if (--mouse.active)
 	  return;
+	free_irq(IRQ_AMIGA_VERTB, mouse_interrupt);
 	MSE_INT_OFF();
 	MOD_DEC_USE_COUNT;
 }
@@ -183,18 +188,29 @@
 
 static int open_mouse(struct inode * inode, struct file * file)
 {
-  if (!mouse.present)
-    return -EINVAL;
-  if (mouse.active++)
-    return 0;
-  mouse.ready = 0;
-  mouse.dx = 0;
-  mouse.dy = 0;
-  mouse.buttons = 0x87;
-  mouse.active = 1;
-  MOD_INC_USE_COUNT;
-  MSE_INT_ON();
-  return 0;
+	if (!mouse.present)
+		return -EINVAL;
+	if (mouse.active++)
+		return 0;
+	/*
+	 *  use VBL to poll mouse deltas
+	 */
+
+	if(request_irq(IRQ_AMIGA_VERTB, mouse_interrupt, 0,
+	               "Amiga mouse", mouse_interrupt)) {
+		mouse.present = 0;
+		printk(KERN_INFO "Installing Amiga mouse failed.\n");
+		return -EIO;
+	}
+
+	mouse.ready = 0;
+	mouse.dx = 0;
+	mouse.dy = 0;
+	mouse.buttons = 0x87;
+	mouse.active = 1;
+	MOD_INC_USE_COUNT;
+	MSE_INT_ON();
+	return 0;
 }
 
 /*
@@ -306,17 +322,6 @@
 	mouse.dy = 0;
 	mouse.wait = NULL;
 
-	/*
-	 *  use VBL to poll mouse deltas
-	 */
-
-	if(request_irq(IRQ_AMIGA_VERTB, mouse_interrupt, 0,
-	               "Amiga mouse", mouse_interrupt)) {
-		mouse.present = 0;
-		printk(KERN_INFO "Installing Amiga mouse failed.\n");
-		return -EIO;
-	}
-
 	mouse.present = 1;
 
 	printk(KERN_INFO "Amiga mouse installed.\n");
@@ -334,7 +339,6 @@
 
 void cleanup_module(void)
 {
-	free_irq(IRQ_AMIGA_VERTB, mouse_interrupt);
 	misc_deregister(&amiga_mouse);
 }
 #endif
diff -urN linux-2.0.15/drivers/char/console.c linux-2.0.18/drivers/char/console.c
--- linux-2.0.15/drivers/char/console.c	Mon Aug 26 22:09:44 1996
+++ linux-2.0.18/drivers/char/console.c	Thu Sep  5 17:46:39 1996
@@ -1433,34 +1433,34 @@
 		  tc = translate[toggle_meta ? (c|0x80) : c];
 		}
 
-		/* If the original code was < 32 we only allow a
-		 * glyph to be displayed if the code is not normally
-		 * used (such as for cursor movement) or if the
-		 * disp_ctrl mode has been explicitly enabled.
-		 * Note: ESC is *never* allowed to be displayed as
-		 * that would disable all escape sequences!
-		 * To display font position 0x1B, go into UTF mode
-		 * and display character U+F01B, or change the mapping.
-		 */
-		ok = (tc && (c >= 32 || (!utf && !(((disp_ctrl ? CTRL_ALWAYS
-					    : CTRL_ACTION) >> c) & 1))));
+                /* If the original code was a control character we
+                 * only allow a glyph to be displayed if the code is
+                 * not normally used (such as for cursor movement) or
+                 * if the disp_ctrl mode has been explicitly enabled.
+                 * Certain characters (as given by the CTRL_ALWAYS
+                 * bitmap) are always displayed as control characters,
+                 * as the console would be pretty useless without
+                 * them; to display an arbitrary font position use the
+                 * direct-to-font zone in UTF-8 mode.
+                 */
+                ok = tc && (c >= 32 ||
+                            (!utf && !(((disp_ctrl ? CTRL_ALWAYS
+                                         : CTRL_ACTION) >> c) & 1)))
+                        && (c != 127 || disp_ctrl);
 
 		if (vc_state == ESnormal && ok) {
 			/* Now try to find out how to display it */
 			tc = conv_uni_to_pc(tc);
-			if ( tc == -4 )
-			  {
-			    /* If we got -4 (not found) then see if we have
-			       defined a replacement character (U+FFFD) */
-			    tc = conv_uni_to_pc(0xfffd);
-			  }
-			else if ( tc == -3 )
-			  {
-			    /* Bad hash table -- hope for the best */
-			    tc = c;
-			  }
+			if ( tc == -4 ) {
+                                /* If we got -4 (not found) then see if we have
+                                   defined a replacement character (U+FFFD) */
+                                tc = conv_uni_to_pc(0xfffd);
+                        } else if ( tc == -3 ) {
+                                /* Bad hash table -- hope for the best */
+                                tc = c;
+                        }
 			if (tc & ~console_charmask)
-			  continue; /* Conversion failed */
+                                continue; /* Conversion failed */
 
 			if (need_wrap) {
 				cr(currcons);
diff -urN linux-2.0.15/drivers/char/ftape/fdc-io.c linux-2.0.18/drivers/char/ftape/fdc-io.c
--- linux-2.0.15/drivers/char/ftape/fdc-io.c	Mon Aug 26 22:10:08 1996
+++ linux-2.0.18/drivers/char/ftape/fdc-io.c	Thu Sep  5 17:46:49 1996
@@ -311,7 +311,7 @@
 	int current_blocked = current->blocked;
 	static int resetting = 0;
 
-	if (wait_intr) {
+	if (waitqueue_active(&wait_intr)) {
 		TRACE(1, "error: nested call");
 		return -EIO;	/* return error... */
 	}
diff -urN linux-2.0.15/drivers/char/ftape/fdc-isr.c linux-2.0.18/drivers/char/ftape/fdc-isr.c
--- linux-2.0.15/drivers/char/ftape/fdc-isr.c	Mon Aug 26 22:10:08 1996
+++ linux-2.0.18/drivers/char/ftape/fdc-isr.c	Thu Sep  5 17:46:50 1996
@@ -771,7 +771,7 @@
 			}
 			seek_completed = 1;
 			fdc_mode = fdc_idle;
-		} else if (!wait_intr) {
+		} else if (!waitqueue_active(&wait_intr)) {
 			if (expected_stray_interrupts == 0) {
 				TRACE(2, "unexpected stray interrupt");
 			} else {
diff -urN linux-2.0.15/drivers/char/lp_m68k.c linux-2.0.18/drivers/char/lp_m68k.c
--- linux-2.0.15/drivers/char/lp_m68k.c	Mon Aug 26 22:09:51 1996
+++ linux-2.0.18/drivers/char/lp_m68k.c	Thu Sep  5 17:46:42 1996
@@ -363,9 +363,9 @@
 	int dev = MINOR(inode->i_rdev);
 
 	if (dev >= max_lp)
-		return -ENODEV;
+		return -ENXIO;
 	if (!(lp_table[dev].flags & LP_EXIST))
-		return -ENODEV;
+		return -ENXIO;
 	if (lp_table[dev].flags & LP_BUSY)
 		return -EBUSY;
 
diff -urN linux-2.0.15/drivers/char/m68kserial.c linux-2.0.18/drivers/char/m68kserial.c
--- linux-2.0.15/drivers/char/m68kserial.c	Mon Aug 26 22:09:51 1996
+++ linux-2.0.18/drivers/char/m68kserial.c	Thu Sep  5 17:46:43 1996
@@ -1240,7 +1240,7 @@
 
 	line = MINOR(tty->device) - tty->driver.minor_start;
 	if ((line < 0) || (line >= NR_PORTS))
-		return -ENODEV;
+		return -ENXIO;
 	info = rs_table + line;
 #ifdef CONFIG_KERNELD
 	if (!info->port) {
diff -urN linux-2.0.15/drivers/char/n_tty.c linux-2.0.18/drivers/char/n_tty.c
--- linux-2.0.15/drivers/char/n_tty.c	Mon Aug 26 22:09:52 1996
+++ linux-2.0.18/drivers/char/n_tty.c	Thu Sep  5 17:46:44 1996
@@ -795,7 +795,7 @@
 		  	current->timeout = (unsigned long) -1;
 			if (time)
 				tty->minimum_to_wake = 1;
-			else if (!tty->read_wait ||
+			else if (!waitqueue_active(&tty->read_wait) ||
 				 (tty->minimum_to_wake > minimum))
 				tty->minimum_to_wake = minimum;
 		} else {
@@ -907,7 +907,7 @@
 	}
 	remove_wait_queue(&tty->read_wait, &wait);
 
-	if (!tty->read_wait)
+	if (!waitqueue_active(&tty->read_wait))
 		tty->minimum_to_wake = minimum;
 
 	current->state = TASK_RUNNING;
@@ -991,7 +991,7 @@
 				return 1;
 			if (tty_hung_up_p(file))
 				return 1;
-			if (!tty->read_wait) {
+			if (!waitqueue_active(&tty->read_wait)) {
 				if (MIN_CHAR(tty) && !TIME_CHAR(tty))
 					tty->minimum_to_wake = MIN_CHAR(tty);
 				else
diff -urN linux-2.0.15/drivers/char/tty_io.c linux-2.0.18/drivers/char/tty_io.c
--- linux-2.0.15/drivers/char/tty_io.c	Mon Aug 26 22:10:01 1996
+++ linux-2.0.18/drivers/char/tty_io.c	Sat Sep  7 09:53:34 1996
@@ -380,8 +380,9 @@
 			continue;
 		if (filp->private_data != tty)
 			continue;
-		if (filp->f_inode
-		    && filp->f_inode->i_rdev == CONSOLE_DEV)
+		if (!filp->f_inode)
+			continue;
+		if (filp->f_inode->i_rdev == CONSOLE_DEV)
 			continue;
 		if (filp->f_op != &tty_fops)
 			continue;
@@ -1349,7 +1350,7 @@
 		return retval;
 
 	if (on) {
-		if (!tty->read_wait)
+		if (!waitqueue_active(&tty->read_wait))
 			tty->minimum_to_wake = 1;
 		if (filp->f_owner == 0) {
 			if (tty->pgrp)
@@ -1358,7 +1359,7 @@
 				filp->f_owner = current->pid;
 		}
 	} else {
-		if (!tty->fasync && !tty->read_wait)
+		if (!tty->fasync && !waitqueue_active(&tty->read_wait))
 			tty->minimum_to_wake = N_TTY_BUF_SIZE;
 	}
 	return 0;
diff -urN linux-2.0.15/drivers/isdn/icn/icn.c linux-2.0.18/drivers/isdn/icn/icn.c
--- linux-2.0.15/drivers/isdn/icn/icn.c	Fri Aug 30 13:05:55 1996
+++ linux-2.0.18/drivers/isdn/icn/icn.c	Thu Sep  5 17:46:59 1996
@@ -1,4 +1,4 @@
-/* $Id: icn.c,v 1.28 1996/06/28 17:02:53 fritz Exp $
+/* $Id: icn.c,v 1.29 1996/08/29 20:34:54 fritz Exp $
  *
  * ISDN low-level module for the ICN active ISDN-Card.
  *
@@ -19,6 +19,11 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: icn.c,v $
+ * Revision 1.29  1996/08/29 20:34:54  fritz
+ * Bugfix in send queue management:
+ * sndcount was not updated correctly.
+ * Minor Bugfixes.
+ *
  * Revision 1.28  1996/06/28 17:02:53  fritz
  * replaced memcpy_fromfs_toio.
  *
@@ -136,7 +141,7 @@
 #undef MAP_DEBUG
 
 static char
-*revision = "$Revision: 1.28 $";
+*revision = "$Revision: 1.29 $";
 
 static int icn_addcard(int, char *, char *);
 
@@ -675,9 +680,7 @@
 /* Append a packet to the transmit buffer-queue.
  * Parameters:
  *   channel = Number of B-channel
- *   buffer  = pointer to packet
- *   len     = size of packet (max 4000)
- *   user    = 1 = call from userproc, 0 = call from kernel
+ *   skb     = pointer to sk_buff
  *   card    = pointer to card-struct
  * Return:
  *   Number of bytes transferred, -E??? on error
@@ -701,19 +704,25 @@
                         return 0;
                 save_flags(flags);
                 cli();
-                card->sndcount[channel] += len;
                 nskb = skb_clone(skb, GFP_ATOMIC);
                 if (nskb) {
-                   skb_queue_tail(&card->spqueue[channel], nskb);
-                   dev_kfree_skb(skb, FREE_WRITE);
-                }
+                        skb_queue_tail(&card->spqueue[channel], nskb);
+                        dev_kfree_skb(skb, FREE_WRITE);
+                } else
+                        len = 0;
+                card->sndcount[channel] += len;
                 restore_flags(flags);
-                if (!nskb) 
-                   return 0;
         }
         return len;
 }
 
+/*
+ * Check card's status after starting the bootstrap loader.
+ * On entry, the card's shared memory has already to be mapped.
+ * Return:
+ *   0 on success (Boot loader ready)
+ *   -EIO on failure (timeout)
+ */
 static int icn_check_loader(int cardnumber)
 {
         int timer = 0;
@@ -1162,7 +1171,19 @@
                         case ICN_IOCTL_GETDOUBLE:
                                 return (int) card->doubleS0;
                         case ICN_IOCTL_DEBUGVAR:
-                                return (ulong) card;
+				if ((i = verify_area(VERIFY_WRITE,
+                                                    (void *) a,
+                                                    sizeof(ulong) * 2)))
+                                        return i;
+                                memcpy_tofs((char *)a,
+                                            (char *)&card, sizeof(ulong));
+				a += sizeof(ulong);
+				{
+                                        ulong l = (ulong)&dev;
+                                        memcpy_tofs((char *)a,
+                                                    (char *)&l, sizeof(ulong));
+                                }
+                                return 0;
                         case ICN_IOCTL_LOADBOOT:
                                 icn_stopcard(card);
                                 return (icn_loadboot((u_char *) a, card));
@@ -1630,7 +1651,7 @@
                 }
                 card = card->next;
         }
-        card = card;
+        card = cards;
         while (card) {
                 last = card;
                 card = card->next;
diff -urN linux-2.0.15/drivers/isdn/icn/icn.h linux-2.0.18/drivers/isdn/icn/icn.h
--- linux-2.0.15/drivers/isdn/icn/icn.h	Mon Aug 26 22:10:22 1996
+++ linux-2.0.18/drivers/isdn/icn/icn.h	Thu Sep  5 17:47:01 1996
@@ -1,4 +1,4 @@
-/* $Id: icn.h,v 1.20 1996/06/24 17:20:37 fritz Exp $
+/* $Id: icn.h,v 1.21 1996/08/29 20:35:57 fritz Exp $
  *
  * ISDN lowlevel-module for the ICN active ISDN-Card.
  *
@@ -19,6 +19,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: icn.h,v $
+ * Revision 1.21  1996/08/29 20:35:57  fritz
+ * Speed up B-Channel polling interval.
+ *
  * Revision 1.20  1996/06/24 17:20:37  fritz
  * Bugfixes in pollbchan_send():
  *   - Using lock field of skbuff breaks networking.
@@ -156,7 +159,7 @@
 #define ICN_BOOT_TIMEOUT1  100   /* Delay for Boot-download (jiffies)       */
 #define ICN_CHANLOCK_DELAY  10   /* Delay for Channel-mapping (jiffies)     */
 
-#define ICN_TIMER_BCREAD 3       /* B-Channel poll-cycle                    */
+#define ICN_TIMER_BCREAD 1       /* B-Channel poll-cycle                    */
 #define ICN_TIMER_DCREAD 50      /* D-Channel poll-cycle                    */
 
 #define ICN_CODE_STAGE1 4096     /* Size of bootcode                        */
diff -urN linux-2.0.15/drivers/isdn/isdn_net.c linux-2.0.18/drivers/isdn/isdn_net.c
--- linux-2.0.15/drivers/isdn/isdn_net.c	Mon Aug 26 22:10:18 1996
+++ linux-2.0.18/drivers/isdn/isdn_net.c	Thu Sep  5 17:46:53 1996
@@ -1,4 +1,4 @@
-/* $Id: isdn_net.c,v 1.18 1996/07/03 13:48:51 hipp Exp $
+/* $Id: isdn_net.c,v 1.20 1996/08/29 20:06:03 fritz Exp $
  *
  * Linux ISDN subsystem, network interfaces and related functions (linklevel).
  *
@@ -21,6 +21,12 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdn_net.c,v $
+ * Revision 1.20  1996/08/29 20:06:03  fritz
+ * Bugfix: Transmission timeout had been much to low.
+ *
+ * Revision 1.19  1996/08/12 16:24:32  hipp
+ * removed some (now) obsolete functions for syncPPP in rebuild_header etc.
+ *
  * Revision 1.18  1996/07/03 13:48:51  hipp
  * bugfix: Call dev_purge_queues() only for master device
  *
@@ -124,7 +130,7 @@
  
 extern void dev_purge_queues(struct device *dev);	/* move this to net/core/dev.c */
 
-char *isdn_net_revision = "$Revision: 1.18 $";
+char *isdn_net_revision = "$Revision: 1.20 $";
 
  /*
   * Code for raw-networking over ISDN
@@ -369,6 +375,10 @@
                                                  */
                                                 lp->chargetime = jiffies;
                                                 /* Immediately send first skb to speed up arp */
+#ifdef CONFIG_ISDN_PPP
+						if(lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+							isdn_ppp_wakeup_daemon(lp);
+#endif
                                                 if (lp->first_skb) {
                                                         if (!(isdn_net_xmit(&p->dev,lp,lp->first_skb)))
                                                                 lp->first_skb = NULL;
@@ -658,7 +668,6 @@
         u_char *p = buf;
 	unsigned short proto = ETH_P_IP;
         int data_ofs;
-        int len;
 	ip_ports *ipp;
 	char addinfo[100];
 
@@ -676,22 +685,6 @@
                         proto = ntohs(*(unsigned short *)&buf[2]);
                         p = &buf[4];
                         break;
-                case ISDN_NET_ENCAP_SYNCPPP:
-                        len = 4;
-#ifdef CONFIG_ISDN_MPP
-                        if (lp->ppp_minor!=-1) {
-                                if (ippp_table[lp->ppp_minor]->mpppcfg &
-                                    SC_MP_PROT) {
-                                        if (ippp_table[lp->ppp_minor]->mpppcfg &
-                                            SC_OUT_SHORT_SEQ)
-                                                len = 7;
-                                        else
-                                                len = 9;
-                                }
-                        }
-#endif
-                        p = &buf[len];
-                        break;
         }
 	data_ofs = ((p[0] & 15) * 4);
         switch (proto) {
@@ -851,7 +844,7 @@
 	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
 
 	if (ndev->tbusy) {
-		if (jiffies - ndev->trans_start < 20)
+		if (jiffies - ndev->trans_start < (2 * HZ))
 			return 1;
                 if (!lp->dialstate)
                         lp->stats.tx_errors++;
@@ -1248,29 +1241,6 @@
                         *((ushort*)&skb->data[2]) = htons(type);
                         len = 4;
                         break;
-#ifdef CONFIG_ISDN_PPP
-                case ISDN_NET_ENCAP_SYNCPPP:
-                        /* reserve space to be filled in isdn_ppp_xmit */
-                        len = 4;
-#ifdef CONFIG_ISDN_MPP
-                        if (lp->ppp_minor!=-1) {
-                                if (ippp_table[lp->ppp_minor]->mpppcfg &
-                                    SC_MP_PROT) {
-                                        if (ippp_table[lp->ppp_minor]->mpppcfg &
-                                            SC_OUT_SHORT_SEQ)
-                                                len = 7;
-                                        else
-                                                len = 9;
-                                }
-                        }
-#endif
-                        /* Initialize first 4 bytes to a value, which is
-                         * guaranteed to be invalid. Need that to check
-                         * for already compressed packets in isdn_ppp_xmit().
-                         */
-                        *((u32 *)skb_push(skb, len)) = 0;
-                        break;
-#endif
 	}
 	return len;
 }
@@ -1756,7 +1726,7 @@
                                         dev->st_netdev[idx] = lp->netdev;
 					p->local.isdn_device = di;
 					p->local.isdn_channel = ch;
-					p->local.ppp_minor = -1;
+					p->local.ppp_slot = -1;
 					p->local.pppbind = -1;
 					p->local.flags |= ISDN_NET_CONNECTED;
 					p->local.dialstate = 7;
@@ -1927,7 +1897,7 @@
 	netdev->local.pre_device = -1;
 	netdev->local.pre_channel = -1;
 	netdev->local.exclusive = -1;
-	netdev->local.ppp_minor = -1;
+	netdev->local.ppp_slot = -1;
 	netdev->local.pppbind = -1;
 	netdev->local.l2_proto = ISDN_PROTO_L2_X75I;
 	netdev->local.l3_proto = ISDN_PROTO_L3_TRANS;
@@ -2443,8 +2413,7 @@
 	for(i=0;i<DEV_NUMBUFFS;i++) {
 		struct sk_buff *skb;
 		while((skb=skb_dequeue(&dev->buffs[i])))
-			if(skb->free)
-				kfree_skb(skb,FREE_WRITE);
+				dev_kfree_skb(skb,FREE_WRITE);
         }
 	
 }
diff -urN linux-2.0.15/drivers/isdn/isdn_ppp.c linux-2.0.18/drivers/isdn/isdn_ppp.c
--- linux-2.0.15/drivers/isdn/isdn_ppp.c	Mon Aug 26 22:10:19 1996
+++ linux-2.0.18/drivers/isdn/isdn_ppp.c	Thu Sep  5 17:46:56 1996
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.c,v 1.13 1996/07/01 19:47:24 hipp Exp $
+/* $Id: isdn_ppp.c,v 1.14 1996/08/12 16:26:47 hipp Exp $
  *
  * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -19,6 +19,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdn_ppp.c,v $
+ * Revision 1.14  1996/08/12 16:26:47  hipp
+ * code cleanup
+ * changed connection management from minors to slots
+ *
  * Revision 1.13  1996/07/01 19:47:24  hipp
  * Fixed memory leak in VJ handling and more VJ changes
  *
@@ -82,21 +86,21 @@
 #endif
  
 /* Prototypes */
-static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int minor);
-static int isdn_ppp_closewait(int);
+static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot);
+static int isdn_ppp_closewait(int slot);
 static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
 			struct sk_buff *skb, int proto);
-static int isdn_ppp_if_get_unit(char **namebuf);
+static int isdn_ppp_if_get_unit(char *namebuf);
 
 #ifdef CONFIG_ISDN_MPP
-static int isdn_ppp_bundle(int, int);
+static int isdn_ppp_bundle(struct ippp_struct *, int unit);
 static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask);
 static void isdn_ppp_cleanup_queue(isdn_net_dev * dev, long min);
 static int isdn_ppp_fill_mpqueue(isdn_net_dev *, struct sk_buff **skb,
 		int BEbyte, int *sqno, int min_sqno);
 #endif
 
-char *isdn_ppp_revision              = "$Revision: 1.13 $";
+char *isdn_ppp_revision              = "$Revision: 1.14 $";
 struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
 
 extern int isdn_net_force_dial_lp(isdn_net_local *);
@@ -109,10 +113,13 @@
 {
 	isdn_net_local *master_lp=lp;
 	unsigned long flags;
+	struct ippp_struct *is;
 
-	if (lp->ppp_minor < 0)
+	if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS)
 		return 0;
 
+	is = ippp_table[lp->ppp_slot];
+
 	save_flags(flags);
 	cli();
 #ifdef CONFIG_ISDN_MPP
@@ -132,13 +139,13 @@
 	lp->next = lp->last = lp;	/* (re)set own pointers */
 #endif
 
-	isdn_ppp_closewait(lp->ppp_minor);	/* force wakeup on ippp device */
+	isdn_ppp_closewait(lp->ppp_slot);	/* force wakeup on ippp device */
 
-	if(ippp_table[lp->ppp_minor]->debug & 0x1)
-		printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_minor, (long) lp,(long) ippp_table[lp->ppp_minor]->lp);
+	if(is->debug & 0x1)
+		printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp,(long) is->lp);
 
-	ippp_table[lp->ppp_minor]->lp = NULL;	/* link is down .. set lp to NULL */
-	lp->ppp_minor = -1;			/* is this OK ?? */
+	is->lp = NULL;	/* link is down .. set lp to NULL */
+	lp->ppp_slot = -1;			/* is this OK ?? */
 	restore_flags(flags);
 
 	return 0;
@@ -151,11 +158,11 @@
 {
 	int i;
 	int unit = 0;
-	char *name;
 	long flags;
+	struct ippp_struct *is;
 
 	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
-		return 0;
+		return -1;
 
 	save_flags(flags);
 	cli();
@@ -172,19 +179,18 @@
 			net_dev = net_dev->next;
 		}
 		/*
-		 * search a free device 
+		 * search a free device / slot
 		 */
 		for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-			if (ippp_table[i]->state == IPPP_OPEN && !exclusive[i]) { /* OPEN, but not connected! */
+			if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) { /* OPEN, but not connected! */
 				break;
 			}
 		}
 	}
 	else {
-		if (ippp_table[lp->pppbind]->state == IPPP_OPEN) /* OPEN, but not connected! */
-			i = lp->pppbind;
-		else
-			i = ISDN_MAX_CHANNELS;	/* trigger error */
+		for(i=0;i<ISDN_MAX_CHANNELS;i++)
+			if(ippp_table[i]->minor == lp->pppbind && ippp_table[i]->state == IPPP_OPEN)
+				break;
 	}
 
 	if (i >= ISDN_MAX_CHANNELS) {
@@ -192,24 +198,33 @@
 		printk(KERN_WARNING "isdn_ppp_bind: Can't find usable ippp device.\n");
 		return -1;
 	}
-	lp->ppp_minor = i;
-	ippp_table[lp->ppp_minor]->lp = lp;
 
-	name = lp->name;
-	unit = isdn_ppp_if_get_unit(&name); /* get unit number from interface name .. ugly! */
-	ippp_table[lp->ppp_minor]->unit = unit;
+	unit = isdn_ppp_if_get_unit(lp->name); /* get unit number from interface name .. ugly! */
+	if(unit < 0) {
+		printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n",lp->name);
+		return -1;
+	}
 
-	ippp_table[lp->ppp_minor]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
+	lp->ppp_slot = i;
+	is = ippp_table[i];
+	is->lp = lp;
+	is->unit = unit;
+	is->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
 
 	restore_flags(flags);
 
-        /*
-         * kick the ipppd on the new device 
-         */
-	if (ippp_table[lp->ppp_minor]->wq)
-		wake_up_interruptible(&ippp_table[lp->ppp_minor]->wq);
+	return lp->ppp_slot;
+}
 
-	return lp->ppp_minor;
+/*
+ * kick the ipppd on the device
+ * (wakes up daemon after B-channel connect)
+ */
+
+void isdn_ppp_wakeup_daemon(isdn_net_local *lp)
+{
+	if (ippp_table[lp->ppp_slot]->wq)
+		wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
 }
 
 /*
@@ -217,51 +232,76 @@
  * force wakeup of the ippp device 
  * go into 'device waits for release' state
  */
-static int isdn_ppp_closewait(int minor)
+static int isdn_ppp_closewait(int slot)
 {
-	if (minor < 0 || minor >= ISDN_MAX_CHANNELS)
+	struct ippp_struct *is;
+
+	if (slot < 0 || slot >= ISDN_MAX_CHANNELS)
 		return 0;
+	is = ippp_table[slot];
 
-	if (ippp_table[minor]->state && ippp_table[minor]->wq)
-		wake_up_interruptible(&ippp_table[minor]->wq);
+	if (is->state && is->wq)
+		wake_up_interruptible(&is->wq);
 
-	ippp_table[minor]->state = IPPP_CLOSEWAIT;
+	is->state = IPPP_CLOSEWAIT;
 	return 1;
 }
 
 /*
+ * isdn_ppp_find_slot / isdn_ppp_free_slot
+ */
+
+static int isdn_ppp_get_slot(void)
+{
+	int i;
+	for(i=0;i<ISDN_MAX_CHANNELS;i++) {
+		if(!ippp_table[i]->state)
+			return i;
+	}
+	return -1;
+}
+
+/*
  * isdn_ppp_open 
  */
 
-int isdn_ppp_open(int minor, struct file *file)
+int isdn_ppp_open(int min, struct file *file)
 {
-	if(ippp_table[minor]->debug & 0x1)
-		printk(KERN_DEBUG "ippp, open, minor: %d state: %04x\n", minor,ippp_table[minor]->state);
-	if (ippp_table[minor]->state)
+	int slot;
+	struct ippp_struct *is;
+
+	slot = isdn_ppp_get_slot();
+	if(slot < 0) {
 		return -EBUSY;
+	}
+	is = file->private_data = ippp_table[slot];
 
-	ippp_table[minor]->lp = 0;
-	ippp_table[minor]->mp_seqno = 0;  /* MP sequence number */
-	ippp_table[minor]->pppcfg = 0;    /* ppp configuration */
-	ippp_table[minor]->mpppcfg = 0;   /* mppp configuration */
-	ippp_table[minor]->range = 0x1000000;	/* MP: 24 bit range */
-	ippp_table[minor]->last_link_seqno = -1;	/* MP: maybe set to Bundle-MIN, when joining a bundle ?? */
-	ippp_table[minor]->unit = -1;	/* set, when we have our interface */
-	ippp_table[minor]->mru = 1524;	/* MRU, default 1524 */
-	ippp_table[minor]->maxcid = 16;	/* VJ: maxcid */
-	ippp_table[minor]->tk = current;
-	ippp_table[minor]->wq = NULL;    /* read() wait queue */
-	ippp_table[minor]->wq1 = NULL;   /* select() wait queue */
-	ippp_table[minor]->first = ippp_table[minor]->rq + NUM_RCV_BUFFS - 1; /* receive queue */
-	ippp_table[minor]->last = ippp_table[minor]->rq;
+	if(is->debug & 0x1)
+		printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n",slot, min,is->state);
+
+	is->lp = 0;
+	is->mp_seqno = 0;  /* MP sequence number */
+	is->pppcfg = 0;    /* ppp configuration */
+	is->mpppcfg = 0;   /* mppp configuration */
+	is->range = 0x1000000;	/* MP: 24 bit range */
+	is->last_link_seqno = -1;	/* MP: maybe set to Bundle-MIN, when joining a bundle ?? */
+	is->unit = -1;	/* set, when we have our interface */
+	is->mru = 1524;	/* MRU, default 1524 */
+	is->maxcid = 16;	/* VJ: maxcid */
+	is->tk = current;
+	is->wq = NULL;    /* read() wait queue */
+	is->wq1 = NULL;   /* select() wait queue */
+	is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
+	is->last = is->rq;
+	is->minor = min;
 #ifdef CONFIG_ISDN_PPP_VJ
         /*
          * VJ header compression init
          */
-	ippp_table[minor]->slcomp = slhc_init(16, 16);	/* not necessary for 2. link in bundle */
+	is->slcomp = slhc_init(16, 16);	/* not necessary for 2. link in bundle */
 #endif
 
-	ippp_table[minor]->state = IPPP_OPEN;
+	is->state = IPPP_OPEN;
 
 	return 0;
 }
@@ -269,38 +309,40 @@
 /*
  * release ippp device
  */
-void isdn_ppp_release(int minor, struct file *file)
+void isdn_ppp_release(int min, struct file *file)
 {
 	int i;
+	struct ippp_struct *is;
 
-	if (minor < 0 || minor >= ISDN_MAX_CHANNELS)
+	if (min < 0 || min >= ISDN_MAX_CHANNELS)
 		return;
+	is = file->private_data;
 
-	if(ippp_table[minor]->debug & 0x1)
-		printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", minor, (long) ippp_table[minor]->lp);
+	if(is->debug & 0x1)
+		printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", min, (long) is->lp);
 
-	if (ippp_table[minor]->lp) {	/* a lp address says: this link is still up */
+	if (is->lp) {	/* a lp address says: this link is still up */
 		isdn_net_dev *p = dev->netdev;
-		p = ippp_table[minor]->lp->netdev;
-		ippp_table[minor]->lp->ppp_minor = -1;
-		isdn_net_hangup(&p->dev); /* lp->ppp_minor==-1 => no calling of isdn_ppp_closewait() */
-		ippp_table[minor]->lp = NULL;
+		p = is->lp->netdev;
+		is->lp->ppp_slot = -1;
+		isdn_net_hangup(&p->dev); /* lp->ppp_slot==-1 => no calling of isdn_ppp_closewait() */
+		is->lp = NULL;
 	}
 	for (i = 0; i < NUM_RCV_BUFFS; i++) {
-		if (ippp_table[minor]->rq[i].buf) {
-			kfree(ippp_table[minor]->rq[i].buf);
-			ippp_table[minor]->rq[i].buf = NULL;
+		if (is->rq[i].buf) {
+			kfree(is->rq[i].buf);
+			is->rq[i].buf = NULL;
 		}
 	}
-        ippp_table[minor]->first = ippp_table[minor]->rq + NUM_RCV_BUFFS - 1; /* receive queue */
-        ippp_table[minor]->last = ippp_table[minor]->rq;
+        is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
+        is->last = is->rq;
 
 #ifdef CONFIG_ISDN_PPP_VJ
-	slhc_free(ippp_table[minor]->slcomp);
-	ippp_table[minor]->slcomp = NULL;
+	slhc_free(is->slcomp);
+	is->slcomp = NULL;
 #endif
 
-	ippp_table[minor]->state = 0;
+	is->state = 0;
 }
 
 /*
@@ -330,62 +372,59 @@
 /*
  * ippp device ioctl 
  */
-int isdn_ppp_ioctl(int minor, struct file *file, unsigned int cmd, unsigned long arg)
+int isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	unsigned long val;
 	int r;
+	struct ippp_struct *is;
+
+	is = file->private_data;
 
-	if(ippp_table[minor]->debug & 0x1)
-		printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n",
-			minor,cmd,ippp_table[minor]->state);
+	if(is->debug & 0x1)
+		printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n",min,cmd,is->state);
 
-	if (!(ippp_table[minor]->state & IPPP_OPEN))
+	if (!(is->state & IPPP_OPEN))
 		return -EINVAL;
 
 	switch (cmd) {
-#if 0
-	case PPPIOCSINPSIG:	/* obsolete: set input ready signal */
-		/* usual: sig = SIGIO *//* we always deliver a SIGIO */
-		break;
-#endif
 	case PPPIOCBUNDLE:
 #ifdef CONFIG_ISDN_MPP
 		if ((r = get_arg((void *) arg, &val)))
 			return r;
 		printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
-                        (int) minor, (int) ippp_table[minor]->unit, (int) val);
-		return isdn_ppp_bundle(minor, val);
+                        (int) min, (int) is->unit, (int) val);
+		return isdn_ppp_bundle(is, val);
 #else
 		return -1;
 #endif
 		break;
 	case PPPIOCGUNIT:	/* get ppp/isdn unit number */
-		if ((r = set_arg((void *) arg, ippp_table[minor]->unit)))
+		if ((r = set_arg((void *) arg, is->unit)))
 			return r;
 		break;
 	case PPPIOCGMPFLAGS:	/* get configuration flags */
-		if ((r = set_arg((void *) arg, ippp_table[minor]->mpppcfg)))
+		if ((r = set_arg((void *) arg, is->mpppcfg)))
 			return r;
 		break;
 	case PPPIOCSMPFLAGS:	/* set configuration flags */
 		if ((r = get_arg((void *) arg, &val)))
 			return r;
-		ippp_table[minor]->mpppcfg = val;
+		is->mpppcfg = val;
 		break;
 	case PPPIOCGFLAGS:	/* get configuration flags */
-		if ((r = set_arg((void *) arg, ippp_table[minor]->pppcfg)))
+		if ((r = set_arg((void *) arg, is->pppcfg)))
 			return r;
 		break;
 	case PPPIOCSFLAGS:	/* set configuration flags */
 		if ((r = get_arg((void *) arg, &val))) {
 			return r;
 		}
-		if (val & SC_ENABLE_IP && !(ippp_table[minor]->pppcfg & SC_ENABLE_IP)) {
-			isdn_net_local *lp = ippp_table[minor]->lp;
+		if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP)) {
+			isdn_net_local *lp = is->lp;
 			lp->netdev->dev.tbusy = 0;
 			mark_bh(NET_BH); /* OK .. we are ready to send buffers */
 		}
-		ippp_table[minor]->pppcfg = val;
+		is->pppcfg = val;
 		break;
 #if 0
 	case PPPIOCGSTAT:	/* read PPP statistic information */
@@ -396,7 +435,7 @@
 	case PPPIOCSMRU:	/* set receive unit size for PPP */
 		if ((r = get_arg((void *) arg, &val)))
 			return r;
-		ippp_table[minor]->mru = val;
+		is->mru = val;
 		break;
 	case PPPIOCSMPMRU:
 		break;
@@ -406,33 +445,33 @@
 		if ((r = get_arg((void *) arg, &val)))
 			return r;
 		val++;
-		if(ippp_table[minor]->maxcid != val) {
+		if(is->maxcid != val) {
 #ifdef CONFIG_ISDN_PPP_VJ
 			struct slcompress *sltmp;
 #endif
-			if(ippp_table[minor]->debug & 0x1)
+			if(is->debug & 0x1)
 				printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n",val);
-			ippp_table[minor]->maxcid = val;
+			is->maxcid = val;
 #ifdef CONFIG_ISDN_PPP_VJ
 			sltmp = slhc_init(16,val);
 			if(!sltmp) {
 				printk(KERN_ERR "ippp, can't realloc slhc struct\n");
 				return -ENOMEM;
 			}	
-			if(ippp_table[minor]->slcomp)
-				slhc_free(ippp_table[minor]->slcomp);
-			ippp_table[minor]->slcomp = sltmp;
+			if(is->slcomp)
+				slhc_free(is->slcomp);
+			is->slcomp = sltmp;
 #endif
 		}
 		break;
 	case PPPIOCGDEBUG:
-		if ((r = set_arg((void *) arg, ippp_table[minor]->debug)))
+		if ((r = set_arg((void *) arg, is->debug)))
 			return r;
 		break;
 	case PPPIOCSDEBUG:
 		if ((r = get_arg((void *) arg, &val)))
 			return r;
-		ippp_table[minor]->debug = val;
+		is->debug = val;
 		break;
 	default:
 		break;
@@ -440,39 +479,42 @@
 	return 0;
 }
 
-int isdn_ppp_select(int minor, struct file *file, int type, select_table * st)
+int isdn_ppp_select(int min, struct file *file, int type, select_table * st)
 {
 	struct ippp_buf_queue *bf, *bl;
 	unsigned long flags;
+	struct ippp_struct *is;
 
-	if(ippp_table[minor]->debug & 0x2)
-		printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n",minor,type);
+	is = file->private_data;
 
-	if (!(ippp_table[minor]->state & IPPP_OPEN))
+	if(is->debug & 0x2)
+		printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n",min,type);
+
+	if (!(is->state & IPPP_OPEN))
 		return -EINVAL;
 
 	switch (type) {
 	case SEL_IN:
 		save_flags(flags);
 		cli();
-		bl = ippp_table[minor]->last;
-		bf = ippp_table[minor]->first;
+		bl = is->last;
+		bf = is->first;
 		/* 
 		 * if IPPP_NOBLOCK is set we return even if we have nothing to read 
 		 */
-		if (bf->next == bl && !(ippp_table[minor]->state & IPPP_NOBLOCK)) {
-			select_wait(&ippp_table[minor]->wq, st);
+		if (bf->next == bl && !(is->state & IPPP_NOBLOCK)) {
+			select_wait(&is->wq, st);
 			restore_flags(flags);
 			return 0;
 		}
-		ippp_table[minor]->state &= ~IPPP_NOBLOCK;
+		is->state &= ~IPPP_NOBLOCK;
 		restore_flags(flags);
 		return 1;
 	case SEL_OUT:
                 /* we're always ready to send .. */
 		return 1;
 	case SEL_EX:
-		select_wait(&ippp_table[minor]->wq1, st);
+		select_wait(&is->wq1, st);
 		return 0;
 	}
 	return 1;
@@ -482,17 +524,20 @@
  *  fill up isdn_ppp_read() queue ..
  */
 
-static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int minor)
+static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot)
 {
 	struct ippp_buf_queue *bf, *bl;
 	unsigned long flags;
 	unsigned char *nbuf;
+	struct ippp_struct *is;
 
-	if (minor < 0 || minor >= ISDN_MAX_CHANNELS) {
-		printk(KERN_WARNING "ippp: illegal minor.\n");
+	if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+		printk(KERN_WARNING "ippp: illegal slot.\n");
 		return 0;
 	}
-	if (!(ippp_table[minor]->state & IPPP_CONNECT)) {
+	is = ippp_table[slot];
+
+	if (!(is->state & IPPP_CONNECT)) {
 		printk(KERN_DEBUG "ippp: device not activated.\n");
 		return 0;
 	}
@@ -511,23 +556,23 @@
 	save_flags(flags);
 	cli();
 
-	bf = ippp_table[minor]->first;
-	bl = ippp_table[minor]->last;
+	bf = is->first;
+	bl = is->last;
 
 	if (bf == bl) {
 		printk(KERN_WARNING "ippp: Queue is full; discarding first buffer\n");
 		bf = bf->next;
 		kfree(bf->buf);
-		ippp_table[minor]->first = bf;
+		is->first = bf;
 	}
 	bl->buf = (char *) nbuf;
 	bl->len = len+4;
 
-	ippp_table[minor]->last = bl->next;
+	is->last = bl->next;
 	restore_flags(flags);
 
-	if (ippp_table[minor]->wq)
-		wake_up_interruptible(&ippp_table[minor]->wq);
+	if (is->wq)
+		wake_up_interruptible(&is->wq);
 
 	return len;
 }
@@ -537,14 +582,16 @@
  *           reports, that there is data
  */
 
-int isdn_ppp_read(int minor, struct file *file, char *buf, int count)
+int isdn_ppp_read(int min, struct file *file, char *buf, int count)
 {
-	struct ippp_struct *c = ippp_table[minor];
+	struct ippp_struct *is;
 	struct ippp_buf_queue *b;
 	int r;
 	unsigned long flags;
 
-	if (!(ippp_table[minor]->state & IPPP_OPEN))
+	is = file->private_data;
+
+	if (!(is->state & IPPP_OPEN))
 		return 0;
 
 	if ((r = verify_area(VERIFY_WRITE, (void *) buf, count)))
@@ -553,7 +600,7 @@
 	save_flags(flags);
 	cli();
 
-	b = c->first->next;
+	b = is->first->next;
 	if (!b->buf) {
 		restore_flags(flags);
 		return -EAGAIN;
@@ -563,7 +610,7 @@
 	memcpy_tofs(buf, b->buf, count);
 	kfree(b->buf);
 	b->buf = NULL;
-	c->first = b;
+	is->first = b;
 	restore_flags(flags);
 
 	return count;
@@ -573,14 +620,17 @@
  * ipppd wanna write a packet to the card .. non-blocking
  */
  
-int isdn_ppp_write(int minor, struct file *file,  const char *buf, int count)
+int isdn_ppp_write(int min, struct file *file,  const char *buf, int count)
 {
 	isdn_net_local *lp;
+	struct ippp_struct *is;
+
+	is = file->private_data;
 
-	if (!(ippp_table[minor]->state & IPPP_CONNECT))
+	if (!(is->state & IPPP_CONNECT))
 		return 0;
 
-	lp = ippp_table[minor]->lp;
+	lp = is->lp;
 
 	/* -> push it directly to the lowlevel interface */
 
@@ -659,7 +709,10 @@
 
 void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
 {
-	if(ippp_table[lp->ppp_minor]->debug & 0x4)
+	struct ippp_struct *is;
+	is = ippp_table[lp->ppp_slot];
+
+	if(is->debug & 0x4)
 		printk(KERN_DEBUG "recv skb, len: %ld\n",skb->len);
 
 	if(net_dev->local.master) {
@@ -669,14 +722,14 @@
 
 	if(skb->data[0] == 0xff && skb->data[1] == 0x03)
 		skb_pull(skb,2);
-	else if (ippp_table[lp->ppp_minor]->pppcfg & SC_REJ_COMP_AC) {
+	else if (is->pppcfg & SC_REJ_COMP_AC) {
 		skb->free = 1;
 		dev_kfree_skb(skb,0 /* FREE_READ */ );
 		return;		/* discard it silently */
 	}
 
 #ifdef CONFIG_ISDN_MPP
-	if (!(ippp_table[lp->ppp_minor]->mpppcfg & SC_REJ_MP_PROT)) {
+	if (!(is->mpppcfg & SC_REJ_MP_PROT)) {
 		int proto;
 		int sqno_end;
 		if (skb->data[0] & 0x1) {
@@ -690,11 +743,11 @@
 			isdn_net_local *lpq;
 			int sqno, min_sqno, tseq;
 			u_char BEbyte = skb->data[0];
-			if(ippp_table[lp->ppp_minor]->debug & 0x8)
-	 			printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_minor, proto ,
+			if(is->debug & 0x8)
+	 			printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_slot, proto ,
 					(int) skb->len, (int) skb->data[0], (int) skb->data[1], (int) skb->data[2], 
 					(int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
-			if (!(ippp_table[lp->ppp_minor]->mpppcfg & SC_IN_SHORT_SEQ)) {
+			if (!(is->mpppcfg & SC_IN_SHORT_SEQ)) {
 				sqno = ((int) skb->data[1] << 16) + ((int) skb->data[2] << 8) + (int) skb->data[3];
 				skb_pull(skb,4);
 			} else {
@@ -702,26 +755,26 @@
 				skb_pull(skb,2);
 			}
 
-			if ((tseq = ippp_table[lp->ppp_minor]->last_link_seqno) >= sqno) {
-				int range = ippp_table[lp->ppp_minor]->range;
+			if ((tseq = is->last_link_seqno) >= sqno) {
+				int range = is->range;
 				if (tseq + 1024 < range + sqno) /* redundancy check .. not MP conform */
 					printk(KERN_WARNING "isdn_ppp_receive, MP, detected overflow with sqno: %d, last: %d !!!\n", sqno, tseq);
 				else {
 					sqno += range;
-					ippp_table[lp->ppp_minor]->last_link_seqno = sqno;
+					is->last_link_seqno = sqno;
 				}
 			} else
-				ippp_table[lp->ppp_minor]->last_link_seqno = sqno;
+				is->last_link_seqno = sqno;
 
 			for (min_sqno = 0, lpq = net_dev->queue;;) {
-				if (ippp_table[lpq->ppp_minor]->last_link_seqno > min_sqno)
-					min_sqno = ippp_table[lpq->ppp_minor]->last_link_seqno;
+				if (ippp_table[lpq->ppp_slot]->last_link_seqno > min_sqno)
+					min_sqno = ippp_table[lpq->ppp_slot]->last_link_seqno;
 				lpq = lpq->next;
 				if (lpq == net_dev->queue)
 					break;
 			}
-			if (min_sqno >= ippp_table[lpq->ppp_minor]->range) {	/* OK, every link overflowed */
-				int mask = ippp_table[lpq->ppp_minor]->range - 1;	/* range is a power of 2 */
+			if (min_sqno >= ippp_table[lpq->ppp_slot]->range) {	/* OK, every link overflowed */
+				int mask = ippp_table[lpq->ppp_slot]->range - 1;	/* range is a power of 2 */
 				isdn_ppp_cleanup_queue(net_dev, min_sqno);
 				isdn_ppp_mask_queue(net_dev, mask);
 				net_dev->ib.next_num &= mask;
@@ -734,14 +787,14 @@
 				}
 				min_sqno &= mask;
 				for (lpq = net_dev->queue;;) {
-					ippp_table[lpq->ppp_minor]->last_link_seqno &= mask;
+					ippp_table[lpq->ppp_slot]->last_link_seqno &= mask;
 					lpq = lpq->next;
 					if (lpq == net_dev->queue)
 						break;
 				}
 			}
 			if ((BEbyte & (MP_BEGIN_FRAG | MP_END_FRAG)) != (MP_BEGIN_FRAG | MP_END_FRAG)) {
-				printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_minor);
+				printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_slot);
 				if ((sqno_end = isdn_ppp_fill_mpqueue(net_dev, &skb , BEbyte, &sqno, min_sqno)) < 0)
 					return;		/* no packet complete */
 			} else
@@ -820,9 +873,14 @@
 }
 
 
+/*
+ * push frame to higher layers
+ * note: net_dev has to be master net_dev
+ */
 static void isdn_ppp_push_higher(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb,int proto)
 {
 	struct device *dev = &net_dev->dev;
+	struct ippp_struct *is = ippp_table[lp->ppp_slot];
 
 	if (proto < 0) {	/* MP, oder normales Paket bei REJ_MP, MP Pakete gehen bei REJ zum pppd */
 		if (skb->data[0] & 0x01) {	/* is it odd? */
@@ -834,12 +892,12 @@
 		}
 	}
 
-	if(ippp_table[lp->ppp_minor]->debug & 0x10)
+	if(is->debug & 0x10)
 		printk(KERN_DEBUG "push, skb %ld %04x\n",skb->len,proto);
 
 	switch (proto) {
 	case PPP_IPX: /* untested */
-		if(ippp_table[lp->ppp_minor]->debug & 0x20)
+		if(is->debug & 0x20)
 			printk(KERN_DEBUG "isdn_ppp: _IPX\n");
 		skb->dev = dev;
 		skb->mac.raw = skb->data;
@@ -847,9 +905,9 @@
 		break;
 #ifdef CONFIG_ISDN_PPP_VJ
 	case PPP_VJC_UNCOMP:
-		if(ippp_table[lp->ppp_minor]->debug & 0x20)
+		if(is->debug & 0x20)
 			printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
-		if(slhc_remember(ippp_table[net_dev->local.ppp_minor]->slcomp, skb->data, skb->len) <= 0) {
+		if(slhc_remember(ippp_table[net_dev->local.ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
 			printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
 			net_dev->local.stats.rx_dropped++;
 			skb->free = 1;
@@ -858,14 +916,14 @@
 		}
 #endif
 	case PPP_IP:
-		if(ippp_table[lp->ppp_minor]->debug & 0x20)
+		if(is->debug & 0x20)
 			printk(KERN_DEBUG "isdn_ppp: IP\n");
 		skb->dev = dev;
 		skb->mac.raw = skb->data;
 		skb->protocol = htons(ETH_P_IP);
 		break;
 	case PPP_VJC_COMP:
-                if(ippp_table[lp->ppp_minor]->debug & 0x20)
+                if(is->debug & 0x20)
                         printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n");
 #ifdef CONFIG_ISDN_PPP_VJ
 		{
@@ -885,7 +943,7 @@
 			skb_put(skb,skb_old->len + 40);
 			memcpy(skb->data, skb_old->data, skb_old->len);
 			skb->mac.raw = skb->data;
-			pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_minor]->slcomp,
+			pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_slot]->slcomp,
 						  skb->data, skb_old->len);
 			dev_kfree_skb(skb_old,0 /* FREE_READ */ );
 			if(pkt_len < 0) {
@@ -906,7 +964,7 @@
 #endif
 		break;
 	default:
-		isdn_ppp_fill_rq(skb->data, skb->len,proto, lp->ppp_minor);	/* push data to pppd device */
+		isdn_ppp_fill_rq(skb->data, skb->len,proto, lp->ppp_slot);	/* push data to pppd device */
 		skb->free = 1;
 		dev_kfree_skb(skb,0 /* FREE_READ */ );
 		return;
@@ -928,6 +986,19 @@
  * skb isn't allowed!! 
  */
 
+static void isdn_ppp_skb_destructor(struct sk_buff *skb)
+{
+	char outstr[80],*outpnt=outstr;
+	int i;
+
+	*outpnt = 0;
+	for(i=0;i<24 && i<skb->len;i++) {
+		sprintf(outpnt,"%02x ",skb->data[i]);
+		outpnt += 3;
+	}
+	printk(KERN_DEBUG "ippp_dstrct: %s\n",outstr);
+}
+
 int isdn_ppp_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct device *mdev = ((isdn_net_local *) (dev->priv) )->master;	/* get master (for redundancy) */
@@ -941,13 +1012,18 @@
 	else
 		mlp = (isdn_net_local *) (dev->priv);
 	nd = mlp->netdev;	/* get master lp */
-	ipts = ippp_table[mlp->ppp_minor];
+	ipts = ippp_table[mlp->ppp_slot];
 
 	if (!(ipts->pppcfg & SC_ENABLE_IP)) {    /* PPP connected ? */
-		printk(KERN_INFO "%s: IP frame delayed.\n",dev->name);
-		return 1;
+		if(ipts->debug & 0x1) {
+			printk(KERN_INFO "%s: IP frame delayed.\n",dev->name);
+			skb->destructor = isdn_ppp_skb_destructor;
+		}
+ 		return 1;
         }
 
+	skb->destructor = NULL;
+
 	lp = nd->queue;		/* get lp on top of queue */
 	if(lp->sav_skb) {	/* find a non-busy device */
 		isdn_net_local *nlp = lp->next;
@@ -958,35 +1034,21 @@
 		}
 		lp = nlp;
 	}
-	ipt = ippp_table[lp->ppp_minor];
+	ipt = ippp_table[lp->ppp_slot];
 
         lp->huptimer = 0;
- 
-        /* If packet is to be resent, it has already been processed and
-         * therefore its first bytes are already initialized. In this case
-         * send it immediately ...
-         */
-        if (*((u32 *)skb->data) != 0) {
-	  printk(KERN_ERR "%s: Whoops .. packet resend should no longer happen!\n",dev->name);
-          return (isdn_net_send_skb(dev , lp , skb));
-        }
 
-        /* ... else packet needs processing. */
+	/*
+	 * after this line .. requeueing in the device queue is no longer allowed!!! 
+	 */
 
-	if(ippp_table[lp->ppp_minor]->debug & 0x4)
+	if(ipt->debug & 0x4)
 		printk(KERN_DEBUG  "xmit skb, len %ld\n",skb->len);
 
 #ifdef CONFIG_ISDN_PPP_VJ
-	if (ipts->pppcfg & SC_COMP_TCP) {	/* ipts here? probably yes .. but check again */
+	if (ipts->pppcfg & SC_COMP_TCP) {	/* ipts here? probably yes .. but this check again */
 		struct sk_buff *new_skb;
-		int len = 4;
-#ifdef CONFIG_ISDN_MPP
-		if (ipt->mpppcfg & SC_MP_PROT) /* sigh */ 	/* ipt or ipts ?? */
-			if (ipt->mpppcfg & SC_OUT_SHORT_SEQ)
-				len += 3;
-			else
-				len += 5;
-#endif
+
 		new_skb = dev_alloc_skb(skb->len);
 		if(new_skb) {
 			u_char *buf;
@@ -995,8 +1057,6 @@
 			new_skb->dev = skb->dev;
 			new_skb->free = 1;
 			skb_put(new_skb,skb->len);
-			skb_pull(skb,len);	/* pull PPP header */
-			skb_pull(new_skb,len);	/* pull PPP header */
 			buf = skb->data;
 
 			pktlen = slhc_compress(ipts->slcomp, skb->data, skb->len, new_skb->data,
@@ -1021,12 +1081,11 @@
 					proto = PPP_VJC_UNCOMP;
 				skb->data[0] = (skb->data[0] & 0x0f) | 0x40;
 			}
-			skb_push(skb,len);
 		}
 	}
 #endif
 
-        if(ippp_table[lp->ppp_minor]->debug & 0x24)
+        if(ipt->debug & 0x24)
  		printk(KERN_DEBUG  "xmit2 skb, len %ld, proto %04x\n",skb->len,proto);
 
 #ifdef CONFIG_ISDN_MPP
@@ -1036,22 +1095,23 @@
 		ipts->mp_seqno++;
 		nd->queue = nd->queue->next;
 		if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) {
-			/* skb_push(skb, 3); Done in isdn_net_header() */
+			skb_push(skb, 3);
 			mp_seqno &= 0xfff;
-			skb->data[4] = MP_BEGIN_FRAG | MP_END_FRAG | (mp_seqno >> 8);	/* (B)egin & (E)ndbit .. */
-			skb->data[5] = mp_seqno & 0xff;
-			skb->data[6] = proto;	/* PID compression */
+			skb->data[0] = MP_BEGIN_FRAG | MP_END_FRAG | (mp_seqno >> 8);	/* (B)egin & (E)ndbit .. */
+			skb->data[1] = mp_seqno & 0xff;
+			skb->data[2] = proto;	/* PID compression */
 		} else {
-			/* skb_push(skb, 5); Done in isdn_net_header () */
-			skb->data[4] = MP_BEGIN_FRAG | MP_END_FRAG;	/* (B)egin & (E)ndbit .. */
-			skb->data[5] = (mp_seqno >> 16) & 0xff;	/* sequence number: 24bit */
-			skb->data[6] = (mp_seqno >> 8) & 0xff;
-			skb->data[7] = (mp_seqno >> 0) & 0xff;
-			skb->data[8] = proto;	/* PID compression */
+			skb_push(skb, 5);
+			skb->data[0] = MP_BEGIN_FRAG | MP_END_FRAG;	/* (B)egin & (E)ndbit .. */
+			skb->data[1] = (mp_seqno >> 16) & 0xff;	/* sequence number: 24bit */
+			skb->data[2] = (mp_seqno >> 8) & 0xff;
+			skb->data[3] = (mp_seqno >> 0) & 0xff;
+			skb->data[4] = proto;	/* PID compression */
 		}
 		proto = PPP_MP; /* MP Protocol, 0x003d */
 	}
 #endif
+	skb_push(skb,4);
 	skb->data[0] = 0xff;        /* All Stations */
 	skb->data[1] = 0x03;        /* Unnumbered information */
 	skb->data[2] = proto >> 8;
@@ -1100,7 +1160,7 @@
 
 #ifdef CONFIG_ISDN_MPP
 
-static int isdn_ppp_bundle(int minor, int unit)
+static int isdn_ppp_bundle(struct ippp_struct *is, int unit)
 {
 	char ifn[IFNAMSIZ + 1];
 	long flags;
@@ -1117,7 +1177,7 @@
 	save_flags(flags);
 	cli();
 
-	nlp = ippp_table[minor]->lp;
+	nlp = is->lp;
 
 	lp = p->queue;
 	p->ib.bundled = 1;
@@ -1127,17 +1187,17 @@
 	nlp->next = lp;
 	p->queue = nlp;
 
-	ippp_table[nlp->ppp_minor]->unit = ippp_table[lp->ppp_minor]->unit;
+	ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit;
 /* maybe also SC_CCP stuff */
-	ippp_table[nlp->ppp_minor]->pppcfg |= ippp_table[lp->ppp_minor]->pppcfg &
+	ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg &
 	    (SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
 
-	ippp_table[nlp->ppp_minor]->mpppcfg |= ippp_table[lp->ppp_minor]->mpppcfg &
+	ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg &
 	    (SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
 #if 0
-	if (ippp_table[nlp->ppp_minor]->mpppcfg != ippp_table[lp->ppp_minor]->mpppcfg) {
+	if (ippp_table[nlp->ppp_slot]->mpppcfg != ippp_table[lp->ppp_slot]->mpppcfg) {
 		printk(KERN_WARNING "isdn_ppp_bundle: different MP options %04x and %04x\n",
-		       ippp_table[nlp->ppp_minor]->mpppcfg, ippp_table[lp->ppp_minor]->mpppcfg);
+		       ippp_table[nlp->ppp_slot]->mpppcfg, ippp_table[lp->ppp_slot]->mpppcfg);
 	}
 #endif
 
@@ -1325,7 +1385,7 @@
 #ifdef CONFIG_ISDN_PPP_VJ
 	/* did we free a stale frame ? */
 	if(toss)
-		slhc_toss(ippp_table[dev->local.ppp_minor]->slcomp);
+		slhc_toss(ippp_table[dev->local.ppp_slot]->slcomp);
 #endif
 }
 
@@ -1355,7 +1415,7 @@
 #ifdef CONFIG_ISDN_PPP_VJ
 				/* did we step over a missing frame ? */
 				if(q->sqno_start != net_dev->ib.next_num)
-					slhc_toss(ippp_table[lp->ppp_minor]->slcomp);
+					slhc_toss(ippp_table[lp->ppp_slot]->slcomp);
 #endif
 
 				ql = net_dev->ib.sq;
@@ -1381,7 +1441,7 @@
  * network device ioctl handlers
  */
 
-static int isdn_ppp_dev_ioctl_stats(int minor,struct ifreq *ifr,struct device *dev)
+static int isdn_ppp_dev_ioctl_stats(int slot,struct ifreq *ifr,struct device *dev)
 {
 	struct ppp_stats *res, t;
 	isdn_net_local *lp = (isdn_net_local *) dev->priv;
@@ -1402,8 +1462,8 @@
 		t.p.ppp_opackets = lp->stats.tx_packets;
 		t.p.ppp_oerrors = lp->stats.tx_errors;
 #ifdef CONFIG_ISDN_PPP_VJ
-		if(minor >= 0 && ippp_table[minor]->slcomp) {
-			struct slcompress *slcomp = ippp_table[minor]->slcomp;
+		if(slot >= 0 && ippp_table[slot]->slcomp) {
+			struct slcompress *slcomp = ippp_table[slot]->slcomp;
 			t.vj.vjs_packets  = slcomp->sls_o_compressed+slcomp->sls_o_uncompressed;
 			t.vj.vjs_compressed = slcomp->sls_o_compressed;
 			t.vj.vjs_searches = slcomp->sls_o_searches;
@@ -1428,7 +1488,7 @@
 	isdn_net_local *lp = (isdn_net_local *) dev->priv;
 
 #if 0
-	printk(KERN_DEBUG "ippp, dev_ioctl: cmd %#08x , %d \n",cmd,lp->ppp_minor);
+	printk(KERN_DEBUG "ippp, dev_ioctl: cmd %#08x , %d \n",cmd,lp->ppp_slot);
 #endif
 
 	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
@@ -1443,7 +1503,7 @@
 				memcpy_tofs(r, PPP_VERSION, len);
 			break;
 		case SIOCGPPPSTATS:
-			error = isdn_ppp_dev_ioctl_stats (lp->ppp_minor, ifr, dev);
+			error = isdn_ppp_dev_ioctl_stats (lp->ppp_slot, ifr, dev);
 			break;
 		default:
 			error = -EINVAL;
@@ -1452,24 +1512,26 @@
 	return error;
 }
 
-static int isdn_ppp_if_get_unit(char **namebuf)
+static int isdn_ppp_if_get_unit(char *name)
 {
-	char *name = *namebuf;
 	int len, i, unit = 0, deci;
 
 	len = strlen(name);
+
+	if(strncmp("ippp",name,4) || len > 8)
+		return -1;
+
 	for (i = 0, deci = 1; i < len; i++, deci *= 10) {
-		if (name[len - 1 - i] >= '0' && name[len - 1 - i] <= '9')
-			unit += (name[len - 1 - i] - '0') * deci;
+		char a = name[len-i-1];
+		if (a >= '0' && a <= '9')
+			unit += (a - '0') * deci;
 		else
 			break;
 	}
-	if (!i)
+	if (!i || len-i != 4)
 		unit = -1;
 
-	*namebuf = name + len - 1 - i;
 	return unit;
-
 }
 
 
diff -urN linux-2.0.15/drivers/isdn/isdn_ppp.h linux-2.0.18/drivers/isdn/isdn_ppp.h
--- linux-2.0.15/drivers/isdn/isdn_ppp.h	Mon Aug 26 22:10:19 1996
+++ linux-2.0.18/drivers/isdn/isdn_ppp.h	Thu Sep  5 17:46:57 1996
@@ -51,5 +51,6 @@
 extern int  isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long);
 extern void isdn_ppp_release(int, struct file *);
 extern int  isdn_ppp_dial_slave(char *);
+extern void isdn_ppp_wakeup_daemon(isdn_net_local *);
 
 extern struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
diff -urN linux-2.0.15/drivers/isdn/pcbit/layer2.h linux-2.0.18/drivers/isdn/pcbit/layer2.h
--- linux-2.0.15/drivers/isdn/pcbit/layer2.h	Mon Aug 26 22:10:25 1996
+++ linux-2.0.18/drivers/isdn/pcbit/layer2.h	Sat Sep  7 09:53:37 1996
@@ -14,7 +14,7 @@
 #ifndef LAYER2_H
 #define LAYER2_H
 
-#include <bytesex.h>
+#include <asm/byteorder.h>
 
 #define BANK1 0x0000U /* PC -> Board */
 #define BANK2 0x01ffU /* Board -> PC */
@@ -85,7 +85,7 @@
 */
 
 struct msg_fmt {
-#if __BYTE_ORDER == 1234              /* Little Endian */
+#ifdef __LITTLE_ENDIAN              /* Little Endian */
   u_char scmd;
   u_char cmd;
   u_char proc;
diff -urN linux-2.0.15/drivers/isdn/teles/card.c linux-2.0.18/drivers/isdn/teles/card.c
--- linux-2.0.15/drivers/isdn/teles/card.c	Mon Aug 26 22:10:28 1996
+++ linux-2.0.18/drivers/isdn/teles/card.c	Thu Sep  5 17:47:04 1996
@@ -1,4 +1,4 @@
-/* $Id: card.c,v 1.12 1996/06/24 17:16:52 fritz Exp $
+/* $Id: card.c,v 1.13 1996/07/18 11:21:24 jdenoud Exp $
  *
  * card.c     low level stuff for the Teles S0 isdn card
  * 
@@ -7,6 +7,9 @@
  * Beat Doebeli         log all D channel traffic
  * 
  * $Log: card.c,v $
+ * Revision 1.13  1996/07/18 11:21:24  jdenoud
+ * Use small buffers for incoming audio data
+ *
  * Revision 1.12  1996/06/24 17:16:52  fritz
  * Added check for misconfigured membase.
  *
@@ -424,7 +427,7 @@
 {
 	byte             r;
 	struct HscxState *hsp = sp->hs + hscx;
-	int              count;
+	int              count, err;
 
 	if (!hsp->init)
 		return;
@@ -471,9 +474,15 @@
 	}
       afterRME:
 	if (val & 0x40) {	/* RPF */
-		if (!hsp->rcvibh)
-			if (BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
-				       GFP_ATOMIC, (void *) 1, 2)) {
+		if (!hsp->rcvibh) {
+			if (hsp->mode == 1)
+				err=BufPoolGet(&hsp->rcvibh, &hsp->smallpool,
+					GFP_ATOMIC, (void *)1, 2);
+			else
+				err=BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+					GFP_ATOMIC, (void *)1, 2);
+			
+			if (err) {
 				printk(KERN_WARNING
                                        "HSCX RPF out of buffers at %ld\n",
                                        jiffies);
@@ -482,7 +491,8 @@
 				goto afterRPF;
 			} else
 				hsp->rcvptr = 0;
-
+		}
+		
 		hscx_empty_fifo(hsp, 32);
                 if (hsp->mode == 1) {
                         /* receive audio data */
diff -urN linux-2.0.15/drivers/net/ac3200.c linux-2.0.18/drivers/net/ac3200.c
--- linux-2.0.15/drivers/net/ac3200.c	Mon Aug 26 22:10:47 1996
+++ linux-2.0.18/drivers/net/ac3200.c	Sat Sep  7 09:53:44 1996
@@ -137,7 +137,7 @@
 	for (i = 0; i < 4; i++)
 		if (inl(ioaddr + AC_ID_PORT) != AC_EISA_ID) {
 			printk("EISA ID mismatch, %8x vs %8x.\n",
-				   inl(ioaddr + AC_EISA_ID), AC_EISA_ID); 
+				   inl(ioaddr + AC_ID_PORT), AC_EISA_ID); 
 			return ENODEV;
 		}
 
diff -urN linux-2.0.15/drivers/scsi/ChangeLog.ncr53c8xx linux-2.0.18/drivers/scsi/ChangeLog.ncr53c8xx
--- linux-2.0.15/drivers/scsi/ChangeLog.ncr53c8xx	Mon Aug 26 22:11:41 1996
+++ linux-2.0.18/drivers/scsi/ChangeLog.ncr53c8xx	Thu Sep  5 17:47:15 1996
@@ -1,3 +1,10 @@
+Fri Aug 30  10:00 1996 Gerard Roudier (groudier@club-internet.fr)
+	* ncr53c8xx.c - Version 1.12c
+	Incorporate the changes of FreeBSD/ncr.c revision 1.76.
+	The changes add support for the 53c860 and 53c875, 
+	but without taking advantage of the new features.
+	Those chips are used exactly as the old 53c810.
+
 Sun Jul 21  00:00 1996 Gerard Roudier (groudier@club-internet.fr)
 	* ncr53c8xx.c, README.ncr53c8xx
 	Add the ncr53c8xx_select_queue_depths() function.
diff -urN linux-2.0.15/drivers/scsi/aha152x.c linux-2.0.18/drivers/scsi/aha152x.c
--- linux-2.0.15/drivers/scsi/aha152x.c	Mon Aug 26 22:11:51 1996
+++ linux-2.0.18/drivers/scsi/aha152x.c	Fri Aug 30 19:18:53 1996
@@ -22,6 +22,7 @@
  *
  * $Id: aha152x.c,v 1.17 1996/08/17 16:05:14 fischer Exp fischer $
  *
+ * $Log: aha152x.c,v $
  * Revision 1.17  1996/08/17 16:05:14  fischer
  * - biosparam improved
  * - interrupt verification
diff -urN linux-2.0.15/drivers/scsi/cyber_esp.c linux-2.0.18/drivers/scsi/cyber_esp.c
--- linux-2.0.15/drivers/scsi/cyber_esp.c	Thu Jan  1 00:00:00 1970
+++ linux-2.0.18/drivers/scsi/cyber_esp.c	Sun Sep  8 14:25:57 1996
@@ -0,0 +1,331 @@
+/* cyber_esp.c: Driver for CyberStorm SCSI
+ *
+ * Copyright (C) 1996 Jesper Skov (jskov@iesd.auc.dk)
+ *
+ * The CyberStorm SCSI driver is based on David S. Miller's ESP driver
+ * for the Sparc computers. 
+ * 
+ * This work was made possible by Phase5 who willingly (and most generously)
+ * supported me with hardware and all the information I needed.
+ */
+
+/* TODO:
+ *
+ * 1) Figure out how to make a cleaner merge with the sparc driver with regard
+ *    to the caches and the Sparc MMU mapping.
+ * 2) General clean up of the code. The eps.c code should be renamed to
+ *    NCR53C9X.c and be updated accordingly (i.e. should be more generic).
+ * 3) Make as few routines required outside the generic driver. A lot of the
+ *    routines in this file used to be inline!
+ * 4) Is esp_bytes_sent() correct?
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/blk.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "esp.h"
+#include "cyber_esp.h"
+
+#include <asm/zorro.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+
+#include <asm/pgtable.h>
+
+int  cyber_dma_bytes_sent(struct Sparc_ESP *, int);
+int  cyber_dma_can_transfer(struct Sparc_ESP *, Scsi_Cmnd *);
+void cyber_dma_dump_state(struct Sparc_ESP *);
+void cyber_dma_init_read(struct Sparc_ESP *, char *, int);
+void cyber_dma_init_write(struct Sparc_ESP *, char *, int);
+void cyber_dma_ints_off(struct Sparc_ESP *esp);
+void cyber_dma_ints_on(struct Sparc_ESP *esp);
+int  cyber_dma_irq_p(struct Sparc_ESP *);
+void cyber_dma_led_off(struct Sparc_ESP *);
+void cyber_dma_led_on(struct Sparc_ESP *);
+void cyber_dma_poll(struct Sparc_ESP *, unsigned char *);
+int  cyber_dma_ports_p(struct Sparc_ESP *esp);
+void cyber_dma_reset(struct Sparc_ESP *);
+void cyber_dma_setup(struct Sparc_ESP *, char *, int, int);
+
+unsigned char ctrl_data = 0;	/* Keep backup of the stuff written
+				 * to ctrl_reg. Always write a copy
+				 * to this register when writing to
+				 * the hardware register!
+				 */
+
+volatile unsigned char esp_dma_block[16+16];
+				/* This is where all commands are put
+				 * before they are trasfered to the ESP chip
+				 * via DMA. This block should be 16 bytes
+				 * aligned (so cache flush can be done
+				 * by pushing a single line in the cache).
+				 * - 
+				 * This is now accomplished by adding another
+				 * 16 bytes to the array, and aligning the
+				 * esp->command pointer correctly in
+				 * esp_detect().
+				 */
+
+/***************************************************************** Detection */
+int cyber_esp_detect(Scsi_Host_Template *tpnt)
+{
+	struct Sparc_ESP *esp;
+	struct ConfigDev *esp_dev;
+	int key;
+	unsigned long address;
+
+	if ((key = zorro_find(MANUF_PHASE5, PROD_CYBERSTORM_SCSI, 0, 0))){
+		esp_dev = zorro_get_board(key);
+		esp = esp_allocate(tpnt, 40000000, (void *) esp_dev);
+
+		/* Required functions */
+		esp->dma_bytes_sent = &cyber_dma_bytes_sent;
+		esp->dma_can_transfer = &cyber_dma_can_transfer;
+		esp->dma_dump_state = &cyber_dma_dump_state;
+		esp->dma_init_read = &cyber_dma_init_read;
+		esp->dma_init_write = &cyber_dma_init_write;
+		esp->dma_ints_off = &cyber_dma_ints_off;
+		esp->dma_ints_on = &cyber_dma_ints_on;
+		esp->dma_irq_p = &cyber_dma_irq_p;
+		esp->dma_ports_p = &cyber_dma_ports_p;
+		esp->dma_setup = &cyber_dma_setup;
+
+		/* Optional functions */
+		esp->dma_barrier = 0;
+		esp->dma_drain = 0;
+		esp->dma_invalidate = 0;
+		esp->dma_irq_entry = 0;
+		esp->dma_irq_exit = 0;
+		esp->dma_led_on = &cyber_dma_led_on;
+		esp->dma_led_off = &cyber_dma_led_off;
+		esp->dma_poll = &cyber_dma_poll;
+		esp->dma_reset = 0;
+
+		/* The DMA registers on the CyberStorm are mapped
+		 * relative to the device (i.e. in the same Zorro
+		 * I/O block).
+		 */
+		address = (unsigned long)ZTWO_VADDR(esp_dev->cd_BoardAddr);
+		esp->dregs = (void *)(address + CYBER_DMA_ADDR);
+
+		/* ESP register base */
+		esp->eregs = (struct ESP_regs *)(address + CYBER_ESP_ADDR);
+		
+		/* Make the DMA command buffer 16 bytes aligned so
+		 * only one cache line has to be cleared.
+		 */
+		esp->esp_command = (volatile unsigned char*)
+			(((unsigned long) esp_dma_block+0x0f) & ~0x0f);
+
+		esp->irq = IRQ_AMIGA_PORTS;
+		request_irq(IRQ_AMIGA_PORTS, esp_intr, 0, "CyberStorm SCSI", esp_intr);
+		/* Figure out our scsi ID on the bus */
+		/* The DMA cond flag contains a hardcoded jumper bit
+		 * which can be used to select host number 6 or 7.
+		 * However, even though it may change, we use a hardcoded
+		 * value of 7.
+		 */
+		esp->scsi_id = 7;
+		esp->scsi_id_mask = (1 << esp->scsi_id);
+		
+		/* Check for differential SCSI-bus */
+		/* What is this stuff? */
+		esp->diff = 0;
+
+		esp_initialize(esp);
+
+		zorro_config_board(key, 0);
+
+		printk("\nESP: Total of %d ESP hosts found, %d actually in use.\n", nesps,esps_in_use);
+		esps_running = esps_in_use;
+		return esps_in_use;
+	}
+	printk("No CyberStorm SCSI controller found!\n");
+	return 0;
+}
+
+/************************************************************* DMA Functions */
+int cyber_dma_bytes_sent(struct Sparc_ESP *esp, int fifo_count)
+{
+	/* Since the CyberStorm DMA is fully dedicated to the ESP chip,
+	 * the number of bytes sent (to the ESP chip) equals the number
+	 * of bytes in the FIFO - there is no buffering in the DMA controller.
+	 * XXXX Do I read this right? It is from host to ESP, right?
+	 */
+	return fifo_count;
+}
+
+int cyber_dma_can_transfer(struct Sparc_ESP *esp, Scsi_Cmnd *sp)
+{
+	/* I don't think there's any limit on the CyberDMA. So we use what
+	 * the ESP chip can handle (24 bit).
+	 */
+	unsigned long sz = sp->SCp.this_residual;
+	if(sz > 0x1000000)
+		sz = 0x1000000;
+	return sz;
+}
+
+void cyber_dma_dump_state(struct Sparc_ESP *esp)
+{
+	ESPLOG(("esp%d: dma -- cond_reg<%02x>\n",
+		esp->esp_id, ((struct cyber_dma_registers *)
+			      (esp->dregs))->cond_reg));
+	ESPLOG(("intreq:<%04x>, intena:<%04x>\n",
+		custom.intreqr, custom.intenar));
+}
+
+void cyber_dma_init_read(struct Sparc_ESP *esp, char *vaddress, int length)
+{
+	struct cyber_dma_registers *dregs = 
+		(struct cyber_dma_registers *) esp->dregs;
+	unsigned long addr = VTOP((unsigned long) vaddress);
+	CACHEINV(addr, length);
+#if 1
+	/* Skimming the files in SCSI have lead me to the (wrong?) conclusion
+	 * that no one expects the low-level SCSI drivers to transfer data
+	 * to or from odd addresses... So I skip this part until I learn
+	 * otherwise.
+	 * An equivalent of the dma_start and dma_stop handling from a3000.c
+	 * would have to be performed in esp_do_data and esp_do_data_finale.
+	 */
+	if(addr & 1){
+		printk("DMA makes odd access in cyber_esp.c\n");
+		printk("Please notify jskov@iesd.auc.dk about this\n");
+		panic("In dma_init_read accessing %08x", (unsigned int) addr);
+	}
+#endif
+	addr &= ~(1);
+	dregs->dma_addr0 = (addr >> 24) & 0xff;
+	dregs->dma_addr1 = (addr >> 16) & 0xff;
+	dregs->dma_addr2 = (addr >>  8) & 0xff;
+	dregs->dma_addr3 = (addr      ) & 0xff;
+	ctrl_data &= ~(CYBER_DMA_WRITE);
+
+	/* Check if physical address is outside Z2 space and of
+	 * block length/block aligned in memory. If this is the
+	 * case, enable 32 bit transfer. In all other cases, fall back
+	 * to 16 bit transfer.
+	 * Obviously 32 bit transfer should be enabled if the DMA address
+	 * and length are 32 bit aligned. However, this leads to some
+	 * strange behavior. Even 64 bit aligned addr/length fails.
+	 * Until I've found a reason for this, 32 bit transfer is only
+	 * used for full-block transfers (1kB).
+	 *							-jskov
+	 */
+#if 0
+	if((addr & 0x3fe) || length & 0x3ff || ((addr > 0x200000) &&
+						(addr < 0xff0000)))
+		ctrl_data &= ~(CYBER_DMA_Z3);	/* Z2, do 16 bit DMA */
+	else
+		ctrl_data |= CYBER_DMA_Z3; /* CHIP/Z3, do 32 bit DMA */
+#else
+	ctrl_data &= ~(CYBER_DMA_Z3);	/* Z2, do 16 bit DMA */
+#endif
+	dregs->ctrl_reg = ctrl_data;
+}
+
+void cyber_dma_init_write(struct Sparc_ESP *esp, char *vaddress, int length)
+{
+	unsigned long addr = VTOP((unsigned long) vaddress);
+	struct cyber_dma_registers *dregs = 
+		(struct cyber_dma_registers *) esp->dregs;
+#if 1
+	if(addr & 1){
+		printk("DMA makes odd access in cyber_esp.c\n");
+		printk("Please notify jskov@iesd.auc.dk about this\n");
+		panic("In dma_init_write accessing %08x",(unsigned int) addr);
+	}
+#endif
+	CACHEFLUSH(addr, length);
+	addr |= 1;
+	dregs->dma_addr0 = (addr >> 24) & 0xff;
+	dregs->dma_addr1 = (addr >> 16) & 0xff;
+	dregs->dma_addr2 = (addr >>  8) & 0xff;
+	dregs->dma_addr3 = (addr      ) & 0xff;
+	ctrl_data |= CYBER_DMA_WRITE;
+
+	/* See comment above */
+#if 0
+	if((addr & 0x3fe) || length & 0x3ff || ((addr > 0x200000) &&
+						(addr < 0xff0000)))
+		ctrl_data &= ~(CYBER_DMA_Z3);	/* Z2, do 16 bit DMA */
+	else
+		ctrl_data |= CYBER_DMA_Z3; /* CHIP/Z3, do 32 bit DMA */
+#else
+	ctrl_data &= ~(CYBER_DMA_Z3);	/* Z2, do 16 bit DMA */
+#endif
+	dregs->ctrl_reg = ctrl_data;
+}
+
+void cyber_dma_ints_off(struct Sparc_ESP *esp)
+{
+	custom.intena = (IF_PORTS);
+}
+
+void cyber_dma_ints_on(struct Sparc_ESP *esp)
+{
+	custom.intena = (IF_SETCLR | IF_PORTS);
+}
+
+int cyber_dma_irq_p(struct Sparc_ESP *esp)
+{
+	/* It's important to check the DMA IRQ bit in the correct way! */
+	return ((esp->eregs->esp_status & ESP_STAT_INTR) &&
+		((((struct cyber_dma_registers *)(esp->dregs))->cond_reg) &
+		 CYBER_DMA_HNDL_INTR));
+}
+
+void cyber_dma_led_off(struct Sparc_ESP *esp)
+{
+	ctrl_data &= ~CYBER_DMA_LED;
+}
+
+void cyber_dma_led_on(struct Sparc_ESP *esp)
+{
+	ctrl_data |= CYBER_DMA_LED;
+}
+
+void cyber_dma_poll(struct Sparc_ESP *esp, unsigned char *vaddr)
+{
+	unsigned long paddr;
+
+	/* We have to keep invalidating the cache, or the loop
+	 * would hang if the address reads 0xff the first time.
+	 */
+	paddr = VTOP(vaddr);
+	do
+		CACHEINVLINE(paddr);
+	while(*vaddr == 0xff);
+	
+	/* How can we be sure that the second byte was also
+	 * received?
+	 * Doing 16 bit DMA by default! Nothing less than that :)
+	 */
+}
+
+int cyber_dma_ports_p(struct Sparc_ESP *esp)
+{
+	return ((custom.intenar) & IF_PORTS);
+}
+
+void cyber_dma_setup(struct Sparc_ESP *esp, char *addr, int count, int write)
+{
+	/* On the Sparc, DMA_ST_WRITE means "move data from device to memory"
+	 * so when (write) is true, it actually means READ!
+	 */
+	if(write){
+		cyber_dma_init_read(esp, addr, count);
+	} else {
+		cyber_dma_init_write(esp, addr, count);
+	}
+}
diff -urN linux-2.0.15/drivers/scsi/cyber_esp.h linux-2.0.18/drivers/scsi/cyber_esp.h
--- linux-2.0.15/drivers/scsi/cyber_esp.h	Thu Jan  1 00:00:00 1970
+++ linux-2.0.18/drivers/scsi/cyber_esp.h	Sun Sep  8 14:25:58 1996
@@ -0,0 +1,87 @@
+/* cyber_esp.h: Defines and structures for the CyberStorm SCSI driver.
+ *
+ * Copyright (C) 1996 Jesper Skov (jskov@iesd.auc.dk)
+ */
+
+#include "esp.h"
+
+#ifndef CYBER_ESP_H
+#define CYBER_ESP_H
+
+/* The controller registers can be found in the Z2 config area at these
+ * offsets:
+ */
+#define CYBER_ESP_ADDR 0xf400
+#define CYBER_DMA_ADDR 0xf800
+
+
+/* The CyberStorm DMA interface */
+struct cyber_dma_registers {
+	volatile unsigned char dma_addr0;	/* DMA address (MSB) [0x000] */
+	unsigned char dmapad1[1];
+	volatile unsigned char dma_addr1;	/* DMA address       [0x002] */
+	unsigned char dmapad2[1];
+	volatile unsigned char dma_addr2;	/* DMA address       [0x004] */
+	unsigned char dmapad3[1];
+	volatile unsigned char dma_addr3;	/* DMA address (LSB) [0x006] */
+	unsigned char dmapad4[0x3fb];
+	volatile unsigned char cond_reg;        /* DMA cond    (ro)  [0x402] */
+#define ctrl_reg  cond_reg			/* DMA control (wo)  [0x402] */
+};
+
+/* DMA control bits */
+#define CYBER_DMA_LED    0x80	/* HD led control 1 = on */
+#define CYBER_DMA_WRITE  0x40	/* DMA direction. 1 = write */
+#define CYBER_DMA_Z3     0x20	/* 16 (Z2) or 32 (CHIP/Z3) bit DMA transfer */
+
+/* DMA status bits */
+#define CYBER_DMA_HNDL_INTR 0x80	/* DMA IRQ pending? */
+
+/* The bits below appears to be Phase5 Debug bits only; they were not
+ * described by Phase5 so using them may seem a bit stupid...
+ */
+#define CYBER_HOST_ID 0x02	/* If set, host ID should be 7, otherwise
+				 * it should be 6.
+				 */
+#define CYBER_SLOW_CABLE 0x08	/* If *not* set, assume SLOW_CABLE */
+
+
+/* CACHE control */
+#if !defined(CACHEFLUSH)
+#define CACHEFLUSH(paddr, length) cache_push(paddr, length)
+#define CACHEINV(paddr, length) cache_clear(paddr, length)
+#define CACHEFLUSHLINE(paddr) cache_push(paddr, 16)
+#define CACHEINVLINE(paddr) cache_clear(paddr, 16)
+#endif
+
+extern int cyber_esp_detect(struct SHT *);
+extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int esp_abort(Scsi_Cmnd *);
+extern int esp_reset(Scsi_Cmnd *, unsigned int);
+
+
+#define SCSI_CYBER_ESP {                                                               \
+/* struct SHT *next */                                         NULL,                   \
+/* long *usage_count */                                        NULL,                   \
+/* struct proc_dir_entry *proc_dir */                          &proc_scsi_esp,         \
+/* int (*proc_info)(char *, char **, off_t, int, int, int) */  NULL,                   \
+/* const char *name */                                         "CyberStorm SCSI", \
+/* int detect(struct SHT *) */                                 cyber_esp_detect,  \
+/* int release(struct Scsi_Host *) */                          NULL,                   \
+/* const char *info(struct Scsi_Host *) */                     NULL,               \
+/* int command(Scsi_Cmnd *) */                                 NULL,            \
+/* int queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)) */ esp_queue,              \
+/* int abort(Scsi_Cmnd *) */                                   esp_abort,              \
+/* int reset(Scsi_Cmnd *) */                                   esp_reset,              \
+/* int slave_attach(int, int) */                               NULL,                   \
+/* int bios_param(Disk *, kdev_t, int[]) */                    NULL,                   \
+/* int can_queue */                                            7,                      \
+/* int this_id */                                              7,                      \
+/* short unsigned int sg_tablesize */                          SG_ALL,                 \
+/* short cmd_per_lun */                                        1,                      \
+/* unsigned char present */                                    0,                      \
+/* unsigned unchecked_isa_dma:1 */                             0,                      \
+/* unsigned use_clustering:1 */                                DISABLE_CLUSTERING, }
+
+#endif /* CYBER_ESP_H */
+
diff -urN linux-2.0.15/drivers/scsi/esp.c linux-2.0.18/drivers/scsi/esp.c
--- linux-2.0.15/drivers/scsi/esp.c	Mon Aug 26 22:12:03 1996
+++ linux-2.0.18/drivers/scsi/esp.c	Sun Sep  8 14:25:18 1996
@@ -1,6 +1,16 @@
 /* esp.c:  EnhancedScsiProcessor Sun SCSI driver code.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Ugly generalization hacks by Jesper Skov.
+ */
+
+/* TODO:
+ *
+ * 1) Maybe disable parity checking in config register one for SCSI1
+ *    targets.
+ * 2) Add support for DMA2 pipelining.
+ * 3) Add tagged queueing.
  */
 
 #include <linux/kernel.h>
@@ -16,42 +26,69 @@
 #include "hosts.h"
 #include "esp.h"
 
+#ifdef CONFIG_CYBERSTORM_SCSI
+#include "cyber_esp.h"
+#endif
+
+#ifdef CONFIG_BLZ2060_SCSI
+#include "blz2060_esp.h"
+#endif
+
+#ifdef CONFIG_AMIGA		/* Used by both CyberStorm and Ferret */
+#include <asm/zorro.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#endif
+
+#ifdef CONFIG_SCSI_SUNESP
 #include <asm/sbus.h>
 #include <asm/dma.h>
-#include <asm/system.h>
-#include <asm/idprom.h>
 #include <asm/machines.h>
-#include <asm/ptrace.h>
-#include <asm/pgtable.h>
 #include <asm/oplib.h>
 #include <asm/vaddrs.h>
-#include <asm/io.h>
-
-#define DEBUG_ESP
-/* #define DEBUG_ESP_SG */
-
-#if defined(DEBUG_ESP)
-#define ESPLOG(foo)  printk foo
-#else
-#define ESPLOG(foo)
-#endif /* (DEBUG_ESP) */
+#endif
 
-#define INTERNAL_ESP_ERROR \
-        (panic ("Internal ESP driver error in file %s, line %d\n", \
-		__FILE__, __LINE__))
-
-#define INTERNAL_ESP_ERROR_NOPANIC \
-        (printk ("Internal ESP driver error in file %s, line %d\n", \
-		 __FILE__, __LINE__))
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
 
-/* This enum will be expanded when we have sync code written. */
+/* Command phase enumeration. */
 enum {
-	not_issued   = 0x01,  /* Still in the issue_SC queue.          */
-	in_selection = 0x02,  /* ESP is arbitrating, awaiting IRQ      */
-	in_datain    = 0x04,  /* Data is transferring over the bus     */
-	in_dataout   = 0x08,  /* Data is transferring over the bus     */
-	in_status    = 0x10,  /* Awaiting status/msg bytes from target */
-	in_finale    = 0x11,  /* Sent Msg ack, awaiting disconnect     */
+	not_issued    = 0x00,  /* Still in the issue_SC queue.          */
+
+	/* Various forms of selecting a target. */
+#define in_slct_mask    0x10
+	in_slct_norm  = 0x10,  /* ESP is arbitrating, normal selection  */
+	in_slct_stop  = 0x11,  /* ESP will select, then stop with IRQ   */
+	in_slct_msg   = 0x12,  /* select, then send a message           */
+	in_slct_tag   = 0x13,  /* select and send tagged queue msg      */
+	in_slct_sneg  = 0x14,  /* select and acquire sync capabilities  */
+
+	/* Any post selection activity. */
+#define in_phases_mask  0x20
+	in_datain     = 0x20,  /* Data is transferring from the bus     */
+	in_dataout    = 0x21,  /* Data is transferring to the bus       */
+	in_data_done  = 0x22,  /* Last DMA data operation done (maybe)  */
+	in_msgin      = 0x23,  /* Eating message from target            */
+	in_msgincont  = 0x24,  /* Eating more msg bytes from target     */
+	in_msgindone  = 0x25,  /* Deciding to do with what we were sent */
+	in_msgout     = 0x26,  /* Sending message to target             */
+	in_msgoutdone = 0x27,  /* Done sending msg out                  */
+	in_cmdbegin   = 0x28,  /* Sending cmd after abnormal selection  */
+	in_cmdend     = 0x29,  /* Done sending slow cmd                 */
+	in_status     = 0x2a,  /* Was in status phase, finishing cmd    */
+	in_freeing    = 0x2b,  /* freeing the bus for cmd cmplt or disc */
+	in_the_dark   = 0x2c,  /* Don't know what bus phase we are in   */
+
+	/* Special states, ie. not normal bus transitions... */
+#define in_spec_mask    0x80
+	in_abortone   = 0x80,  /* Aborting one command currently        */
+	in_abortall   = 0x81,  /* Blowing away all commands we have     */
+	in_resetdev   = 0x82,  /* SCSI target reset in progress         */
+	in_resetbus   = 0x83,  /* SCSI bus reset in progress            */
+	in_tgterror   = 0x84,  /* Target did something stupid           */
 };
 
 struct proc_dir_entry proc_scsi_esp = {
@@ -59,9 +96,10 @@
 	S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
 
-struct Sparc_ESP *espchain;
+struct Sparc_ESP *espchain = 0;
+int nesps = 0, esps_in_use = 0, esps_running = 0;
 
-static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs);
+void esp_intr(int irq, void *dev_id, struct pt_regs *pregs);
 static void esp_done(struct Sparc_ESP *esp, int error);
 
 /* Debugging routines */
@@ -181,10 +219,129 @@
 		(stepreg == ESP_STEP_SID ? "1BYTE_MSG_SENT" :
 		 (stepreg == ESP_STEP_NCMD ? "NOT_IN_CMD_PHASE" :
 		  (stepreg == ESP_STEP_PPC ? "CMD_BYTES_LOST" :
-		   (stepreg == ESP_STEP_FINI ? "CMD_SENT_OK" :
+		   (stepreg == ESP_STEP_FINI4 ? "CMD_SENT_OK" :
 		    "UNKNOWN"))))));
 }
 
+#ifdef DEBUG_ESP
+static char *phase_string(int phase)
+{
+	switch(phase) {
+	case not_issued:
+		return "UNISSUED";
+	case in_slct_norm:
+		return "SLCTNORM";
+	case in_slct_stop:
+		return "SLCTSTOP";
+	case in_slct_msg:
+		return "SLCTMSG";
+	case in_slct_tag:
+		return "SLCTTAG";
+	case in_slct_sneg:
+		return "SLCTSNEG";
+	case in_datain:
+		return "DATAIN";
+	case in_dataout:
+		return "DATAOUT";
+	case in_data_done:
+		return "DATADONE";
+	case in_msgin:
+		return "MSGIN";
+	case in_msgincont:
+		return "MSGINCONT";
+	case in_msgindone:
+		return "MSGINDONE";
+	case in_msgout:
+		return "MSGOUT";
+	case in_msgoutdone:
+		return "MSGOUTDONE";
+	case in_cmdbegin:
+		return "CMDBEGIN";
+	case in_cmdend:
+		return "CMDEND";
+	case in_status:
+		return "STATUS";
+	case in_freeing:
+		return "FREEING";
+	case in_the_dark:
+		return "CLUELESS";
+	case in_abortone:
+		return "ABORTONE";
+	case in_abortall:
+		return "ABORTALL";
+	case in_resetdev:
+		return "RESETDEV";
+	case in_resetbus:
+		return "RESETBUS";
+	case in_tgterror:
+		return "TGTERROR";
+	default:
+		return "UNKNOWN";
+	};
+}
+#endif
+
+static inline void esp_advance_phase(Scsi_Cmnd *s, int newphase)
+{
+#ifdef DEBUG_STATE_MACHINE
+	ESPLOG(("<%s>", phase_string(newphase)));
+#endif
+	s->SCp.sent_command = s->SCp.phase;
+	s->SCp.phase = newphase;
+}
+
+extern inline void esp_cmd(struct Sparc_ESP *esp, struct ESP_regs *eregs,
+			   unchar cmd)
+{
+#ifdef DEBUG_ESP_CMDS
+	esp->espcmdlog[esp->espcmdent] = cmd;
+	esp->espcmdent = (esp->espcmdent + 1) & 31;
+#endif
+	eregs->esp_cmd = cmd;
+}
+
+/* How we use the various Linux SCSI data structures for operation.
+ *
+ * struct scsi_cmnd:
+ *
+ *   We keep track of the syncronous capabilities of a target
+ *   in the device member, using sync_min_period and
+ *   sync_max_offset.  These are the values we directly write
+ *   into the ESP registers while running a command.  If offset
+ *   is zero the ESP will use asynchronous transfers.
+ *   If the borken flag is set we assume we shouldn't even bother
+ *   trying to negotiate for synchronous transfer as this target
+ *   is really stupid.  If we notice the target is dropping the
+ *   bus, and we have been allowing it to disconnect, we clear
+ *   the disconnect flag.
+ *
+ *   XXX When and how to first set the disconnect flag initially? XXX
+ *
+ *   XXX Tagged commands?  Linked commands, if at all? XXX
+ *
+ *   The scsi_pointer member of a command structure is used for a few
+ *   things.  We hold the scatter list, if any, here first of all.  Since
+ *   we may need to virtually remap some transfers to different addresses
+ *   we keep track of this in the dvma_address member per scatter, before
+ *   we call the completion routine we call the mmu specific unmapping
+ *   routine which is expected to set all the dvma_address fields to NULL
+ *   so that the target driver does not get confused.
+ *   As expected the Status and Message members of the scsi_pointer struct
+ *   hold the information few to us by the target when a command completes.
+ *   The have_data_in field is used as our "save/restore pointers" save area.
+ *   To make scatter and non-scatter work correctly, this value is said
+ *   to be the logic offset from the beggining of this data transfer.  So
+ *   if for example, the target disconnects (legally) in the middle of
+ *   the third scatter list element we will save have_data_in to be the
+ *   (length_of_scatter_element1 + length_of_scatter_element2 +
+ *    how_far_we_got_in_scatter_element3).  This works out perfectly because
+ *   this is what the numbers mean when the target gives us a modify data
+ *   pointers message.
+ *   The phase keeps track of the state of the ESP driver for this particular
+ *   command.  We hold the previous phase in the sent_command field.
+ */
+
+
 /* Manipulation of the ESP command queues.  Thanks to the aha152x driver
  * and its author, Juergen E. Fischer, for the methods used here.
  * Note that these are per-ESP queues, not global queues like
@@ -207,6 +364,16 @@
 	restore_flags(flags);
 }
 
+static inline void prepend_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC)
+{
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	new_SC->host_scribble = (unsigned char *) *SC;
+	*SC = new_SC;
+	restore_flags(flags);
+}
+
 static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd **SC)
 {
 	Scsi_Cmnd *ptr;
@@ -240,289 +407,307 @@
 	return ptr;
 }
 
-static inline void do_pause(unsigned amount)
-{
-	unsigned long the_time = jiffies + amount;
-
-	while(jiffies < the_time)
-		barrier(); /* Not really needed, but... */
-}
+/* Resetting various pieces of the ESP scsi driver chipset */
 
-/* This places the ESP into a known state at boot time. */
-static inline void esp_bootup_reset(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs)
+/* Reset the ESP chip, _not_ the SCSI bus. */
+static inline void esp_reset_esp(struct Sparc_ESP *esp, struct ESP_regs *eregs)
 {
-	struct sparc_dma_registers *dregs = esp->dregs;
-	volatile unchar trash;
-
-	/* Punt the DVMA into a known state. */
-	dregs->cond_reg |= DMA_RST_SCSI;
-	do_pause(100);
-	dregs->cond_reg &= ~(DMA_RST_SCSI);
-	if(esp->dma->revision == dvmarev2)
-		if(esp->erev != esp100)
-			dregs->cond_reg |= DMA_3CLKS;
-	else if(esp->dma->revision == dvmarev3)
-		if(esp->erev == fas236 || esp->erev == fas100a) {
-			dregs->cond_reg &= ~(DMA_3CLKS);
-			dregs->cond_reg |= DMA_2CLKS;
-		}
-	else if(esp->dma->revision == dvmaesc1)
-		dregs->cond_reg |= DMA_ADD_ENABLE;
-	DMA_INTSON(dregs);
+	int family_code, version, i;
+	volatile int trash;
 
 	/* Now reset the ESP chip */
-	eregs->esp_cmd = ESP_CMD_RC;
-	eregs->esp_cmd = (ESP_CMD_NULL | ESP_CMD_DMA);
-	eregs->esp_cmd = (ESP_CMD_NULL | ESP_CMD_DMA); /* borken hardware... */
+
+	esp_cmd(esp, eregs, ESP_CMD_RC);
+	esp_cmd(esp, eregs, ESP_CMD_NULL | ESP_CMD_DMA);
+	esp_cmd(esp, eregs, ESP_CMD_NULL | ESP_CMD_DMA);
 
 	/* Reload the configuration registers */
-	eregs->esp_cfg1  = esp->config1;
 	eregs->esp_cfact = esp->cfact;
 	eregs->esp_stp   = 0;
 	eregs->esp_soff  = 0;
-	eregs->esp_timeo = esp->sync_defp;
-	if(esp->erev == esp100a || esp->erev == esp236)
+	eregs->esp_timeo = esp->neg_defp;
+
+	/* This is the only point at which it is reliable to read
+	 * the ID-code for a fast ESP chip variant.
+	 */
+	if(esp->erev == fast) {
+		version = eregs->esp_uid;
+		family_code = (version & 0xf8) >> 3;
+		if(family_code != 2)
+			esp->erev = fas100a;
+		else
+			esp->erev = fas236;
+		printk("esp%d: FAST chip is %s (family=%d, version=%d)\n",
+		       esp->esp_id,
+		       (esp->erev == fas236) ? "fas236" : "fas100a",
+		       family_code, (version & 7));
+
+
+		esp->max_period = ((35 * esp->ccycle) / 1000);
+		esp->min_period = ((4 * esp->ccycle) / 1000);
+	} else {
+		esp->max_period = ((35 * esp->ccycle) / 1000);
+		esp->min_period = ((5 * esp->ccycle) / 1000);
+	}
+	esp->max_period = (esp->max_period + 3)>>2;
+	esp->min_period = (esp->min_period + 3)>>2;
+
+	eregs->esp_cfg1  = esp->config1;
+	switch(esp->erev) {
+	case esp100:
+		/* nothing to do */
+		break;
+	case esp100a:
+		eregs->esp_cfg2 = esp->config2;
+		break;
+	case esp236:
+		/* Slow 236 */
+		eregs->esp_cfg2 = esp->config2;
+		eregs->esp_cfg3 = esp->config3[0];
+		break;
+	case fas236:
+		/* Fast 236 */
+		eregs->esp_cfg2 = esp->config2;
+		for(i=0; i<8; i++)
+			esp->config3[i] |= ESP_CONFIG3_FCLK;
+		eregs->esp_cfg3 = esp->config3[0];
+		if(esp->diff)
+			esp->radelay = 0;
+		else
+			esp->radelay = 96;
+		break;
+	case fas100a:
+		/* Fast 100a */
 		eregs->esp_cfg2 = esp->config2;
-	if(esp->erev == esp236)
+		for(i=0; i<8; i++)
+			esp->config3[i] |= ESP_CONFIG3_FCLOCK;
 		eregs->esp_cfg3 = esp->config3[0];
+		esp->radelay = 32;
+		break;
+	default:
+		panic("esp: what could it be... I wonder...");
+		break;
+	};
+
 	/* Eat any bitrot in the chip */
 	trash = eregs->esp_intrpt;
+}
+
+/* This places the ESP into a known state at boot time. */
+inline void esp_bootup_reset(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	volatile unchar trash;
+
+	/* Reset the DMA */
+	if(esp->dma_reset)
+		esp->dma_reset(esp);
+
+	/* Reset the ESP */
+	esp_reset_esp(esp, eregs);
 
 	/* Reset the SCSI bus, but tell ESP not to generate an irq */
 	eregs->esp_cfg1 |= ESP_CONFIG1_SRRDISAB;
-	eregs->esp_cmd = ESP_CMD_RS;
-	do_pause(200);
+	esp_cmd(esp, eregs, ESP_CMD_RS);
+	udelay(400);
 	eregs->esp_cfg1 = esp->config1;
 
 	/* Eat any bitrot in the chip and we are done... */
 	trash = eregs->esp_intrpt;
 }
 
-/* Detecting ESP chips on the machine.  This is the simple and easy
- * version.
+/* Allocate structure and insert basic data such as SCSI chip frequency
+ * data and a pointer to the device
  */
-int esp_detect(Scsi_Host_Template *tpnt)
+struct Sparc_ESP* esp_allocate(Scsi_Host_Template *tpnt, unsigned int fmhz,
+			       void *esp_dev)
 {
 	struct Sparc_ESP *esp, *elink;
 	struct Scsi_Host *esp_host;
-	struct linux_sbus *sbus;
-	struct linux_sbus_device *esp_dev, *sbdev_iter;
-	struct Sparc_ESP_regs *eregs;
-	struct sparc_dma_registers *dregs;
-	struct Linux_SBus_DMA *dma, *dlink;
-	unsigned int fmhz;
-	unchar ccf, bsizes, bsizes_more;
-	int nesps = 0;
-	int esp_node;
-
-	espchain = 0;
-	if(!SBus_chain)
-		panic("No SBUS in esp_detect()");
-	for_each_sbus(sbus) {
-		for_each_sbusdev(sbdev_iter, sbus) {
-			/* Is it an esp sbus device? */
-			esp_dev = sbdev_iter;
-			if(strcmp(esp_dev->prom_name, "esp") &&
-			   strcmp(esp_dev->prom_name, "SUNW,esp")) {
-				if(!esp_dev->child ||
-				   strcmp(esp_dev->prom_name, "espdma"))
-					continue; /* nope... */
-				esp_dev = esp_dev->child;
-				if(strcmp(esp_dev->prom_name, "esp") &&
-				   strcmp(esp_dev->prom_name, "SUNW,esp"))
-					continue; /* how can this happen? */
-			}
-			esp_host = scsi_register(tpnt, sizeof(struct Sparc_ESP));
-			if(!esp_host)
-				panic("Cannot register ESP SCSI host");
-			esp = (struct Sparc_ESP *) esp_host->hostdata;
-			if(!esp)
-				panic("No esp in hostdata");
-			esp->ehost = esp_host;
-			esp->edev = esp_dev;
-			/* Put into the chain of esp chips detected */
-			if(espchain) {
-				elink = espchain;
-				while(elink->next) elink = elink->next;
-				elink->next = esp;
-			} else {
-				espchain = esp;
-			}
-			esp->next = 0;
+	unchar ccf;
 
-			/* Get misc. prom information */
-#define ESP_IS_MY_DVMA(esp, dma)  \
-	((esp->edev->my_bus == dma->SBus_dev->my_bus) && \
-         (esp->edev->slot == dma->SBus_dev->slot) && \
-	 (!strcmp(dma->SBus_dev->prom_name, "dma") || \
-	  !strcmp(dma->SBus_dev->prom_name, "espdma")))
-
-			esp_node = esp_dev->prom_node;
-			prom_getstring(esp_node, "name", esp->prom_name,
-				       sizeof(esp->prom_name));
-			esp->prom_node = esp_node;
-			for_each_dvma(dlink) {
-				if(ESP_IS_MY_DVMA(esp, dlink) && !dlink->allocated)
-					break;
-			}
-#undef ESP_IS_MY_DVMA
-			/* If we don't know how to handle the dvma, do not use this device */
-			if(!dlink){
-				printk ("Cannot find dvma for ESP SCSI\n");
-				scsi_unregister (esp_host);
-				continue;
-			}
-			if (dlink->allocated){
-				printk ("esp: can't use my espdma\n");
-				scsi_unregister (esp_host);
-				continue;
-			}
-			dlink->allocated = 1;
-			dma = dlink;
-			esp->dma = dma;
-			esp->dregs = dregs = dma->regs;
-
-			/* Map in the ESP registers from I/O space */
-			prom_apply_sbus_ranges(esp->edev->reg_addrs, 1);
-			esp->eregs = eregs = (struct Sparc_ESP_regs *)
-				sparc_alloc_io(esp->edev->reg_addrs[0].phys_addr, 0,
-					       PAGE_SIZE, "ESP Registers",
-					       esp->edev->reg_addrs[0].which_io, 0x0);
-			if(!eregs)
-				panic("ESP registers unmappable");
-			esp->esp_command =
-				sparc_dvma_malloc(16, "ESP DVMA Cmd Block");
-			if(!esp->esp_command)
-				panic("ESP DVMA transport area unmappable");
-
-			/* Set up the irq's etc. */
-			esp->ehost->base = (unsigned char *) esp->eregs;
-			esp->ehost->io_port = (unsigned int) esp->eregs;
-			esp->ehost->n_io_port = (unsigned char)
-				esp->edev->reg_addrs[0].reg_size;
-			/* XXX The following may be different on sun4ms XXX */
-			esp->ehost->irq = esp->irq = esp->edev->irqs[0].pri;
-
-			/* Allocate the irq only if necessary */
-			for_each_esp(elink) {
-				if((elink != esp) && (esp->irq == elink->irq)) {
-					goto esp_irq_acquired; /* BASIC rulez */
-				}
-			}
-			/* XXX We have shared interrupts per level now, maybe
-			 * XXX use them, maybe not...
-			 */
-			if(request_irq(esp->ehost->irq, esp_intr, SA_INTERRUPT,
-				       "Sparc ESP SCSI", NULL))
-				panic("Cannot acquire ESP irq line");
-esp_irq_acquired:
-			printk("esp%d: IRQ %d ", nesps, esp->ehost->irq);
-			/* Figure out our scsi ID on the bus */
-			esp->scsi_id = prom_getintdefault(esp->prom_node,
-							  "initiator-id", -1);
-			if(esp->scsi_id == -1)
-				esp->scsi_id = prom_getintdefault(esp->prom_node,
-								  "scsi-initiator-id", -1);
-			if(esp->scsi_id == -1)
-				esp->scsi_id =
-					prom_getintdefault(esp->edev->my_bus->prom_node,
-							   "scsi-initiator-id", 7);
-			esp->ehost->this_id = esp->scsi_id;
-			esp->scsi_id_mask = (1 << esp->scsi_id);
-			/* Check for differential bus */
-			esp->diff = prom_getintdefault(esp->prom_node, "differential", -1);
-			esp->diff = (esp->diff == -1) ? 0 : 1;
-			/* Check out the clock properties of the chip */
-			fmhz = prom_getintdefault(esp->prom_node, "clock-frequency", -1);
-			if(fmhz==-1)
-				fmhz = prom_getintdefault(esp->edev->my_bus->prom_node,
-							  "clock-frequency", -1);
-			if(fmhz <= (5000))
-				ccf = 0;
-			else
-				ccf = (((5000 - 1) + (fmhz))/(5000));
-			if(!ccf || ccf > 8) {
-				ccf = ESP_CCF_F4;
-				fmhz = (5000 * 4);
-			}
-			if(ccf==(ESP_CCF_F7+1))
-				esp->cfact = ESP_CCF_F0;
-			else if(ccf == ESP_CCF_NEVER)
-				esp->cfact = ESP_CCF_F2;
-			else
-				esp->cfact = ccf;
-			esp->cfreq = fmhz;
-			esp->ccycle = ((1000000000) / ((fmhz)/1000));
-			esp->ctick = ((7682 * esp->cfact * esp->ccycle)/1000);
-			esp->sync_defp = ((7682 + esp->ctick - 1) / esp->ctick);
-
-			/* XXX HACK HACK HACK XXX */
-			if (esp->sync_defp < 153)
-				esp->sync_defp = 153;
-
-			printk("SCSI ID %d  Clock %d MHz Period %2x ", esp->scsi_id,
-			       (fmhz / 1000), esp->sync_defp);
-
-			/* Find the burst sizes this dma supports. */
-			bsizes = prom_getintdefault(esp->prom_node, "burst-sizes", 0xff);
-			bsizes_more = prom_getintdefault(esp->edev->my_bus->prom_node,
-							 "burst-sizes", 0xff);
-			if(bsizes_more != 0xff) bsizes &= bsizes_more;
-			if(bsizes == 0xff || (bsizes & DMA_BURST16)==0 ||
-			   (bsizes & DMA_BURST32)==0)
-				bsizes = (DMA_BURST32 - 1);
-			esp->bursts = bsizes;
-
-			/* Probe the revision of this esp */
-			esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
-			esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
-			esp->config3[0] = ESP_CONFIG3_TENB;
-			eregs->esp_cfg2 = esp->config2;
-			if((eregs->esp_cfg2 & ~(ESP_CONFIG2_MAGIC)) !=
-			   (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
-				printk("NCR53C90(esp100) detected\n");
-				esp->erev = esp100;
-			} else {
-				eregs->esp_cfg2 = esp->config2 = 0;
-				eregs->esp_cfg3 = 0;
-				eregs->esp_cfg3 = esp->config3[0] = 5;
-				if(eregs->esp_cfg3 != 5) {
-					printk("NCR53C90A(esp100a) detected\n");
-					esp->erev = esp100a;
-				} else {
-					int target;
+	esp_host = scsi_register(tpnt, sizeof(struct Sparc_ESP));
+	if(!esp_host)
+		panic("Cannot register ESP SCSI host");
+	esp = (struct Sparc_ESP *) esp_host->hostdata;
+	if(!esp)
+		panic("No esp in hostdata");
+	esp->ehost = esp_host;
+	esp->edev = esp_dev;
+	esp->esp_id = nesps++;
+	/* Put into the chain of esp chips detected */
+	if(espchain) {
+		elink = espchain;
+		while(elink->next) elink = elink->next;
+		elink->next = esp;
+	} else {
+		espchain = esp;
+	}
+	esp->next = 0;
 
-					for(target=0; target<8; target++)
-						esp->config3[target] = 0;
-					eregs->esp_cfg3 = 0;
-					if(esp->cfact > ESP_CCF_F5) {
-						printk("NCR53C9XF(espfast) detected\n");
-						esp->erev = fast;
-						esp->config2 |= ESP_CONFIG2_FENAB;
-						eregs->esp_cfg2 = esp->config2;
-					} else {
-						printk("NCR53C9x(esp236) detected\n");
-						esp->erev = esp236;
-						eregs->esp_cfg2 = esp->config2 = 0;
-					}
-				}
-			}				
 
-			/* Initialize the command queues */
-			esp->current_SC = 0;
-			esp->disconnected_SC = 0;
-			esp->issue_SC = 0;
+	/* Check out the clock properties of the chip. */
 
-			/* Reset the thing before we try anything... */
-			esp_bootup_reset(esp, eregs);
+	/* This is getting messy but it has to be done
+	 * correctly or else you get weird behavior all
+	 * over the place.  We are trying to basically
+	 * figure out three pieces of information.
+	 *
+	 * a) Clock Conversion Factor
+	 *
+	 *    This is a representation of the input
+	 *    crystal clock frequency going into the
+	 *    ESP on this machine.  Any operation whose
+	 *    timing is longer than 400ns depends on this
+	 *    value being correct.  For example, you'll
+	 *    get blips for arbitration/selection during
+	 *    high load or with multiple targets if this
+	 *    is not set correctly.
+	 *
+	 * b) Selection Time-Out
+	 *
+	 *    The ESP isn't very bright and will arbitrate
+	 *    for the bus and try to select a target
+	 *    forever if you let it.  This value tells
+	 *    the ESP when it has taken too long to
+	 *    negotiate and that it should interrupt
+	 *    the CPU so we can see what happened.
+	 *    The value is computed as follows (from
+	 *    NCR/Symbios chip docs).
+	 *
+	 *          (Time Out Period) *  (Input Clock)
+	 *    STO = ----------------------------------
+	 *          (8192) * (Clock Conversion Factor)
+	 *
+	 *    You usually want the time out period to be
+	 *    around 250ms, I think we'll set it a little
+	 *    bit higher to account for fully loaded SCSI
+	 *    bus's and slow devices that don't respond so
+	 *    quickly to selection attempts. (yeah, I know
+	 *    this is out of spec. but there is a lot of
+	 *    buggy pieces of firmware out there so bite me)
+	 *
+	 * c) Imperical constants for synchronous offset
+	 *    and transfer period register values
+	 *
+	 *    This entails the smallest and largest sync
+	 *    period we could ever handle on this ESP.
+	 */
+	
+	if(fmhz <= (5000000))
+		ccf = 0;
+	else
+		ccf = (((5000000 - 1) + (fmhz))/(5000000));
+	if(!ccf || ccf > 8) {
+		/* If we can't find anything reasonable,
+		 * just assume 20MHZ.  This is the clock
+		 * frequency of the older sun4c's where I've
+		 * been unable to find the clock-frequency
+		 * PROM property.  All other machines provide
+		 * useful values it seems.
+		 */
+		ccf = ESP_CCF_F4;
+		fmhz = (20000000);
+	}
+	if(ccf==(ESP_CCF_F7+1))
+		esp->cfact = ESP_CCF_F0;
+	else if(ccf == ESP_CCF_NEVER)
+		esp->cfact = ESP_CCF_F2;
+	else
+		esp->cfact = ccf;
+	esp->cfreq = fmhz;
+	esp->ccf = ccf;
+	esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz);
+	esp->ctick = ESP_TICK(ccf, esp->ccycle);
+	esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf);
+	esp->sync_defp = SYNC_DEFP_SLOW;
+
+	return esp;
+}
+
+/* Complete initialization of ESP structure and device
+ * Caller must have initialized appropriate parts of the ESP structure
+ * between the call to esp_allocate and this function.
+ */
+void esp_initialize(struct Sparc_ESP *esp)
+{
+	struct ESP_regs *eregs = esp->eregs;
+	int i;
+	
+	/* Fill in ehost data */
+	esp->ehost->base = (unsigned char *) eregs;
+	esp->ehost->io_port = (unsigned int) eregs;
+	esp->ehost->this_id = esp->scsi_id;
+	esp->ehost->irq = esp->irq;
+
+	/* Probe the revision of this esp */
+	esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
+	esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
+	eregs->esp_cfg2 = esp->config2;
+	if((eregs->esp_cfg2 & ~(ESP_CONFIG2_MAGIC)) !=
+	   (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
+		printk("NCR53C90(esp100) detected\n");
+		esp->erev = esp100;
+	} else {
+		eregs->esp_cfg2 = esp->config2 = 0;
+		eregs->esp_cfg3 = 0;
+		eregs->esp_cfg3 = esp->config3[0] = 5;
+		if(eregs->esp_cfg3 != 5) {
+			printk("NCR53C90A(esp100a) detected\n");
+			esp->erev = esp100a;
+		} else {
+			int target;
+			
+			for(target=0; target<8; target++)
+				esp->config3[target] = 0;
+			eregs->esp_cfg3 = 0;
+			if((esp->ccf) > ESP_CCF_F5) {
+				printk("NCR53C9XF(espfast) detected\n");
+				esp->erev = fast;
+				eregs->esp_cfg2 = esp->config2 = 0;
+				esp->sync_defp = SYNC_DEFP_FAST;
+			} else {
+				printk("NCR53C9x(esp236) detected\n");
+				esp->erev = esp236;
+				eregs->esp_cfg2 = esp->config2 = 0;
+			}
+		}
+	}				
+	
+	/* Initialize the command queues */
+	esp->current_SC = 0;
+	esp->disconnected_SC = 0;
+	esp->issue_SC = 0;
+
+	/* Clear the state machines. */
+	esp->targets_present = 0;
+	esp->being_reselected = 0;
+	esp->resetting_bus = 0;
+	esp->aborting_cmd = 0;
+	esp->aborting_all = 0;
+	esp->snip = 0;
+	esp->targets_present = 0;
+	for(i = 0; i < 32; i++)
+		esp->espcmdlog[i] = 0;
+	esp->espcmdent = 0;
+	for(i = 0; i < 16; i++) {
+		esp->cur_msgout[i] = 0;
+		esp->cur_msgin[i] = 0;
+	}
+	esp->prevmsgout = esp->prevmsgin = 0;
+	esp->msgout_len = esp->msgin_len = 0;
+	
+	/* Reset the thing before we try anything... */
+	esp_bootup_reset(esp, eregs);
+	
+	esps_in_use++;
+
+	printk("SCSI ID %d  Clock %d MHz CCF=%d Time-Out %d ",
+	       esp->scsi_id, (esp->cfreq / 1000000),
+	       esp->ccf, (int) esp->neg_defp);
 
-			nesps++;
 #ifdef THREADED_ESP_DRIVER
-			kernel_thread(esp_kernel_thread, esp, 0);
+	kernel_thread(esp_kernel_thread, esp, 0);
 #endif
-		} /* for each sbusdev */
-	} /* for each sbus */
-	return nesps;
 }
 
 /*
@@ -542,8 +727,6 @@
 		return "Sparc ESP100A (NCR53C90A)";
 	case esp236:
 		return "Sparc ESP236";
-	case fast:
-		return "Sparc ESP-FAST (236 or 100A)";
 	case fas236:
 		return "Sparc ESP236-FAST";
 	case fas100a:
@@ -553,80 +736,241 @@
 	};
 }
 
-/* Execute a SCSI command when the bus is free.   All callers
- * turn off all interrupts, so we don't need to explicitly do
- * it here.
+/* Some rules:
+ *
+ *   1) Never ever panic while something is live on the bus.
+ *      If there is to be any chance of syncing the disks this
+ *      rule is to be obeyed.
+ *
+ *   2) Any target that causes a foul condition will no longer
+ *      have synchronous transfers done to it, no questions
+ *      asked.
+ *
+ *   3) Keep register accesses to a minimum.  Think about some
+ *      day when we have Xbus machines this is running on and
+ *      the ESP chip is on the other end of the machine on a
+ *      different board from the cpu where this is running.
+ */
+
+/* Fire off a command.  We assume the bus is free and that the only
+ * case where we could see an interrupt is where we have disconnected
+ * commands active and they are trying to reselect us.
  */
+static inline void esp_check_cmd(struct Sparc_ESP *esp, Scsi_Cmnd *sp)
+{
+	switch(sp->cmd_len) {
+	case 6:
+	case 10:
+	case 12:
+		esp->esp_slowcmd = 0;
+		break;
+
+	default:
+		esp->esp_slowcmd = 1;
+		esp->esp_scmdleft = sp->cmd_len;
+		esp->esp_scmdp = &sp->cmnd[0];
+		break;
+	};
+}
+
+static inline void build_sync_nego_msg(struct Sparc_ESP *esp, int period, int offset)
+{
+	esp->cur_msgout[0] = EXTENDED_MESSAGE;
+	esp->cur_msgout[1] = 3;
+	esp->cur_msgout[2] = EXTENDED_SDTR;
+	esp->cur_msgout[3] = period;
+	esp->cur_msgout[4] = offset;
+	esp->msgout_len = 5;
+}
+
 static inline void esp_exec_cmd(struct Sparc_ESP *esp)
 {
-	struct sparc_dma_registers *dregs;
-	struct Sparc_ESP_regs *eregs;
+	struct ESP_regs *eregs = esp->eregs;
 	Scsi_Cmnd *SCptr;
+	Scsi_Device *SDptr;
+	volatile unchar *cmdp = esp->esp_command;
+	unsigned char the_esp_command;
+	int lun, target;
 	int i;
 
-	eregs = esp->eregs;
-	dregs = esp->dregs;
+	/* Try to save the register access. */
+	if(esp->disconnected_SC && (esp->dma_irq_p(esp)))
+		return; /* Hold off, we've been reselected. */
 
 	/* Grab first member of the issue queue. */
 	SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC);
+
+	/* Safe to panic here because current_SC is null. */
 	if(!SCptr)
-		goto bad;
-	SCptr->SCp.phase = in_selection;
+		panic("esp: esp_exec_cmd and issue queue is NULL");
 
-	/* NCR docs say:
-	 * 1) Load select/reselect Bus ID register with target ID
-	 * 2) Load select/reselect Timeout Reg with desired value
-	 * 3) Load Synchronous offset register with zero (for
-	 *    asynchronous transfers).
-	 * 4) Load Synchronous Transfer Period register (if
-	 *    synchronous)
-	 * 5) Load FIFO with 6, 10, or 12 byte SCSI command
-	 * 6) Issue SELECTION_WITHOUT_ATTENTION command
-	 *
-	 * They also mention that a DMA NOP command must be issued
-	 * to the SCSI chip under many circumstances, plus it's
-	 * also a good idea to flush out the fifo just in case.
+	SDptr = SCptr->device;
+	lun = SCptr->lun;
+	target = SCptr->target;
+
+	esp->snip = 0;
+	esp->msgout_len = 0;
+
+	/* Send it out whole, or piece by piece?   The ESP
+	 * only knows how to automatically send out 6, 10,
+	 * and 12 byte commands.  I used to think that the
+	 * Linux SCSI code would never throw anything other
+	 * than that to us, but then again there is the
+	 * SCSI generic driver which can send us anything.
 	 */
+	esp_check_cmd(esp, SCptr);
 
-	/* Load zeros into COUNTER via 2 DMA NOP chip commands
-	 * due to flaky implementations of the 53C9x which don't
-	 * get the idea the first time around.
+	/* If arbitration/selection is successful, the ESP will leave
+	 * ATN asserted, causing the target to go into message out
+	 * phase.  The ESP will feed the target the identify and then
+	 * the target can only legally go to one of command,
+	 * datain/out, status, or message in phase, or stay in message
+	 * out phase (should we be trying to send a sync negotiation
+	 * message after the identify).  It is not allowed to drop
+	 * BSY, but some buggy targets do and we check for this
+	 * condition in the selection complete code.  Most of the time
+	 * we'll make the command bytes available to the ESP and it
+	 * will not interrupt us until it finishes command phase, we
+	 * cannot do this for command sizes the ESP does not
+	 * understand and in this case we'll get interrupted right
+	 * when the target goes into command phase.
+	 *
+	 * It is absolutely _illegal_ in the presence of SCSI-2 devices
+	 * to use the ESP select w/o ATN command.  When SCSI-2 devices are
+	 * present on the bus we _must_ always go straight to message out
+	 * phase with an identify message for the target.  Being that
+	 * selection attempts in SCSI-1 w/o ATN was an option, doing SCSI-2
+	 * selections should not confuse SCSI-1 we hope.
 	 */
-	dregs->cond_reg = (DMA_INT_ENAB | DMA_FIFO_INV);
 
-	eregs->esp_tclow = 0;
-	eregs->esp_tcmed = 0;
-	eregs->esp_cmd   = (ESP_CMD_NULL | ESP_CMD_DMA);
+	if(SDptr->sync) {
+		/* this targets sync is known */
+		if(SDptr->disconnect)
+			*cmdp++ = IDENTIFY(1, lun);
+		else
+			*cmdp++ = IDENTIFY(0, lun);
+
+		if(esp->esp_slowcmd) {
+			the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA);
+			esp_advance_phase(SCptr, in_slct_stop);
+		} else {
+			the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA);
+			esp_advance_phase(SCptr, in_slct_norm);
+		}
+	} else if(!(esp->targets_present & (1<<target)) || !(SDptr->disconnect)) {
+		/* After the bootup SCSI code sends both the
+		 * TEST_UNIT_READY and INQUIRY commands we want
+		 * to at least attempt allowing the device to
+		 * disconnect.
+		 */
+		ESPMISC(("esp: Selecting device for first time. target=%d "
+			 "lun=%d\n", target, SCptr->lun));
+		if(!SDptr->borken && !SDptr->disconnect)
+			SDptr->disconnect = 1;
+
+		*cmdp++ = IDENTIFY(0, lun);
+		esp->prevmsgout = NOP;
+		esp_advance_phase(SCptr, in_slct_norm);
+		the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA);
+
+		/* Take no chances... */
+		SDptr->sync_max_offset = 0;
+		SDptr->sync_min_period = 0;
+	} else {
+		int toshiba_cdrom_hwbug_wkaround = 0;
 
-	/* Flush the fifo of excess garbage. */
-	eregs->esp_cmd   = ESP_CMD_FLUSH;
+		/* We've talked to this guy before,
+		 * but never negotiated.. lets try
+		 */
+
+		if(!SDptr->borken) {
+			if((SDptr->type == TYPE_ROM) &&
+			   (!strncmp(SDptr->vendor, "TOSHIBA", 7))) {
+				/* Nice try sucker... */
+				printk("esp%d: Disabling sync for buggy "
+				       "Toshiba CDROM.\n", esp->esp_id);
+				toshiba_cdrom_hwbug_wkaround = 1;
+				build_sync_nego_msg(esp, 0, 0);
+			} else {
+				build_sync_nego_msg(esp, esp->sync_defp, 15);
+			}
+		} else {
+			build_sync_nego_msg(esp, 0, 0);
+		}
+		SDptr->sync = 1;
+		esp->snip = 1;
 
-	/* Load bus-id and timeout values. */
-	eregs->esp_busid = (SCptr->target & 7);
-	eregs->esp_timeo = esp->sync_defp;
+		/* A fix for broken SCSI1 targets, when they disconnect
+		 * they lock up the bus and confuse ESP.  So disallow
+		 * disconnects for SCSI1 targets for now until we
+		 * find a better fix.
+		 *
+		 * Addendum: This is funny, I figured out what was going
+		 *           on.  The blotzed SCSI1 target would disconnect,
+		 *           one of the other SCSI2 targets or both would be
+		 *           disconnected as well.  The SCSI1 target would
+		 *           stay disconnected long enough that we start
+		 *           up a command on one of the SCSI2 targets.  As
+		 *           the ESP is arbitrating for the bus the SCSI1
+		 *           target begins to arbitrate as well to reselect
+		 *           the ESP.  The SCSI1 target refuses to drop it's
+		 *           ID bit on the data bus even though the ESP is
+		 *           at ID 7 and is the obvious winner for any
+		 *           arbitration.  The ESP is a poor sport and refuses
+		 *           to lose arbitration, it will continue indefinately
+		 *           trying to arbitrate for the bus and can only be
+		 *           stopped via a chip reset or SCSI bus reset.
+		 *           Therefore _no_ disconnects for SCSI1 targets
+		 *           thank you very much. ;-)
+		 */
+		if((SDptr->scsi_level < SCSI_2) || toshiba_cdrom_hwbug_wkaround ||
+		   SDptr->borken) {
+			printk("esp%d: Disabling DISCONNECT for target %d lun %d\n",
+			       esp->esp_id, SCptr->target, SCptr->lun);
+			SDptr->disconnect = 0;
+			*cmdp++ = IDENTIFY(0, lun);
+		} else {
+			*cmdp++ = IDENTIFY(1, lun);
+		}
 
-	eregs->esp_soff  = 0; /* This means async transfer... */
-	eregs->esp_stp   = 0;
+		/* ESP fifo is only so big...
+		 * Make this look like a slow command.
+		 */
+		esp->esp_slowcmd = 1;
+		esp->esp_scmdleft = SCptr->cmd_len;
+		esp->esp_scmdp = &SCptr->cmnd[0];
 
-	/* Load FIFO with the actual SCSI command. */
-	for(i=0; i < SCptr->cmd_len; i++)
-		eregs->esp_fdata = SCptr->cmnd[i];
+		the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA);
+		esp_advance_phase(SCptr, in_slct_msg);
+	}
 
-	/* Make sure the dvma forwards the ESP interrupt. */
-	dregs->cond_reg = DMA_INT_ENAB;
+	if(!esp->esp_slowcmd)
+		for(i = 0; i < SCptr->cmd_len; i++)
+			*cmdp++ = SCptr->cmnd[i];
+
+	eregs->esp_busid = (target & 7);
+	eregs->esp_soff = SDptr->sync_max_offset;
+	eregs->esp_stp  = SDptr->sync_min_period;
+	if(esp->erev > esp100a)
+		eregs->esp_cfg3 = esp->config3[target];
 
-	/* Tell ESP to SELECT without asserting ATN. */
-	eregs->esp_cmd = ESP_CMD_SEL;
-	return;
+	i = (cmdp - esp->esp_command);
+
+	/* Set up the DMA and ESP counters */
+	eregs->esp_tclow = i;
+	eregs->esp_tcmed = 0;
 
-bad:
-	panic("esp: daaarrrkk starrr crashesss....");
+	esp->dma_init_write(esp, (char *) esp->esp_command, i);
+
+	/* Tell ESP to "go". */
+	esp_cmd(esp, eregs, the_esp_command);
 }
 
 /* Queue a SCSI command delivered from the mid-level Linux SCSI code. */
 int esp_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
 {
 	struct Sparc_ESP *esp;
+	int don;
 	unsigned long flags;
 
 	save_flags(flags); cli();
@@ -637,40 +981,70 @@
 
 	esp = (struct Sparc_ESP *) SCpnt->host->hostdata;
 
+	if(esp->dma_led_on)
+		esp->dma_led_on(esp);
+
+	don = esp->dma_ports_p(esp);
+	if(don)
+		esp->dma_ints_off(esp);
+
 	/* We use the scratch area. */
+	ESPQUEUE(("esp_queue: target=%d lun=%d ", SCpnt->target, SCpnt->lun));
+	ESPDISC(("N<%02x,%02x>", SCpnt->target, SCpnt->lun));
 	if(!SCpnt->use_sg) {
+		ESPQUEUE(("!use_sg\n"));
 		SCpnt->SCp.this_residual    = SCpnt->request_bufflen;
 		SCpnt->SCp.buffer           =
 			(struct scatterlist *) SCpnt->request_buffer;
 		SCpnt->SCp.buffers_residual = 0;
-		SCpnt->SCp.Status           = CHECK_CONDITION;
-		SCpnt->SCp.Message          = 0;
-		SCpnt->SCp.have_data_in     = 0;
-		SCpnt->SCp.sent_command     = 0;
-		SCpnt->SCp.ptr = mmu_get_scsi_one((char *)SCpnt->SCp.buffer,
-						     SCpnt->SCp.this_residual,
-						     esp->edev->my_bus);
+#ifdef CONFIG_SCSI_SUNESP
+		/* Sneaky. */
+		SCpnt->SCp.have_data_in = (int) SCpnt->SCp.ptr =
+			mmu_get_scsi_one((char *)SCpnt->SCp.buffer,
+					 SCpnt->SCp.this_residual,
+					 ((struct linux_sbus_device *) (esp->edev))->my_bus);
+#else
+		SCpnt->SCp.have_data_in = (int) SCpnt->SCp.ptr =
+			(char *)SCpnt->request_buffer;
+#endif
+
 	} else {
+		ESPQUEUE(("use_sg "));
 #ifdef DEBUG_ESP_SG
-		printk("esp: sglist at %p with %d buffers\n",
-		       SCpnt->buffer, SCpnt->use_sg);
+		printk("esp%d: sglist at %p with %d buffers\n",
+		       esp->esp_id, SCpnt->buffer, SCpnt->use_sg);
 #endif
 		SCpnt->SCp.buffer           = (struct scatterlist *) SCpnt->buffer;
 		SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
 		SCpnt->SCp.this_residual    = SCpnt->SCp.buffer->length;
+#ifdef CONFIG_SCSI_SUNESP
 		mmu_get_scsi_sgl((struct mmu_sglist *) SCpnt->SCp.buffer,
 				 SCpnt->SCp.buffers_residual,
-				 esp->edev->my_bus);
-		SCpnt->SCp.ptr              = (char *) SCpnt->SCp.buffer->alt_address;
+				 ((struct linux_sbus_device *) (esp->edev))->my_bus);
+		SCpnt->SCp.ptr              = (char *) SCpnt->SCp.buffer->dvma_address;
+#else
+		SCpnt->SCp.ptr              = (char *) SCpnt->SCp.buffer->address;
+#endif
 	}
+	SCpnt->SCp.Status           = CHECK_CONDITION;
+	SCpnt->SCp.Message          = 0xff;
+	SCpnt->SCp.sent_command     = 0;
 
 	/* Place into our queue. */
-	append_SC(&esp->issue_SC, SCpnt);
+	if(SCpnt->cmnd[0] == REQUEST_SENSE) {
+		ESPQUEUE(("RQSENSE\n"));
+		prepend_SC(&esp->issue_SC, SCpnt);
+	} else {
+		ESPQUEUE(("\n"));
+		append_SC(&esp->issue_SC, SCpnt);
+	}
 
-	/* Run it now if we can */
-	if(!esp->current_SC)
+	/* Run it now if we can. */
+	if(!esp->current_SC && !esp->resetting_bus)
 		esp_exec_cmd(esp);
 
+	if(don)
+		esp->dma_ints_on(esp);
 	restore_flags(flags);
 	return 0;
 }
@@ -678,17 +1052,152 @@
 /* Only queuing supported in this ESP driver. */
 int esp_command(Scsi_Cmnd *SCpnt)
 {
-	ESPLOG(("esp: esp_command() called...\n"));
+#ifdef DEBUG_ESP
+	struct Sparc_ESP *esp = (struct Sparc_ESP *) SCpnt->host->hostdata;
+#endif
+
+	ESPLOG(("esp%d: esp_command() called...\n", esp->esp_id));
 	return -1;
 }
 
-/* Abort a command.  Those that are on the bus force a SCSI bus
- * reset.
- */
-int esp_abort(Scsi_Cmnd *SCpnt)
+/* Dump driver state. */
+static inline void esp_dump_cmd(Scsi_Cmnd *SCptr)
+{
+	ESPLOG(("[tgt<%02x> lun<%02x> "
+		"pphase<%s> cphase<%s>]",
+		SCptr->target, SCptr->lun,
+		phase_string(SCptr->SCp.sent_command),
+		phase_string(SCptr->SCp.phase)));
+}
+
+static inline void esp_dump_state(struct Sparc_ESP *esp, 
+				  struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+#ifdef DEBUG_ESP_CMDS
+	int i;
+#endif
+
+	ESPLOG(("esp%d: dumping state\n", esp->esp_id));
+	
+	/* Print DMA status */
+	esp->dma_dump_state(esp);
+
+	ESPLOG(("esp%d: SW [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
+		esp->esp_id, esp->sreg, esp->seqreg, esp->ireg));
+	ESPLOG(("esp%d: HW reread [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
+		esp->esp_id, eregs->esp_status, eregs->esp_sstep, eregs->esp_intrpt));
+#ifdef DEBUG_ESP_CMDS
+	printk("esp%d: last ESP cmds [", esp->esp_id);
+	i = (esp->espcmdent - 1) & 31;
+	printk("<");
+	esp_print_cmd(esp->espcmdlog[i]);
+	printk(">");
+	i = (i - 1) & 31;
+	printk("<");
+	esp_print_cmd(esp->espcmdlog[i]);
+	printk(">");
+	i = (i - 1) & 31;
+	printk("<");
+	esp_print_cmd(esp->espcmdlog[i]);
+	printk(">");
+	i = (i - 1) & 31;
+	printk("<");
+	esp_print_cmd(esp->espcmdlog[i]);
+	printk(">");
+	printk("]\n");
+#endif /* (DEBUG_ESP_CMDS) */
+
+	if(SCptr) {
+		ESPLOG(("esp%d: current command ", esp->esp_id));
+		esp_dump_cmd(SCptr);
+	}
+	ESPLOG(("\n"));
+	SCptr = esp->disconnected_SC;
+	ESPLOG(("esp%d: disconnected ", esp->esp_id));
+	while(SCptr) {
+		esp_dump_cmd(SCptr);
+		SCptr = (Scsi_Cmnd *) SCptr->host_scribble;
+	}
+	ESPLOG(("\n"));
+}
+
+/* Abort a command. */
+int esp_abort(Scsi_Cmnd *SCptr)
 {
-	ESPLOG(("esp_abort: Not implemented yet\n"));
-	return SCSI_ABORT_ERROR;
+	struct Sparc_ESP *esp = (struct Sparc_ESP *) SCptr->host->hostdata;
+	struct ESP_regs *eregs = esp->eregs;
+	int don;
+	unsigned long flags;
+
+	ESPLOG(("esp%d: Aborting command\n", esp->esp_id));
+	save_flags(flags); cli();
+
+	esp_dump_state(esp, eregs);
+
+	/* Wheee, if this is the current command on the bus, the
+	 * best we can do is assert ATN and wait for msgout phase.
+	 * This should even fix a hung SCSI bus when we lose state
+	 * in the driver and timeout because the eventual phase change
+	 * will cause the ESP to (eventually) give an interrupt.
+	 */
+	if(esp->current_SC == SCptr) {
+		esp->cur_msgout[0] = ABORT;
+		esp->msgout_len = 1;
+		esp->msgout_ctr = 0;
+		esp_cmd(esp, eregs, ESP_CMD_SATN);
+		restore_flags(flags);
+		return SCSI_ABORT_PENDING;
+	}
+
+	/* If it is still in the issue queue then we can safely
+	 * call the completion routine and report abort success.
+	 */
+	don = esp->dma_ports_p(esp);
+	if(don)
+		esp->dma_ints_off(esp);
+	if(esp->issue_SC) {
+		Scsi_Cmnd **prev, *this;
+		for(prev = (&esp->issue_SC), this = esp->issue_SC;
+		    this;
+		    prev = (Scsi_Cmnd **) &(this->host_scribble),
+		    this = (Scsi_Cmnd *) this->host_scribble) {
+			if(this == SCptr) {
+				*prev = (Scsi_Cmnd *) this->host_scribble;
+				this->host_scribble = NULL;
+				this->result = DID_ABORT << 16;
+				this->done(this);
+				cli();
+				if(don)
+					esp->dma_ints_on(esp);
+				restore_flags(flags);
+				return SCSI_ABORT_SUCCESS;
+			}
+		}
+	}
+
+	/* Yuck, the command to abort is disconnected, it is not
+	 * worth trying to abort it now if something else is live
+	 * on the bus at this time.  So, we let the SCSI code wait
+	 * a little bit and try again later.
+	 */
+	if(esp->current_SC) {
+		if(don)
+			esp->dma_ints_on(esp);
+		restore_flags(flags);
+		return SCSI_ABORT_SNOOZE;
+	}
+
+	/* It's disconnected, we have to reconnect to re-establish
+	 * the nexus and tell the device to abort.
+	 *
+	 * XXX write this code.
+	 */
+
+	if(don)
+		esp->dma_ints_on(esp);
+	restore_flags(flags);
+	return SCSI_ABORT_SNOOZE;
 }
 
 /* Reset ESP chip, reset hanging bus, then kill active and
@@ -696,8 +1205,13 @@
  */
 int esp_reset(Scsi_Cmnd *SCptr, unsigned int how)
 {
-	ESPLOG(("esp_reset: Not implemented yet\n"));
-	return SCSI_RESET_ERROR;
+	struct Sparc_ESP *esp = (struct Sparc_ESP *) SCptr->host->hostdata;
+	struct ESP_regs *eregs = esp->eregs;
+
+	ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id));
+	esp->resetting_bus = 1;
+	esp_cmd(esp, eregs, ESP_CMD_RS);
+	return SCSI_RESET_PENDING;
 }
 
 /* Internal ESP done function. */
@@ -709,39 +1223,62 @@
 	if(esp->current_SC) {
 		/* Critical section... */
 		save_flags(flags); cli();
+
 		done_SC = esp->current_SC;
 		esp->current_SC = NULL;
+
 		/* Free dvma entry. */
 		if(!done_SC->use_sg) {
-			mmu_release_scsi_one(done_SC->SCp.ptr,
-						done_SC->SCp.this_residual,
-						esp->edev->my_bus);
+#ifdef CONFIG_SCSI_SUNESP
+			/* Sneaky. */
+			mmu_release_scsi_one((char *)done_SC->SCp.have_data_in,
+					     done_SC->request_bufflen,
+					     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+#endif
 		} else {
-			struct scatterlist *scl = (struct scatterlist *)done_SC->buffer;
 #ifdef DEBUG_ESP_SG
-			printk("esp: unmapping sg ");
+			printk("esp%d: unmapping sg ", esp->esp_id);
 #endif
+#ifdef CONFIG_SCSI_SUNESP
+			struct scatterlist *scl = (struct scatterlist *)done_SC->buffer;
 			mmu_release_scsi_sgl((struct mmu_sglist *) scl,
 					     done_SC->use_sg - 1,
-					     esp->edev->my_bus);
+					     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+#endif
 #ifdef DEBUG_ESP_SG
 			printk("done.\n");
 #endif
 		}
+
 		done_SC->result = error;
 		if(done_SC->scsi_done)
 			done_SC->scsi_done(done_SC);
 		else
 			panic("esp: esp->current_SC->scsi_done() == NULL");
 
+		/* There is a window of time within the scsi_done() path
+		 * of execution where interrupts are turned back on full
+		 * blast and left that way.  During that time we could
+		 * reconnect to a disconnected command, then we'd bomb
+		 * out below.  We could also end up executing two commands
+		 * at _once_.  ...just so you know why the cli() is here...
+		 */
+
+		cli(); /* GRRR!!! */
+
 		/* Bus is free, issue any commands in the queue. */
-		if(esp->issue_SC)
+		if(esp->issue_SC && !esp->current_SC)
 			esp_exec_cmd(esp);
 
 		restore_flags(flags);
 		/* End of critical section... */
-	} else
+	} else {
+		/* Panic is safe as current_SC is null so we may still
+		 * be able to accept more commands to sync buffers.
+		 */
+		ESPLOG(("panicing\n"));
 		panic("esp: done() called with NULL esp->current_SC");
+	}
 }
 
 #ifdef THREADED_ESP_DRIVER /* planning stage... */
@@ -771,358 +1308,1872 @@
 }
 #endif
 
-/* Read the interrupt status registers on this ESP board */
-static inline void esp_updatesoft(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs)
-{
-	/* Update our software copies of the three ESP status
-	 * registers for this ESP.  Be careful, reading the
-	 * ESP interrupt register clears the status and sequence
-	 * step registers (unlatches them, you get the idea).
-	 * So read the interrupt register last.
-	 */
-
-	esp->seqreg = eregs->esp_sstep;
-	esp->sreg = eregs->esp_status;
-
-	/* Supposedly, the ESP100A and above assert the highest
-	 * bit in the status register if an interrupt is pending.
-	 * I've never seen this work properly, so let's clear it
-	 * manually while we are here.  If I see any esp chips
-	 * for which this bit is reliable I will conditionalize
-	 * this.  However, I don't see what this extra bit can
-	 * buy me with all the tests I'll have to place all over
-	 * the code to actually use it when I 'can'.  Plus the
-	 * 'pending interrupt' condition can more than reliably
-	 * be obtained from the DVMA control register.
-	 *
-	 * "Broken hardware"  -Linus
-	 */
-	esp->sreg &= (~ESP_STAT_INTR);
-	esp->ireg = eregs->esp_intrpt;   /* Must be last or we lose */
-}
+/* Wheee, ESP interrupt engine. */  
 
-/* #define ESP_IRQ_TRACE */
+enum {
+	do_phase_determine, do_reset_bus, do_reset_complete,
+	do_work_bus, do_intr_end,
+};
 
-#ifdef ESP_IRQ_TRACE
-#define ETRACE(foo)  printk foo
-#else
-#define ETRACE(foo)
-#endif
+/* Forward declarations. */
+static int esp_do_phase_determine(struct Sparc_ESP *esp, 
+				  struct ESP_regs *eregs);
+static int esp_do_data_finale(struct Sparc_ESP *esp, struct ESP_regs *eregs);
+static int esp_select_complete(struct Sparc_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_status(struct Sparc_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_msgin(struct Sparc_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_msgindone(struct Sparc_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_msgout(struct Sparc_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_cmdbegin(struct Sparc_ESP *esp, struct ESP_regs *eregs);
 
-static char last_fflags, last_status, last_msg;
 
-/* Main interrupt handler for an esp adapter. */
-static inline void esp_handle(struct Sparc_ESP *esp)
+/* We try to avoid some interrupts by jumping ahead and see if the ESP
+ * has gotten far enough yet.  Hence the following.
+ */
+static inline int skipahead1(struct Sparc_ESP *esp, struct ESP_regs *eregs,
+			     Scsi_Cmnd *scp, int prev_phase, int new_phase)
 {
-	struct sparc_dma_registers *dregs;
-	struct Sparc_ESP_regs *eregs;
-	Scsi_Cmnd *SCptr;
+	if(scp->SCp.sent_command != prev_phase)
+		return 0;
 
-	eregs = esp->eregs;
-	dregs = esp->dregs;
-	SCptr = esp->current_SC;
+	if(esp->dma_irq_p(esp)) {
+		/* Yes, we are able to save an interrupt. */
+		esp->sreg = eregs->esp_status;
+		esp->ireg = eregs->esp_intrpt;
+
+		esp->sreg &= ~(ESP_STAT_INTR);
+		if(!(esp->ireg & ESP_INTR_SR))
+			return 0;
+		else
+			return do_reset_complete;
+	}
+	/* Ho hum, target is taking forever... */
+	scp->SCp.sent_command = new_phase; /* so we don't recurse... */
+	return do_intr_end;
+}
 
-	DMA_IRQ_ENTRY(esp->dma, dregs);
-	esp_updatesoft(esp, eregs);
+static inline int skipahead2(struct Sparc_ESP *esp,
+			     struct ESP_regs *eregs,
+			     Scsi_Cmnd *scp, int prev_phase1, int prev_phase2,
+			     int new_phase)
+{
+	if(scp->SCp.sent_command != prev_phase1 &&
+	   scp->SCp.sent_command != prev_phase2)
+		return 0;
+	if(esp->dma_irq_p(esp)) {
+		/* Yes, we are able to save an interrupt. */
+		esp->sreg = eregs->esp_status;
+		esp->ireg = eregs->esp_intrpt;
+
+		esp->sreg &= ~(ESP_STAT_INTR);
+		if(!(esp->ireg & ESP_INTR_SR))
+			return 0;
+		else
+			return do_reset_complete;
+	}
+	/* Ho hum, target is taking forever... */
+	scp->SCp.sent_command = new_phase; /* so we don't recurse... */
+	return do_intr_end;
+}
 
-	ETRACE(("ESPIRQ: <%2x,%2x,%2x> --> ", esp->ireg, esp->sreg, esp->seqreg));
+/* Misc. esp helper routines. */
+static inline void esp_setcount(struct ESP_regs *eregs, int cnt)
+{
+	eregs->esp_tclow = (cnt & 0xff);
+	eregs->esp_tcmed = ((cnt >> 8) & 0xff);
+}
 
-	/* Check for errors. */
-	if(!SCptr)
-		panic("esp_handle: current_SC == penguin within interrupt!");
+static inline int esp_getcount(struct ESP_regs *eregs)
+{
+	return (((eregs->esp_tclow)&0xff) |
+		(((eregs->esp_tcmed)&0xff) << 8));
+}
+
+static inline int fcount(struct ESP_regs *eregs)
+{
+	return eregs->esp_fflags & ESP_FF_FBYTES;
+}
+
+static inline int fnzero(struct ESP_regs *eregs)
+{
+	return eregs->esp_fflags & ESP_FF_ONOTZERO;
+}
+
+/* XXX speculative nops unnecessary when continuing amidst a data phase
+ * XXX even on esp100!!!  another case of flooding the bus with I/O reg
+ * XXX writes...
+ */
+static inline void esp_maybe_nop(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	if(esp->erev == esp100)
+		esp_cmd(esp, eregs, ESP_CMD_NULL);
+}
+
+static inline int sreg_to_dataphase(unchar sreg)
+{
+	if((sreg & ESP_STAT_PMASK) == ESP_DOP)
+		return in_dataout;
+	else
+		return in_datain;
+}
+
+static inline int sreg_datainp(unchar sreg)
+{
+	return (sreg & ESP_STAT_PMASK) == ESP_DIP;
+}
+
+static inline int sreg_dataoutp(unchar sreg)
+{
+	return (sreg & ESP_STAT_PMASK) == ESP_DOP;
+}
+
+/* The ESP100 when in synchronous data phase, can mistake a long final
+ * REQ pulse from the target as an extra byte, it places whatever is on
+ * the data lines into the fifo.  For now, we will assume when this
+ * happens is that the target is a bit quirky and we don't want to
+ * be talking synchronously to it anyways.  Regardless, we need to
+ * tell the ESP to eat the extraneous byte so that we can proceed
+ * to the next phase.
+ */
+static inline int esp100_sync_hwbug(struct Sparc_ESP *esp,
+				    struct ESP_regs *eregs, Scsi_Cmnd *sp)
+{
+	/* Do not touch this piece of code. */
+	if((!(esp->erev == esp100)) ||
+	   (!(sreg_datainp((esp->sreg = eregs->esp_status)) && !fcount(eregs)) &&
+	    !(sreg_dataoutp(esp->sreg) && !fnzero(eregs)))) {
+		if(sp->SCp.phase == in_dataout)
+			esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+		return 0;
+	} else {
+		/* Async mode for this guy. */
+		build_sync_nego_msg(esp, 0, 0);
+
+		/* Ack the bogus byte, but set ATN first. */
+		esp_cmd(esp, eregs, ESP_CMD_SATN);
+		esp_cmd(esp, eregs, ESP_CMD_MOK);
+
+		/* We may have to sit around for a bit. */
+		esp_advance_phase(sp, in_the_dark);
+		return 1;
+	}
+}
+
+/* This closes the window during a selection with a reselect pending, because
+ * we use DMA for the selection process the FIFO should hold the correct
+ * contents if we get reselected during this process.  So we just need to
+ * ack the possible illegal cmd interrupt pending on the esp100.
+ */
+static inline int esp100_reconnect_hwbug(struct Sparc_ESP *esp,
+					 struct ESP_regs *eregs)
+{
+	volatile unchar junk;
+
+	if(esp->erev != esp100)
+		return 0;
+	junk = eregs->esp_intrpt;
+
+	if(junk & ESP_INTR_SR)
+		return 1;
+	return 0;
+}
+
+/* This verifies the BUSID bits during a reselection so that we know which
+ * target is talking to us.
+ */
+static inline int reconnect_target(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	int it, me = esp->scsi_id_mask, targ = 0;
+
+	if(2 != fcount(eregs))
+		return -1;
+	it = eregs->esp_fdata;
+	if(!(it & me))
+		return -1;
+	it &= ~me;
+	if(it & (it - 1))
+		return -1;
+	while(!(it & 1))
+		targ++, it >>= 1;
+	return targ;
+}
+
+/* This verifies the identify from the target so that we know which lun is
+ * being reconnected.
+ */
+static inline int reconnect_lun(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	int lun;
+
+	if((esp->sreg & ESP_STAT_PMASK) != ESP_MIP)
+		return -1;
+	lun = eregs->esp_fdata;
+	if(esp->sreg & ESP_STAT_PERR)
+		return 0;
+	if((lun & 0x40) || !(lun & 0x80))
+		return -1;
+	return lun & 7;
+}
+
+/* This puts the driver in a state where it can continue a command that
+ * is being continued via reselection.
+ */
+static inline void esp_connect(struct Sparc_ESP *esp, struct ESP_regs *eregs,
+			       Scsi_Cmnd *sp)
+{
+	Scsi_Device *dp = sp->device;
+	eregs->esp_soff = dp->sync_max_offset;
+	eregs->esp_stp  = dp->sync_min_period;
+	if(esp->erev > esp100a)
+		eregs->esp_cfg3 = esp->config3[sp->target];
+	esp->current_SC = sp;
+}
+
+/* This will place the current working command back into the issue queue
+ * if we are to receive a reselection amidst a selection attempt.
+ */
+static inline void esp_reconnect(struct Sparc_ESP *esp, Scsi_Cmnd *sp)
+{
+	if(!esp->disconnected_SC)
+		printk("esp%d: Weird, being reselected but disconnected "
+		       "command queue is empty.\n", esp->esp_id);
+	esp->snip = 0;
+	esp->current_SC = 0;
+	sp->SCp.phase = not_issued;
+	append_SC(&esp->issue_SC, sp);
+}
+
+/* Begin message in phase. */
+static inline int esp_do_msgin(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+	esp_maybe_nop(esp, eregs);
+	esp_cmd(esp, eregs, ESP_CMD_TI);
+	esp->msgin_len = 1;
+	esp->msgin_ctr = 0;
+	esp_advance_phase(esp->current_SC, in_msgindone);
+	return do_work_bus;
+}
+
+/* Determine how many bytes were successfully sent/received by the ESP.
+ */
+static inline void advance_sg(Scsi_Cmnd *sp)
+{
+	++sp->SCp.buffer;
+	--sp->SCp.buffers_residual;
+	sp->SCp.this_residual = sp->SCp.buffer->length;
+#ifdef CONFIG_SCSI_SUNESP
+	sp->SCp.ptr = sp->SCp.buffer->dvma_address;
+#else
+	sp->SCp.ptr = sp->SCp.buffer->address;
+#endif
+}
+
+/* Complete a command, this is the only way to safely finish
+ * a command with disconnected commands or a reset interrupt
+ * pending because scsi_done() can enable interrupts full blast.
+ */
+static inline void safe_esp_done(struct Sparc_ESP *esp, int error)
+{
+	int before = 0;
 
-	/* At this point in time, this esp driver should not see
-	 * scsibus resets, parity errors, or gross errors unless
-	 * something truly terrible happens which we are not ready
-	 * to properly recover from yet.
+	if(esp->dma_ports_p(esp)){
+		before = 1;
+		esp->dma_ints_off(esp);
+	}
+	esp_done(esp, error);
+	if(before)
+		esp->dma_ints_on(esp);
+}
+
+/* Please note that the way I've coded these routines is that I _always_
+ * check for a disconnect during any and all information transfer
+ * phases.  The SCSI standard states that the target _can_ cause a BUS
+ * FREE condition by dropping all MSG/CD/IO/BSY signals.  Also note
+ * that during information transfer phases the target controls every
+ * change in phase, the only thing the initiator can do is "ask" for
+ * a message out phase by driving ATN true.  The target can, and sometimes
+ * will, completely ignore this request so we cannot assume anything when
+ * we try to force a message out phase to abort/reset a target.  Most of
+ * the time the target will eventually be nice and go to message out, so
+ * we may have to hold on to our state about what we want to tell the target
+ * for some period of time.
+ */
+
+/* I think I have things working here correctly.  Even partial transfers
+ * within a buffer or sub-buffer should not upset us at all no matter
+ * how bad the target and/or ESP fucks things up.
+ */
+
+static inline int esp_do_data(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int thisphase, hmuch;
+
+	ESPDATA(("esp_do_data: "));
+	esp_maybe_nop(esp, eregs);
+	thisphase = sreg_to_dataphase(esp->sreg);
+	esp_advance_phase(SCptr, thisphase);
+	ESPDATA(("newphase<%s> ", (thisphase == in_datain) ? "DATAIN" : "DATAOUT"));
+	hmuch = esp->dma_can_transfer(esp, SCptr);
+	ESPDATA(("hmuch<%d> ", hmuch));
+	esp_setcount(eregs, hmuch);
+
+	esp->dma_setup(esp, SCptr->SCp.ptr, hmuch, (thisphase == in_datain));
+	esp->current_transfer_size = hmuch;
+	ESPDATA(("DMA|TI --> do_intr_end\n"));
+	esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+	return do_intr_end;
+}
+
+/* See how successful the data transfer was. */
+static inline int esp_do_data_finale(struct Sparc_ESP *esp,
+				     struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int bogus_data = 0, bytes_sent = 0, fifocnt, ecount;
+
+	if(esp->dma_led_off)
+		esp->dma_led_off(esp);
+
+	ESPDATA(("esp_do_data_finale: "));
+
+	if(SCptr->SCp.phase == in_datain) {
+		if(esp->sreg & ESP_STAT_PERR) {
+			/* Yuck, parity error.  The ESP asserts ATN
+			 * so that we can go to message out phase
+			 * immediately and inform the target that
+			 * something bad happened.
+			 */
+			ESPLOG(("esp%d: data bad parity detected.\n",
+				esp->esp_id));
+			esp->cur_msgout[0] = INITIATOR_ERROR;
+			esp->msgout_len = 1;
+		}
+		if(esp->dma_drain)
+			esp->dma_drain(esp);
+	}
+	if(esp->dma_invalidate)
+		esp->dma_invalidate(esp);
+
+	/* This could happen for the above parity error case. */
+	if(!(esp->ireg == ESP_INTR_BSERV)) {
+		/* Please go to msgout phase, please please please... */
+		ESPLOG(("esp%d: !BSERV after data, probably to msgout\n",
+			esp->esp_id));
+		return esp_do_phase_determine(esp, eregs);
+	}	
+
+	/* Check for partial transfers and other horrible events. */
+	fifocnt = fcount(eregs);
+	ecount = esp_getcount(eregs);
+	bytes_sent = esp->current_transfer_size;
+	if(!(esp->sreg & ESP_STAT_TCNT))
+		bytes_sent -= ecount;
+	if(SCptr->SCp.phase == in_dataout)
+		bytes_sent -= fifocnt;
+
+	ESPDATA(("bytes_sent=%d, ", bytes_sent));
+
+	/* If we were in synchronous mode, check for peculiarities. */
+	if(SCptr->device->sync_max_offset)
+		bogus_data = esp100_sync_hwbug(esp, eregs, SCptr);
+	else
+		esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+
+	/* Until we are sure of what has happened, we are certainly
+	 * in the dark.
 	 */
-	if((esp->ireg & (ESP_INTR_SR | ESP_INTR_IC)) ||
-	   (esp->sreg & (ESP_STAT_PERR | ESP_STAT_SPAM))) {
-		printk("esp: really bad error detected\n");
-		printk("esp: intr<%2x> stat<%2x> seq<%2x>",
-		       esp->ireg, esp->sreg, esp->seqreg);
-		printk("esp: SCptr->SCp.phase = %d\n", SCptr->SCp.phase);
-		panic("esp: cannot continue\n");
+	esp_advance_phase(SCptr, in_the_dark);
+
+	if(bytes_sent < 0) {
+		/* I've seen this happen due to lost state in this
+		 * driver.  No idea why it happened, but allowing
+		 * this value to be negative caused things to
+		 * lock up.  This allows greater chance of recovery.
+		 */
+		ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id));
+		ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n",
+			esp->esp_id,
+			esp->current_transfer_size, fifocnt, ecount));
+		ESPLOG(("esp%d: use_sg=%d ptr=%p this_residual=%d\n",
+			esp->esp_id,
+			SCptr->use_sg, SCptr->SCp.ptr, SCptr->SCp.this_residual));
+		bytes_sent = 0;
 	}
-	if(dregs->cond_reg & DMA_HNDL_ERROR) {
-		printk("esp: DMA shows an error cond_reg<%08lx> addr<%p>\n",
-		       dregs->cond_reg, dregs->st_addr);
-		printk("esp: intr<%2x> stat<%2x> seq<%2x>",
-		       esp->ireg, esp->sreg, esp->seqreg);
-		printk("esp: SCptr->SCp.phase = %d\n", SCptr->SCp.phase);
-		panic("esp: cannot continue\n");
-	}
-	if(esp->sreg & ESP_STAT_PERR) {
-		printk("esp: SCSI bus parity error\n");
-		printk("esp: intr<%2x> stat<%2x> seq<%2x>",
-		       esp->ireg, esp->sreg, esp->seqreg);
-		printk("esp: SCptr->SCp.phase = %d\n", SCptr->SCp.phase);
-		panic("esp: cannot continue\n");
+
+	/* Update the state of our transfer. */
+	SCptr->SCp.ptr += bytes_sent;
+	SCptr->SCp.this_residual -= bytes_sent;
+	if(SCptr->SCp.this_residual < 0) {
+		/* shit */
+		printk("esp%d: Data transfer overrun (%d).\n", esp->esp_id, SCptr->SCp.this_residual);
+		SCptr->SCp.this_residual = 0;
 	}
 
-	/* Service interrupt. */
-	switch(SCptr->SCp.phase) {
-	case not_issued:
-		panic("Unexpected ESP interrupt, current_SC not issued.");
-		break;
-	case in_selection:
-		if(esp->ireg & ESP_INTR_RSEL) {
-			/* XXX Some day XXX */
-			panic("ESP penguin reselected in async mode.");
-		} else if(esp->ireg & ESP_INTR_DC) {
-			/* Either we are scanning the bus and no-one
-			 * lives at this target or it didn't respond.
+	/* Maybe continue. */
+	if(!bogus_data) {
+		ESPDATA(("!bogus_data, "));
+		/* NO MATTER WHAT, we advance the scatterlist,
+		 * if the target should decide to disconnect
+		 * in between scatter chunks (which is common)
+		 * we could die horribly!  I used have the sg
+		 * advance occur only if we are going back into
+		 * (or are staying in) a data phase, you can
+		 * imagine the hell I went through trying to
+		 * figure this out.
+		 */
+		if(SCptr->use_sg && !SCptr->SCp.this_residual)
+			advance_sg(SCptr);
+		if(sreg_datainp(esp->sreg) || sreg_dataoutp(esp->sreg)) {
+			ESPDATA(("to more data\n"));
+			return esp_do_data(esp, eregs);
+		}
+		ESPDATA(("to new phase\n"));
+		return esp_do_phase_determine(esp, eregs);
+	}
+	/* Bogus data, just wait for next interrupt. */
+	ESPLOG(("esp%d: bogus_data during end of data phase\n",
+		esp->esp_id));
+	return do_intr_end;
+}
+
+/* Either a command is completing or a target is dropping off the bus
+ * to continue the command in the background so we can do other work.
+ */
+static inline int esp_do_freebus(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int rval;
+
+	rval = skipahead2(esp, eregs, SCptr, in_status, in_msgindone, in_freeing);
+	if(rval)
+		return rval;
+
+	if(esp->ireg != ESP_INTR_DC) {
+		ESPLOG(("esp%d: Target will not disconnect\n", esp->esp_id));
+		return do_reset_bus; /* target will not drop BSY... */
+	}
+	esp->msgout_len = 0;
+	esp->prevmsgout = NOP;
+	if(esp->prevmsgin == COMMAND_COMPLETE) {
+		/* Normal end of nexus. */
+		if(esp->disconnected_SC)
+			esp_cmd(esp, eregs, ESP_CMD_ESEL);
+
+		if(SCptr->SCp.Status != GOOD &&
+		   ((1<<SCptr->target) & esp->targets_present) &&
+		   SCptr->device->sync && SCptr->device->sync_max_offset) {
+			/* SCSI standard says that the synchronous capabilities
+			 * should be renegotiated at this point.  Most likely
+			 * we are about to request sense from this target
+			 * in which case we want to avoid using sync
+			 * transfers until we are sure of the current target
+			 * state.
 			 */
-			ETRACE(("DISCONNECT\n"));
-#ifdef THREADED_ESP_DRIVER
-			append_SC(esp->eatme_SC, esp->current_SC);
-			esp->current_SC = 0;
-			wake_up(esp_kernel_thread);
-#else
-			esp_done(esp, (DID_NO_CONNECT << 16));
-#endif
-			goto esp_handle_done;
-		} else if((esp->ireg & (ESP_INTR_FDONE | ESP_INTR_BSERV)) ==
-			  (ESP_INTR_FDONE | ESP_INTR_BSERV)) {
-			/* Selection successful, check the sequence step. */
-			/* XXX I know, I know... add error recovery.  XXX */
-			switch(esp->seqreg & ESP_STEP_VBITS) {
-			case ESP_STEP_NCMD:
-				panic("esp: penguin didn't enter cmd phase.");
-				break;
-			case ESP_STEP_PPC:
-				panic("esp: penguin prematurely changed from cmd phase.");
-				break;
-			case ESP_STEP_FINI:
-				/* At the completion of every command
-				 * or message-out phase, we _must_
-				 * unlatch the fifo-flags register
-				 * with an ESP nop command.
-				 */
-				eregs->esp_cmd = ESP_CMD_NULL;
+			ESPMISC(("esp: Status <%d> for target %d lun %d\n",
+				 SCptr->SCp.Status, SCptr->target, SCptr->lun));
+
+			/* But don't do this when spinning up a disk at
+			 * boot time while we poll for completion as it
+			 * fills up the console with messages.  Also, tapes
+			 * can report not ready many times right after
+			 * loading up a tape.
+			 */
+			if(SCptr->cmnd[0] != START_STOP &&
+			   SCptr->data_cmnd[0] != START_STOP &&
+			   SCptr->cmnd[0] != TEST_UNIT_READY &&
+			   SCptr->data_cmnd[0] != TEST_UNIT_READY &&
+			   !(SCptr->device->type == TYPE_TAPE &&
+			     (SCptr->cmnd[0] == TEST_UNIT_READY ||
+			      SCptr->data_cmnd[0] == TEST_UNIT_READY ||
+			      SCptr->cmnd[0] == MODE_SENSE ||
+			      SCptr->data_cmnd[0] == MODE_SENSE)))
+				SCptr->device->sync = 0;
+		}
+		ESPDISC(("F<%02x,%02x>", SCptr->target, SCptr->lun));
+		safe_esp_done(esp, ((SCptr->SCp.Status & 0xff) |
+				    ((SCptr->SCp.Message & 0xff)<<8) |
+				    (DID_OK << 16)));
+	} else if(esp->prevmsgin == DISCONNECT) {
+		/* Normal disconnect. */
+		esp_cmd(esp, eregs, ESP_CMD_ESEL);
+		ESPDISC(("D<%02x,%02x>", SCptr->target, SCptr->lun));
+		append_SC(&esp->disconnected_SC, SCptr);
+		esp->current_SC = NULL;
+		if(esp->issue_SC)
+			esp_exec_cmd(esp);
+	} else {
+		/* Driver bug, we do not expect a disconnect here
+		 * and should not have advanced the state engine
+		 * to in_freeing.
+		 */
+		ESPLOG(("esp%d: last msg not disc and not cmd cmplt.\n",
+			esp->esp_id));
+		return do_reset_bus;
+	}
+	return do_intr_end;
+}
+
+/* Do the needy when a target tries to reconnect to us. */
+static inline int esp_do_reconnect(struct Sparc_ESP *esp, 
+				   struct ESP_regs *eregs)
+{
+	int lun, target;
+	Scsi_Cmnd *SCptr;
+
+	/* Check for all bogus conditions first. */
+	target = reconnect_target(esp, eregs);
+	if(target < 0) {
+		ESPDISC(("bad bus bits\n"));
+		return do_reset_bus;
+	}
+	lun = reconnect_lun(esp, eregs);
+	if(lun < 0) {
+		ESPDISC(("target=%2x, bad identify msg\n", target));
+		return do_reset_bus;
+	}
 
-				/* Selection/Command sequence completed.  We
-				 * (at least for this driver) will be in
-				 * either one of the data phases or status
-				 * phase, check the status register to find
-				 * out.
+	/* Things look ok... */
+	ESPDISC(("R<%02x,%02x>", target, lun));
+	esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+	if(esp100_reconnect_hwbug(esp, eregs))
+		return do_reset_bus;
+	esp_cmd(esp, eregs, ESP_CMD_NULL);
+	SCptr = remove_SC(&esp->disconnected_SC, (unchar) target, (unchar) lun);
+	if(!SCptr) {
+		Scsi_Cmnd *sp;
+
+		ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n",
+			esp->esp_id));
+		ESPLOG(("QUEUE DUMP\n"));
+		sp = esp->issue_SC;
+		ESPLOG(("esp%d: issue_SC[", esp->esp_id));
+		while(sp) {
+			ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
+			sp = (Scsi_Cmnd *) sp->host_scribble;
+		}
+		ESPLOG(("]\n"));
+		sp = esp->current_SC;
+		ESPLOG(("esp%d: current_SC[", esp->esp_id));
+		while(sp) {
+			ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
+			sp = (Scsi_Cmnd *) sp->host_scribble;
+		}
+		ESPLOG(("]\n"));
+		sp = esp->disconnected_SC;
+		ESPLOG(("esp%d: disconnected_SC[", esp->esp_id));
+		while(sp) {
+			ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
+			sp = (Scsi_Cmnd *) sp->host_scribble;
+		}
+		ESPLOG(("]\n"));
+		return do_reset_bus;
+	}
+	esp_connect(esp, eregs, SCptr);
+	esp_cmd(esp, eregs, ESP_CMD_MOK);
+
+	/* No need for explicit restore pointers operation. */
+	esp->snip = 0;
+	esp_advance_phase(SCptr, in_the_dark);
+	return do_intr_end;
+}
+
+/* End of NEXUS (hopefully), pick up status + message byte then leave if
+ * all goes well.
+ */
+static int esp_do_status(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int intr;
+	int rval;
+
+	rval = skipahead1(esp, eregs, SCptr, in_the_dark, in_status);
+	if(rval)
+		return rval;
+
+	intr = esp->ireg;
+	ESPSTAT(("esp_do_status: "));
+	if(intr != ESP_INTR_DC) {
+		int message_out = 0; /* for parity problems */
+
+		/* Ack the message. */
+		ESPSTAT(("ack msg, "));
+		esp_cmd(esp, eregs, ESP_CMD_MOK);
+
+		if(esp->dma_poll)
+			esp->dma_poll(esp, (unsigned char *) esp->esp_command);
+
+		ESPSTAT(("got something, "));
+		/* ESP chimes in with one of
+		 *
+		 * 1) function done interrupt:
+		 *	both status and message in bytes
+		 *	are available
+		 *
+		 * 2) bus service interrupt:
+		 *	only status byte was acquired
+		 *
+		 * 3) Anything else:
+		 *	can't happen, but we test for it
+		 *	anyways
+		 *
+		 * ALSO: If bad parity was detected on either
+		 *       the status _or_ the message byte then
+		 *       the ESP has asserted ATN on the bus
+		 *       and we must therefore wait for the
+		 *       next phase change.
+		 */
+		if(intr & ESP_INTR_FDONE) {
+			/* We got it all, hallejulia. */
+			ESPSTAT(("got both, "));
+			SCptr->SCp.Status = esp->esp_command[0];
+			SCptr->SCp.Message = esp->esp_command[1];
+			esp->prevmsgin = SCptr->SCp.Message;
+			esp->cur_msgin[0] = SCptr->SCp.Message;
+			if(esp->sreg & ESP_STAT_PERR) {
+				/* There was bad parity for the
+				 * message byte, the status byte
+				 * was ok.
 				 */
-				switch(esp->sreg & ESP_STAT_PMASK) {
-				default:
-					printk("esp: Not datain/dataout/status.\n");
-					panic("esp: penguin phase transition after selection.");
-					break;
-				case ESP_DOP:
-					/* Data out phase. */
-					dregs->cond_reg |= DMA_FIFO_INV;
-					while(dregs->cond_reg & DMA_FIFO_ISDRAIN)
-						barrier();
-					SCptr->SCp.phase = in_dataout;
-#ifdef DEBUG_ESP_SG
-					if(SCptr->use_sg)
-						printk("esp: sg-start <%p,%d>",
-						       SCptr->SCp.ptr,
-						       SCptr->SCp.this_residual);
-#endif
-					eregs->esp_tclow = SCptr->SCp.this_residual;
-					eregs->esp_tcmed = (SCptr->SCp.this_residual>>8);
-					eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_NULL);
-
-					/* This is either the one buffer dvma ptr,
-					 * or the first one in the scatter gather
-					 * list.  Check out esp_queue to see how
-					 * this is set up.
-					 */
-					dregs->st_addr = SCptr->SCp.ptr;
-					dregs->cond_reg &= ~(DMA_ST_WRITE);
-					dregs->cond_reg |= (DMA_ENABLE | DMA_INT_ENAB);
-					eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_TI);
-					ETRACE(("DATA_OUT\n"));
-					goto esp_handle_done;
-				case ESP_DIP:
-					/* Data in phase. */
-					dregs->cond_reg |= DMA_FIFO_INV;
-					while(dregs->cond_reg & DMA_FIFO_ISDRAIN)
-						barrier();
-					SCptr->SCp.phase = in_datain;
-#ifdef DEBUG_ESP_SG
-					if(SCptr->use_sg)
-						printk("esp: sg-start <%p,%d>",
-						       SCptr->SCp.ptr,
-						       SCptr->SCp.this_residual);
-#endif
-					eregs->esp_tclow = SCptr->SCp.this_residual;
-					eregs->esp_tcmed = (SCptr->SCp.this_residual>>8);
-					eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_NULL);
-
-					/* This is either the one buffer dvma ptr,
-					 * or the first one in the scatter gather
-					 * list.  Check out esp_queue to see how
-					 * this is set up.
-					 */
-					dregs->st_addr = SCptr->SCp.ptr;
-					dregs->cond_reg |= (DMA_ENABLE | DMA_ST_WRITE | DMA_INT_ENAB);
-					eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_TI);
-					ETRACE(("DATA_IN\n"));
-					goto esp_handle_done;
-				case ESP_STATP:
-					/* Status phase. */
-					SCptr->SCp.phase = in_status;
-					eregs->esp_cmd = ESP_CMD_ICCSEQ;
-					ETRACE(("STATUS\n"));
-					goto esp_handle_done; /* Wait for message. */
-				};
-			};
-		} else if(esp->ireg & ESP_INTR_FDONE) {
-			/* I'd like to investigate why this happens... */
-			ESPLOG(("esp: This is weird, halfway through "));
-			ESPLOG(("selection, trying to continue anyways.\n"));
-			goto esp_handle_done;
+				message_out = MSG_PARITY_ERROR;
+			}
+		} else if(intr == ESP_INTR_BSERV) {
+			/* Only got status byte. */
+			ESPLOG(("esp%d: got status only, ", esp->esp_id));
+			if(!(esp->sreg & ESP_STAT_PERR)) {
+				SCptr->SCp.Status = esp->esp_command[0];
+				SCptr->SCp.Message = 0xff;
+			} else {
+				/* The status byte had bad parity.
+				 * we leave the scsi_pointer Status
+				 * field alone as we set it to a default
+				 * of CHECK_CONDITION in esp_queue.
+				 */
+				message_out = INITIATOR_ERROR;
+			}
 		} else {
-			panic("esp: Did not get bus service during selection.");
-			goto esp_handle_done;
+			/* This shouldn't happen ever. */
+			ESPSTAT(("got bolixed\n"));
+			esp_advance_phase(SCptr, in_the_dark);
+			return esp_do_phase_determine(esp, eregs);
 		}
-		panic("esp: Mr. Potatoe Head is on the loose!");
 
-	case in_datain:
-		/* Drain the fifo for writes to memory. */
-		switch(esp->dma->revision) {
-		case dvmarev0:
-		case dvmarev1:
-		case dvmarevplus:
-		case dvmarev2:
-		case dvmarev3:
-			/* Force a drain. */
-			dregs->cond_reg |= DMA_FIFO_STDRAIN;
-
-			/* fall through */
-		case dvmaesc1:
-			/* Wait for the fifo to drain completely. */
-			while(dregs->cond_reg & DMA_FIFO_ISDRAIN)
-				barrier();
+		if(!message_out) {
+			ESPSTAT(("status=%2x msg=%2x, ", SCptr->SCp.Status,
+				SCptr->SCp.Message));
+			if(SCptr->SCp.Message == COMMAND_COMPLETE) {
+				ESPSTAT(("and was COMMAND_COMPLETE\n"));
+				esp_advance_phase(SCptr, in_freeing);
+				return esp_do_freebus(esp, eregs);
+			} else {
+				ESPLOG(("esp%d: and _not_ COMMAND_COMPLETE\n",
+					esp->esp_id));
+				esp->msgin_len = esp->msgin_ctr = 1;
+				esp_advance_phase(SCptr, in_msgindone);
+				return esp_do_msgindone(esp, eregs);
+			}
+		} else {
+			/* With luck we'll be able to let the target
+			 * know that bad parity happened, it will know
+			 * which byte caused the problems and send it
+			 * again.  For the case where the status byte
+			 * receives bad parity, I do not believe most
+			 * targets recover very well.  We'll see.
+			 */
+			ESPLOG(("esp%d: bad parity somewhere mout=%2x\n",
+				esp->esp_id, message_out));
+			esp->cur_msgout[0] = message_out;
+			esp->msgout_len = esp->msgout_ctr = 1;
+			esp_advance_phase(SCptr, in_the_dark);
+			return esp_do_phase_determine(esp, eregs);
+		}
+	} else {
+		/* If we disconnect now, all hell breaks loose. */
+		ESPLOG(("esp%d: whoops, disconnect\n", esp->esp_id));
+		esp_advance_phase(SCptr, in_the_dark);
+		return esp_do_phase_determine(esp, eregs);
+	}
+}
+
+/* The target has control of the bus and we have to see where it has
+ * taken us.
+ */
+static int esp_do_phase_determine(struct Sparc_ESP *esp,
+				  struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+
+	ESPPHASE(("esp_do_phase_determine: "));
+	if(!(esp->ireg & ESP_INTR_DC)) {
+		switch(esp->sreg & ESP_STAT_PMASK) {
+		case ESP_DOP:
+		case ESP_DIP:
+			ESPPHASE(("to data phase\n"));
+			return esp_do_data(esp, eregs);
+
+		case ESP_STATP:
+			/* Whee, status phase, finish up the command. */
+			ESPPHASE(("to status phase\n"));
+			esp->esp_command[0] = 0xff;
+			esp->esp_command[1] = 0xff;
+#ifdef __mc68000__
+			/* When initiating a DMA read as will happen below
+			 * there should only be made a cache invalidate.
+			 * Therefore this flush. (Hint: The DMA will overwrite
+			 * the two bytes in memory).
+			 */
+			CACHEFLUSHLINE(VTOP(esp->esp_command));
+#endif
+			esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+
+			eregs->esp_tclow = 2;
+			eregs->esp_tcmed = 0;
+
+			esp->dma_init_read(esp, (char *) esp->esp_command, 2);
+
+			esp_advance_phase(SCptr, in_status);
+			esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_ICCSEQ);
+			return esp_do_status(esp, eregs);
+
+		case ESP_MOP:
+			ESPPHASE(("to msgout phase\n"));
+			esp_advance_phase(SCptr, in_msgout);
+			return esp_do_msgout(esp, eregs);
+
+		case ESP_MIP:
+			ESPPHASE(("to msgin phase\n"));
+			esp_advance_phase(SCptr, in_msgin);
+			return esp_do_msgin(esp, eregs);
+
+		case ESP_CMDP:
+			/* Ugh, we're running a non-standard command the
+			 * ESP doesn't understand, one byte at a time.
+			 */
+			ESPPHASE(("to cmd phase\n"));
+			esp_advance_phase(SCptr, in_cmdbegin);
+			return esp_do_cmdbegin(esp, eregs);
+		};
+	} else {
+		Scsi_Device *dp = SCptr->device;
+
+		/* This means real problems if we see this
+		 * here.  Unless we were actually trying
+		 * to force the device to abort/reset.
+		 */
+		ESPLOG(("esp%d Disconnect amidst phases, ", esp->esp_id));
+		ESPLOG(("pphase<%s> cphase<%s>, ",
+			phase_string(SCptr->SCp.phase),
+			phase_string(SCptr->SCp.sent_command)));
+		if(esp->disconnected_SC)
+			esp_cmd(esp, eregs, ESP_CMD_ESEL);
+
+		switch(esp->cur_msgout[0]) {
+		default:
+			/* We didn't expect this to happen at all. */
+			ESPLOG(("device is bolixed\n"));
+			esp_advance_phase(SCptr, in_tgterror);
+			safe_esp_done(esp, (DID_ERROR << 16));
+			break;
+
+		case BUS_DEVICE_RESET:
+			ESPLOG(("device reset successful\n"));
+			dp->sync_max_offset = 0;
+			dp->sync_min_period = 0;
+			dp->sync = 0;
+			esp_advance_phase(SCptr, in_resetdev);
+			safe_esp_done(esp, (DID_RESET << 16));
 			break;
+
+		case ABORT:
+			ESPLOG(("device abort successful\n"));
+			esp_advance_phase(SCptr, in_abortone);
+			safe_esp_done(esp, (DID_ABORT << 16));
+			break;
+
 		};
+		return do_intr_end;
+	}
 
-	case in_dataout:
-		dregs->cond_reg &= ~DMA_ENABLE;
+	ESPLOG(("esp%d: to unknown phase\n", esp->esp_id));
+	printk("esp%d: Bizarre bus phase %2x.\n", esp->esp_id,
+	       esp->sreg & ESP_STAT_PMASK);
+	return do_reset_bus;
+}
+
+/* First interrupt after exec'ing a cmd comes here. */
+static int esp_select_complete(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	Scsi_Device *SDptr = SCptr->device;
+	int cmd_bytes_sent, fcnt;
+
+	esp->seqreg = (eregs->esp_sstep & ESP_STEP_VBITS);
+	fcnt = (eregs->esp_fflags & ESP_FF_FBYTES);
+	cmd_bytes_sent = esp->dma_bytes_sent(esp, fcnt);
+	if(esp->dma_invalidate)
+		esp->dma_invalidate(esp);
+
+	/* Let's check to see if a reselect happened
+	 * while we we're trying to select.  This must
+	 * be checked first.
+	 */
+	if(esp->ireg == (ESP_INTR_RSEL | ESP_INTR_FDONE)) {
+		esp_reconnect(esp, SCptr);
+		return esp_do_reconnect(esp, eregs);
+	}
 
-		/* We may be pipelining an sg-list. */
-		if(SCptr->use_sg) {
-			if(SCptr->SCp.buffers_residual) {
-				/* If we do not see a BUS SERVICE interrupt
-				 * at this point, or we see that we have left
-				 * the current data phase, then we lose.
+	/* Looks like things worked, we should see a bus service &
+	 * a function complete interrupt at this point.  Note we
+	 * are doing a direct comparison because we don't want to
+	 * be fooled into thinking selection was successful if
+	 * ESP_INTR_DC is set, see below.
+	 */
+	if(esp->ireg == (ESP_INTR_FDONE | ESP_INTR_BSERV)) {
+		/* target speaks... */
+		esp->targets_present |= (1<<SCptr->target);
+
+		/* What if the target ignores the sdtr? */
+		if(esp->snip)
+			SDptr->sync = 1;
+
+		/* See how far, if at all, we got in getting
+		 * the information out to the target.
+		 */
+		switch(esp->seqreg) {
+		default:
+
+		case ESP_STEP_ASEL:
+			/* Arbitration won, target selected, but
+			 * we are in some phase which is not command
+			 * phase nor is it message out phase.
+			 *
+			 * XXX We've confused the target, obviously.
+			 * XXX So clear it's state, but we also end
+			 * XXX up clearing everyone elses.  That isn't
+			 * XXX so nice.  I'd like to just reset this
+			 * XXX target, but if I cannot even get it's
+			 * XXX attention and finish selection to talk
+			 * XXX to it, there is not much more I can do.
+			 * XXX If we have a loaded bus we're going to
+			 * XXX spend the next second or so renegotiating
+			 * XXX for synchronous transfers.
+			 */
+			ESPLOG(("esp%d: STEP_ASEL for tgt %d\n",
+				esp->esp_id, SCptr->target));
+
+		case ESP_STEP_SID:
+			/* Arbitration won, target selected, went
+			 * to message out phase, sent one message
+			 * byte, then we stopped.  ATN is asserted
+			 * on the SCSI bus and the target is still
+			 * there hanging on.  This is a legal
+			 * sequence step if we gave the ESP a select
+			 * and stop command.
+			 *
+			 * XXX See above, I could set the borken flag
+			 * XXX in the device struct and retry the
+			 * XXX command.  But would that help for
+			 * XXX tagged capable targets?
+			 */
+
+		case ESP_STEP_NCMD:
+			/* Arbitration won, target selected, maybe
+			 * sent the one message byte in message out
+			 * phase, but we did not go to command phase
+			 * in the end.  Actually, we could have sent
+			 * only some of the message bytes if we tried
+			 * to send out the entire identify and tag
+			 * message using ESP_CMD_SA3.
+			 */
+			cmd_bytes_sent = 0;
+			break;
+
+		case ESP_STEP_PPC:
+			/* No, not the powerPC pinhead.  Arbitration
+			 * won, all message bytes sent if we went to
+			 * message out phase, went to command phase
+			 * but only part of the command was sent.
+			 *
+			 * XXX I've seen this, but usually in conjunction
+			 * XXX with a gross error which appears to have
+			 * XXX occurred between the time I told the
+			 * XXX ESP to arbitrate and when I got the
+			 * XXX interrupt.  Could I have misloaded the
+			 * XXX command bytes into the fifo?  Actually,
+			 * XXX I most likely missed a phase, and therefore
+			 * XXX went into never never land and didn't even
+			 * XXX know it.  That was the old driver though.
+			 * XXX What is even more peculiar is that the ESP
+			 * XXX showed the proper function complete and
+			 * XXX bus service bits in the interrupt register.
+			 */
+
+		case ESP_STEP_FINI4:
+		case ESP_STEP_FINI5:
+		case ESP_STEP_FINI6:
+		case ESP_STEP_FINI7:
+			/* Account for the identify message */
+			if(SCptr->SCp.phase == in_slct_norm)
+				cmd_bytes_sent -= 1;
+		};
+		esp_cmd(esp, eregs, ESP_CMD_NULL);
+
+		/* Be careful, we could really get fucked during synchronous
+		 * data transfers if we try to flush the fifo now.
+		 */
+		if(!fcnt && /* Fifo is empty and... */
+		   /* either we are not doing synchronous transfers or... */
+		   (!SDptr->sync_max_offset ||
+		    /* We are not going into data in phase. */
+		    ((esp->sreg & ESP_STAT_PMASK) != ESP_DIP)))
+			esp_cmd(esp, eregs, ESP_CMD_FLUSH); /* flush is safe */
+
+		/* See how far we got if this is not a slow command. */
+		if(!esp->esp_slowcmd) {
+			if(cmd_bytes_sent < 0)
+				cmd_bytes_sent = 0;
+			if(cmd_bytes_sent != SCptr->cmd_len) {
+				/* Crapola, mark it as a slowcmd
+				 * so that we have some chance of
+				 * keeping the command alive with
+				 * good luck.
+				 *
+				 * XXX Actually, if we didn't send it all
+				 * XXX this means either we didn't set things
+				 * XXX up properly (driver bug) or the target
+				 * XXX or the ESP detected parity on one of
+				 * XXX the command bytes.  This makes much
+				 * XXX more sense, and therefore this code
+				 * XXX should be changed to send out a
+				 * XXX parity error message or if the status
+				 * XXX register shows no parity error then
+				 * XXX just expect the target to bring the
+				 * XXX bus into message in phase so that it
+				 * XXX can send us the parity error message.
+				 * XXX SCSI sucks...
 				 */
-				if(!(esp->ireg & ESP_INTR_BSERV) ||
-				   ((esp->sreg & ESP_STAT_PMASK) > 1))
-					panic("esp: Aiee penguin on the SCSI-bus.");
-
-				++SCptr->SCp.buffer;
-				--SCptr->SCp.buffers_residual;
-				SCptr->SCp.this_residual = SCptr->SCp.buffer->length;
-				SCptr->SCp.ptr = SCptr->SCp.buffer->alt_address;
+				esp->esp_slowcmd = 1;
+				esp->esp_scmdp = &(SCptr->cmnd[cmd_bytes_sent]);
+				esp->esp_scmdleft = (SCptr->cmd_len - cmd_bytes_sent);
+			}
+		}
 
-#ifdef DEBUG_ESP_SG
-				printk("<%p,%d> ", SCptr->SCp.ptr,
-				       SCptr->SCp.this_residual);
-#endif
+		/* Now figure out where we went. */
+		esp_advance_phase(SCptr, in_the_dark);
+		return esp_do_phase_determine(esp, eregs);
+	}
 
-				/* Latch in new esp counters... */
-				eregs->esp_tclow = SCptr->SCp.this_residual;
-				eregs->esp_tcmed = (SCptr->SCp.this_residual>>8);
-				eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_NULL);
+	/* Did the target even make it? */
+	if(esp->ireg == ESP_INTR_DC) {
+		/* wheee... nobody there or they didn't like
+		 * what we told it to do, clean up.
+		 */
+
+		/* If anyone is off the bus, but working on
+		 * a command in the background for us, tell
+		 * the ESP to listen for them.
+		 */
+		if(esp->disconnected_SC)
+			esp_cmd(esp, eregs, ESP_CMD_ESEL);
+
+		if(((1<<SCptr->target) & esp->targets_present) &&
+		   esp->seqreg && esp->cur_msgout[0] == EXTENDED_MESSAGE &&
+		   (SCptr->SCp.phase == in_slct_msg ||
+		    SCptr->SCp.phase == in_slct_stop)) {
+			/* shit */
+			esp->snip = 0;
+			printk("esp%d: Failed synchronous negotiation for target %d "
+			       "lun %d\n",
+			       esp->esp_id, SCptr->target, SCptr->lun);
+			SDptr->sync_max_offset = 0;
+			SDptr->sync_min_period = 0;
+			SDptr->sync = 1; /* so we don't negotiate again */
+
+			/* Run the command again, this time though we
+			 * won't try to negotiate for synchronous transfers.
+			 *
+			 * XXX I'd like to do something like send an
+			 * XXX INITIATOR_ERROR or ABORT message to the
+			 * XXX target to tell it, "Sorry I confused you,
+			 * XXX please come back and I will be nicer next
+			 * XXX time".  But that requires having the target
+			 * XXX on the bus, and it has dropped BSY on us.
+			 */
+			esp->current_SC = NULL;
+			esp_advance_phase(SCptr, not_issued);
+			prepend_SC(&esp->issue_SC, SCptr);
+			esp_exec_cmd(esp);
+			return do_intr_end;
+		}
 
-				/* Reload DVMA gate array with new vaddr and enab. */
-				dregs->st_addr = SCptr->SCp.ptr;
-				dregs->cond_reg |= DMA_ENABLE;
+		/* Ok, this is normal, this is what we see during boot
+		 * or whenever when we are scanning the bus for targets.
+		 * But first make sure that is really what is happening.
+		 */
+		if(((1<<SCptr->target) & esp->targets_present)) {
+			printk("esp%d: Warning, live target %d not responding to "
+			       "selection.\n", esp->esp_id, SCptr->target);
+
+			/* This _CAN_ happen.  The SCSI standard states that
+			 * the target is to _not_ respond to selection if
+			 * _it_ detects bad parity on the bus for any reason.
+			 * Therefore, we assume that if we've talked successfully
+			 * to this target before, bad parity is the problem.
+			 */
+			safe_esp_done(esp, (DID_PARITY << 16));
+		} else {
+			/* Else, there really isn't anyone there. */
+			ESPMISC(("esp: selection failure, maybe nobody there?\n"));
+			ESPMISC(("esp: target %d lun %d\n",
+				 SCptr->target, SCptr->lun));
+			safe_esp_done(esp, (DID_BAD_TARGET << 16));
+		}
+		return do_intr_end;
+	}
 
-				/* Tell the esp to start transferring. */
-				eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_TI);
-				goto esp_handle_done;
+
+	ESPLOG(("esp%d: Selection failure.\n", esp->esp_id));
+	printk("esp%d: Currently -- ", esp->esp_id);
+	esp_print_ireg(esp->ireg);
+	printk(" ");
+	esp_print_statreg(esp->sreg);
+	printk(" ");
+	esp_print_seqreg(esp->seqreg);
+	printk("\n");
+	printk("esp%d: New -- ", esp->esp_id);
+	esp->sreg = eregs->esp_status;
+	esp->seqreg = eregs->esp_sstep;
+	esp->ireg = eregs->esp_intrpt;
+
+	esp_print_ireg(esp->ireg);
+	printk(" ");
+	esp_print_statreg(esp->sreg);
+	printk(" ");
+	esp_print_seqreg(esp->seqreg);
+	printk("\n");
+	ESPLOG(("esp%d: resetting bus\n", esp->esp_id));
+	return do_reset_bus; /* ugh... */
+}
+
+/* Continue reading bytes for msgin phase. */
+static int esp_do_msgincont(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	if(esp->ireg & ESP_INTR_BSERV) {
+		/* in the right phase too? */
+		if((esp->sreg & ESP_STAT_PMASK) == ESP_MIP) {
+			/* phew... */
+			esp_cmd(esp, eregs, ESP_CMD_TI);
+			esp_advance_phase(esp->current_SC, in_msgindone);
+			return do_intr_end;
+		}
+
+		/* We changed phase but ESP shows bus service,
+		 * in this case it is most likely that we, the
+		 * hacker who has been up for 20hrs straight
+		 * staring at the screen, drowned in coffee
+		 * smelling like retched cigarette ashes
+		 * have miscoded something..... so, try to
+		 * recover as best we can.
+		 */
+		printk("esp%d: message in mis-carriage.\n", esp->esp_id);
+	}
+	esp_advance_phase(esp->current_SC, in_the_dark);
+	return do_phase_determine;
+}
+
+static inline int check_singlebyte_msg(struct Sparc_ESP *esp,
+				       struct ESP_regs *eregs)
+{
+	esp->prevmsgin = esp->cur_msgin[0];
+	if(esp->cur_msgin[0] & 0x80) {
+		/* wheee... */
+		ESPLOG(("esp%d: target sends identify amidst phases\n",
+			esp->esp_id));
+		esp_advance_phase(esp->current_SC, in_the_dark);
+		return 0;
+	} else if(((esp->cur_msgin[0] & 0xf0) == 0x20) ||
+		  (esp->cur_msgin[0] == EXTENDED_MESSAGE)) {
+		esp->msgin_len = 2;
+		esp_advance_phase(esp->current_SC, in_msgincont);
+		return 0;
+	}
+	esp_advance_phase(esp->current_SC, in_the_dark);
+	switch(esp->cur_msgin[0]) {
+	default:
+		/* We don't want to hear about it. */
+		ESPLOG(("esp%d: msg %02x which we dont know about\n", esp->esp_id,
+			esp->cur_msgin[0]));
+		return MESSAGE_REJECT;
+
+	case NOP:
+		ESPLOG(("esp%d: target %d sends a nop\n", esp->esp_id,
+			esp->current_SC->target));
+		return 0;
+
+	case RESTORE_POINTERS:
+	case SAVE_POINTERS:
+		/* We handle this all automatically. */
+		return 0;
+
+	case COMMAND_COMPLETE:
+	case DISCONNECT:
+		/* Freeing the bus, let it go. */
+		esp->current_SC->SCp.phase = in_freeing;
+		return 0;
+
+	case MESSAGE_REJECT:
+		ESPMISC(("msg reject, "));
+		if(esp->prevmsgout == EXTENDED_MESSAGE) {
+			Scsi_Device *SDptr = esp->current_SC->device;
+
+			/* Doesn't look like this target can
+			 * do synchronous transfers.
+			 */
+			ESPSDTR(("got reject, was trying sync nego, clearing sync\n"));
+			SDptr->sync = 1;
+			SDptr->sync_min_period = 0;
+			SDptr->sync_max_offset = 0;
+			return 0;
+		} else {
+			ESPMISC(("not sync nego, sending ABORT\n"));
+			return ABORT;
+		}
+	};
+}
+
+/* Target negotiates for synchronous transfers before we do, this
+ * is legal although very strange.  What is even funnier is that
+ * the SCSI2 standard specifically recommends against targets doing
+ * this because so many initiators cannot cope with this occuring.
+ */
+static inline int target_with_ants_in_pants(struct Sparc_ESP *esp,
+					    Scsi_Cmnd *SCptr,
+					    Scsi_Device *SDptr)
+{
+	if(SDptr->sync || SDptr->borken) {
+		/* sorry, no can do */
+		ESPSDTR(("forcing to async, "));
+		build_sync_nego_msg(esp, 0, 0);
+		SDptr->sync = 1;
+		esp->snip = 1;
+		ESPLOG(("esp%d: hoping for msgout\n", esp->esp_id));
+		esp_advance_phase(SCptr, in_the_dark);
+		return EXTENDED_MESSAGE;
+	}
+
+	/* Ok, we'll check them out... */
+	return 0;
+}
+
+static inline void sync_report(struct Sparc_ESP *esp)
+{
+	int msg3, msg4;
+
+	msg3 = esp->cur_msgin[3];
+	msg4 = esp->cur_msgin[4];
+	if(msg4) {
+		int hz = 1000000000 / (msg3 * 4);
+		int integer = hz / 1000000;
+		int fraction = (hz - (integer * 1000000)) / 10000;
+		printk("esp%d: target %d [period %dns offset %d %d.%02dMHz %s SCSI%s]\n",
+		       esp->esp_id, esp->current_SC->target,
+		       (int) msg3 * 4,
+		       (int) msg4,
+		       integer, fraction,
+		       (((msg3 * 4) < 200) ? "FAST" : "synchronous"),
+		       (((msg3 * 4) < 200) ? "-II" : ""));
+	} else {
+		printk("esp%d: target %d asynchronous\n",
+		       esp->esp_id, esp->current_SC->target);
+	}
+}
+
+static inline int check_multibyte_msg(struct Sparc_ESP *esp,
+				       struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	Scsi_Device *SDptr = SCptr->device;
+	unchar regval = 0;
+	int message_out = 0;
+
+	ESPSDTR(("chk multibyte msg: "));
+	if(esp->cur_msgin[2] == EXTENDED_SDTR) {
+		int period = esp->cur_msgin[3];
+		int offset = esp->cur_msgin[4];
+
+		ESPSDTR(("is sync nego response, "));
+		if(!esp->snip) {
+			int rval;
+
+			/* Target negotiates first! */
+			ESPSDTR(("target jumps the gun, "));
+			message_out = EXTENDED_MESSAGE; /* we must respond */
+			rval = target_with_ants_in_pants(esp, SCptr, SDptr);
+			if(rval)
+				return rval;
+		}
+
+		ESPSDTR(("examining sdtr, "));
+
+		/* Offset cannot be larger than ESP fifo size. */
+		if(offset > 15) {
+			ESPSDTR(("offset too big %2x, ", offset));
+			offset = 15;
+			ESPSDTR(("sending back new offset\n"));
+			build_sync_nego_msg(esp, period, offset);
+			return EXTENDED_MESSAGE;
+		}
+
+		if(offset && period > esp->max_period) {
+			/* Yeee, async for this slow device. */
+			ESPSDTR(("period too long %2x, ", period));
+			build_sync_nego_msg(esp, 0, 0);
+			ESPSDTR(("hoping for msgout\n"));
+			esp_advance_phase(esp->current_SC, in_the_dark);
+			return EXTENDED_MESSAGE;
+		} else if (offset && period < esp->min_period) {
+			ESPSDTR(("period too short %2x, ", period));
+			period = esp->min_period;
+			if(esp->erev > esp236)
+				regval = 4;
+			else
+				regval = 5;
+		} else if(offset) {
+			int tmp;
+
+			ESPSDTR(("period is ok, "));
+			tmp = esp->ccycle / 1000;
+			regval = (((period << 2) + tmp - 1) / tmp);
+			if(regval && ((esp->erev == fas100a ||
+				       esp->erev == fas236))) {
+				if(period >= 50)
+					regval--;
 			}
-#ifdef DEBUG_ESP_SG
-			printk("done.\n");
+		}
+
+		if(offset) {
+
+			unchar bit;
+
+			SDptr->sync_min_period = (regval & 0x1f);
+			SDptr->sync_max_offset = (offset | esp->radelay);
+			if((esp->erev == fas100a || esp->erev == fas236)) {
+				if(esp->erev == fas100a)
+					bit = ESP_CONFIG3_FAST;
+				else
+					bit = ESP_CONFIG3_FSCSI;
+				if(period < 50)
+					esp->config3[SCptr->target] |= bit;
+				else
+					esp->config3[SCptr->target] &= ~bit;
+				eregs->esp_cfg3 = esp->config3[SCptr->target];
+			}
+			eregs->esp_soff = SDptr->sync_min_period;
+			eregs->esp_stp  = SDptr->sync_max_offset;
+
+			ESPSDTR(("soff=%2x stp=%2x cfg3=%2x\n",
+				SDptr->sync_max_offset,
+				SDptr->sync_min_period,
+				esp->config3[SCptr->target]));
+
+			esp->snip = 0;
+		} else if(SDptr->sync_max_offset) {
+			unchar bit;
+
+			/* back to async mode */
+			ESPSDTR(("unaccaptable sync nego, forcing async\n"));
+			SDptr->sync_max_offset = 0;
+			SDptr->sync_min_period = 0;
+			eregs->esp_soff = 0;
+			eregs->esp_stp = 0;
+			if((esp->erev == fas100a || esp->erev == fas236)) {
+				if(esp->erev == fas100a)
+					bit = ESP_CONFIG3_FAST;
+				else
+					bit = ESP_CONFIG3_FSCSI;
+				esp->config3[SCptr->target] &= ~bit;
+				eregs->esp_cfg3 = esp->config3[SCptr->target];
+			}
+		}
+
+		sync_report(esp);
+
+		ESPSDTR(("chk multibyte msg: sync is known, "));
+		SDptr->sync = 1;
+
+		if(message_out) {
+			ESPLOG(("esp%d: sending sdtr back, hoping for msgout\n",
+				esp->esp_id));
+			build_sync_nego_msg(esp, period, offset);
+			esp_advance_phase(SCptr, in_the_dark);
+			return EXTENDED_MESSAGE;
+		}
+
+		ESPSDTR(("returning zero\n"));
+		esp_advance_phase(SCptr, in_the_dark); /* ...or else! */
+		return 0;
+	} else if(esp->cur_msgin[2] == EXTENDED_MODIFY_DATA_POINTER) {
+		ESPLOG(("esp%d: rejecting modify data ptr msg\n", esp->esp_id));
+		message_out = MESSAGE_REJECT;
+	}
+	esp_advance_phase(SCptr, in_the_dark);
+	return message_out;
+}
+
+static int esp_do_msgindone(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int message_out = 0, it = 0;
+	int rval;
+
+	rval = skipahead1(esp, eregs, SCptr, in_msgin, in_msgindone);
+	if(rval)
+		return rval;
+
+	if(SCptr->SCp.sent_command != in_status) {
+		if(!(esp->ireg & ESP_INTR_DC)) {
+			if(esp->msgin_len && (esp->sreg & ESP_STAT_PERR)) {
+				message_out = MSG_PARITY_ERROR;
+				esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+			} else if((it = (eregs->esp_fflags & ESP_FF_FBYTES))!=1) {
+				/* We certainly dropped the ball somewhere. */
+				message_out = INITIATOR_ERROR;
+				esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+			} else if(!esp->msgin_len) {
+				it = eregs->esp_fdata;
+				esp_advance_phase(SCptr, in_msgincont);
+			} else {
+				/* it is ok and we want it */
+				it = esp->cur_msgin[esp->msgin_ctr] = eregs->esp_fdata;
+				esp->msgin_ctr++;
+			}
+		} else {
+			esp_advance_phase(SCptr, in_the_dark);
+			return do_work_bus;
+		}
+	} else {
+		it = esp->cur_msgin[0];
+	}
+	if(!message_out && esp->msgin_len) {
+		if(esp->msgin_ctr < esp->msgin_len) {
+			esp_advance_phase(SCptr, in_msgincont);
+		} else if(esp->msgin_len == 1) {
+			message_out = check_singlebyte_msg(esp, eregs);
+		} else if(esp->msgin_len == 2) {
+			if(esp->cur_msgin[0] == EXTENDED_MESSAGE) {
+				if((it+2) >= 15) {
+					message_out = MESSAGE_REJECT;
+				} else {
+					esp->msgin_len = (it + 2);
+					esp_advance_phase(SCptr, in_msgincont);
+				}
+			} else {
+				message_out = MESSAGE_REJECT; /* foo on you */
+			}
+		} else {
+			message_out = check_multibyte_msg(esp, eregs);
+		}
+	}
+	if(message_out < 0) {
+		return -message_out;
+	} else if(message_out) {
+		if(((message_out != 1) &&
+		    ((message_out < 0x20) || (message_out & 0x80))))
+			esp->msgout_len = 1;
+		esp->cur_msgout[0] = message_out;
+		esp_cmd(esp, eregs, ESP_CMD_SATN);
+		esp_advance_phase(SCptr, in_msgincont);
+		esp->msgin_len = 0;
+	}
+	esp->sreg = eregs->esp_status;
+	esp->sreg &= ~(ESP_STAT_INTR);
+	if((esp->sreg & (ESP_STAT_PMSG|ESP_STAT_PCD)) == (ESP_STAT_PMSG|ESP_STAT_PCD))
+		esp_cmd(esp, eregs, ESP_CMD_MOK);
+	if((SCptr->SCp.sent_command == in_msgindone) &&
+	    (SCptr->SCp.phase == in_freeing))
+		return esp_do_freebus(esp, eregs);
+	return do_intr_end;
+}
+
+static int esp_do_cmdbegin(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+
+	esp_advance_phase(SCptr, in_cmdend);
+	esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+	eregs->esp_fdata = *esp->esp_scmdp++;
+	esp->esp_scmdleft--;
+	esp_cmd(esp, eregs, ESP_CMD_TI);
+	return do_intr_end;
+}
+
+static inline int esp_do_cmddone(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	esp_cmd(esp, eregs, ESP_CMD_NULL);
+	if(esp->ireg & ESP_INTR_BSERV) {
+		esp_advance_phase(esp->current_SC, in_the_dark);
+		return esp_do_phase_determine(esp, eregs);
+	}
+	ESPLOG(("esp%d: in do_cmddone() but didn't get BSERV interrupt.\n",
+		esp->esp_id));
+	return do_reset_bus;
+}
+
+static int esp_do_msgout(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+	switch(esp->msgout_len) {
+	case 1:
+		eregs->esp_fdata = esp->cur_msgout[0];
+		esp_cmd(esp, eregs, ESP_CMD_TI);
+		break;
+
+	case 2:
+		esp->esp_command[0] = esp->cur_msgout[0];
+		esp->esp_command[1] = esp->cur_msgout[1];
+		esp->dma_setup(esp, (char *) esp->esp_command, 2, 0);
+		esp_setcount(eregs, 2);
+		esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+		break;
+
+	case 5:
+		esp->esp_command[0] = esp->cur_msgout[0];
+		esp->esp_command[1] = esp->cur_msgout[1];
+		esp->esp_command[2] = esp->cur_msgout[2];
+		esp->esp_command[3] = esp->cur_msgout[3];
+		esp->esp_command[4] = esp->cur_msgout[4];
+		esp->esp_command[5] = esp->cur_msgout[5];
+		esp->snip = 1;
+		esp->dma_setup(esp, (char *) esp->esp_command, 5, 0);
+		esp_setcount(eregs, 5);
+		esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+		break;
+
+	default:
+		/* whoops */
+		ESPMISC(("bogus msgout sending NOP\n"));
+		eregs->esp_fdata = NOP;
+		esp->msgout_len = 1;
+		esp_cmd(esp, eregs, ESP_CMD_TI);
+		break;
+	}
+	esp_advance_phase(esp->current_SC, in_msgoutdone);
+	return do_intr_end;
+}
+
+static inline int esp_do_msgoutdone(struct Sparc_ESP *esp, 
+				    struct ESP_regs *eregs)
+{
+	if((esp->msgout_len > 1) && esp->dma_barrier)
+		esp->dma_barrier(esp);
+
+	if(!(esp->ireg & ESP_INTR_DC)) {
+		esp_cmd(esp, eregs, ESP_CMD_NULL);
+		switch(esp->sreg & ESP_STAT_PMASK) {
+		case ESP_MOP:
+			/* whoops, parity error */
+			ESPLOG(("esp%d: still in msgout, parity error assumed\n",
+				esp->esp_id));
+			if(esp->msgout_len > 1)
+				esp_cmd(esp, eregs, ESP_CMD_SATN);
+			esp_advance_phase(esp->current_SC, in_msgout);
+			return do_work_bus;
+
+		case ESP_DIP:
+			break;
+
+		default:
+			if(!fcount(eregs) &&
+			   !(esp->current_SC->device->sync_max_offset))
+				esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+			break;
+
+		};
+	} else {
+		ESPLOG(("esp%d: disconnect, resetting bus\n", esp->esp_id));
+		return do_reset_bus;
+	}
+
+	/* If we sent out a synchronous negotiation message, update
+	 * our state.
+	 */
+	if(esp->cur_msgout[2] == EXTENDED_MESSAGE &&
+	   esp->cur_msgout[4] == EXTENDED_SDTR) {
+		esp->snip = 1; /* anal retentiveness... */
+	}
+
+	esp->prevmsgout = esp->cur_msgout[0];
+	esp->msgout_len = 0;
+	esp_advance_phase(esp->current_SC, in_the_dark);
+	return esp_do_phase_determine(esp, eregs);
+}
+
+/* This is the second tier in our dual-level SCSI state machine. */
+static inline int esp_work_bus(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+
+	ESPBUS(("esp_work_bus: "));
+	if(!SCptr) {
+		ESPBUS(("reconnect\n"));
+		return esp_do_reconnect(esp, eregs);
+	}
+
+	switch(SCptr->SCp.phase) {
+	case in_the_dark:
+		ESPBUS(("in the dark\n"));
+		return esp_do_phase_determine(esp, eregs);
+
+	case in_slct_norm:
+	case in_slct_stop:
+	case in_slct_msg:
+	case in_slct_tag:
+	case in_slct_sneg:
+		ESPBUS(("finish selection\n"));
+		return esp_select_complete(esp, eregs);
+
+	case in_datain:
+	case in_dataout:
+		ESPBUS(("finish data\n"));
+		return esp_do_data_finale(esp, eregs);
+
+	case in_msgout:
+		ESPBUS(("message out "));
+		return esp_do_msgout(esp, eregs);
+
+	case in_msgoutdone:
+		ESPBUS(("finish message out "));
+		return esp_do_msgoutdone(esp, eregs);
+
+	case in_msgin:
+		ESPBUS(("message in "));
+		return esp_do_msgin(esp, eregs);
+
+	case in_msgincont:
+		ESPBUS(("continue message in "));
+		return esp_do_msgincont(esp, eregs);
+
+	case in_msgindone:
+		ESPBUS(("finish message in "));
+		return esp_do_msgindone(esp, eregs);
+
+	case in_status:
+		ESPBUS(("status phase "));
+		return esp_do_status(esp, eregs);
+
+	case in_freeing:
+		ESPBUS(("freeing the bus "));
+		return esp_do_freebus(esp, eregs);
+
+	case in_cmdbegin:
+		ESPBUS(("begin slow cmd "));
+		return esp_do_cmdbegin(esp, eregs);
+
+	case in_cmdend:
+		ESPBUS(("end slow cmd "));
+		return esp_do_cmddone(esp, eregs);
+
+	default:
+		printk("esp%d: command in weird state %2x\n",
+		       esp->esp_id, esp->current_SC->SCp.phase);
+		return do_reset_bus;
+	};
+}
+
+/* Main interrupt handler for an esp adapter. */
+inline void esp_handle(struct Sparc_ESP *esp)
+{
+	struct ESP_regs *eregs;
+	Scsi_Cmnd *SCptr;
+	int what_next = do_intr_end;
+#ifdef CONFIG_SCSI_SUNESP
+	struct dma_registers *dregs = esp->dregs;
 #endif
+	eregs = esp->eregs;
+	SCptr = esp->current_SC;
+
+	if(esp->dma_irq_entry)
+		esp->dma_irq_entry(esp);
+
+	/* Check for errors. */
+	esp->sreg = eregs->esp_status;
+	esp->sreg &= (~ESP_STAT_INTR);
+
+	if(esp->sreg & (ESP_STAT_SPAM)) {
+		/* Gross error, could be due to one of:
+		 *
+		 * - top of fifo overwritten, could be because
+		 *   we tried to do a synchronous transfer with
+		 *   an offset greater than ESP fifo size
+		 *
+		 * - top of command register overwritten
+		 *
+v		 * - DMA setup to go in one direction, SCSI
+		 *   bus points in the other, whoops
+		 *
+		 * - weird phase change during asynchronous
+		 *   data phase while we are initiator
+		 */
+		ESPLOG(("esp%d: Gross error sreg=%2x\n", esp->esp_id, esp->sreg));
+
+		/* If a command is live on the bus we cannot safely
+		 * reset the bus, so we'll just let the pieces fall
+		 * where they may.  Here we are hoping that the
+		 * target will be able to cleanly go away soon
+		 * so we can safely reset things.
+		 */
+		if(!SCptr) {
+			ESPLOG(("esp%d: No current cmd during gross error, "
+				"resetting bus\n", esp->esp_id));
+			what_next = do_reset_bus;
+			goto again;
 		}
-		/* Take a look at what happened. */
-		if(esp->ireg & ESP_INTR_DC) {
-			panic("esp: target disconnects during data transfer.");
-			goto esp_handle_done;
-		} else if(esp->ireg & ESP_INTR_BSERV) {
-			if((esp->sreg & ESP_STAT_PMASK) != ESP_STATP) {
-				panic("esp: Not status phase after data phase.");
+	}
+
+#ifdef CONFIG_SCSI_SUNESP
+	if(dregs->cond_reg & DMA_HNDL_ERROR) {
+		/* A DMA gate array error.  Here we must
+		 * be seeing one of two things.  Either the
+		 * virtual to physical address translation
+		 * on the SBUS could not occur, else the
+		 * translation it did get pointed to a bogus
+		 * page.  Ho hum...
+		 */
+		ESPLOG(("esp%d: DMA error %08lx\n", esp->esp_id,
+			dregs->cond_reg));
+
+		/* DMA gate array itself must be reset to clear the
+		 * error condition.
+		 */
+		if(esp->dma_reset)
+			esp->dma_reset(esp);
+
+		what_next = do_reset_bus;
+		goto again;
+	}
+#endif /* CONFIG_SCSI_SUNESP */
+
+	esp->ireg = eregs->esp_intrpt;   /* Unlatch intr and stat regs */
+
+	/* No current cmd is only valid this point when there are
+	 * commands off the bus or we are trying a reset.
+	 */
+	if(!SCptr && !esp->disconnected_SC && !(esp->ireg & ESP_INTR_SR)) {
+		/* Panic is safe, since current_SC is null. */
+		ESPLOG(("esp%d: no command in esp_handle()\n", esp->esp_id));
+		panic("esp_handle: current_SC == penguin within interrupt!");
+	}
+
+	if(esp->ireg & (ESP_INTR_IC)) {
+		/* Illegal command fed to ESP.  Outside of obvious
+		 * software bugs that could cause this, there is
+		 * a condition with esp100 where we can confuse the
+		 * ESP into an erroneous illegal command interrupt
+		 * because it does not scrape the FIFO properly
+		 * for reselection.  See esp100_reconnect_hwbug()
+		 * to see how we try very hard to avoid this.
+		 */
+		ESPLOG(("esp%d: illegal command\n", esp->esp_id));
+
+		esp_dump_state(esp, eregs);
+
+		if(SCptr) {
+			/* Devices with very buggy firmware can drop BSY
+			 * during a scatter list interrupt when using sync
+			 * mode transfers.  We continue the transfer as
+			 * expected, the target drops the bus, the ESP
+			 * gets confused, and we get a illegal command
+			 * interrupt because the bus is in the disconnected
+			 * state now and ESP_CMD_TI is only allowed when
+			 * a nexus is alive on the bus.
+			 */
+			ESPLOG(("esp%d: Forcing async and disabling disconnect for "
+				"target %d\n", esp->esp_id, SCptr->target));
+			SCptr->device->borken = 1; /* foo on you */
+		}
+
+		what_next = do_reset_bus;
+		goto again;
+	}
+
+	if(!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) {
+		int phase;
+		if(SCptr) {
+			phase = SCptr->SCp.phase;
+			if(phase & in_phases_mask) {
+				what_next = esp_work_bus(esp, eregs);
+			} else if(phase & in_slct_mask) {
+				what_next = esp_select_complete(esp, eregs);
+			} else {
+				ESPLOG(("esp%d: interrupt for no good reason...\n",
+					esp->esp_id));
 				goto esp_handle_done;
 			}
-			SCptr->SCp.phase = in_status;
-			eregs->esp_cmd = ESP_CMD_ICCSEQ;
-			ETRACE(("STATUS\n"));
-			goto esp_handle_done; /* Wait for message. */
 		} else {
-			printk("esp: did not get bus service after data transfer.");
-			printk("esp_status: intr<%2x> stat<%2x> seq<%2x>\n",
-			       esp->ireg, esp->sreg, esp->seqreg);
-			panic("esp: penguin data transfer.");
-			goto esp_handle_done;
+			ESPLOG(("esp%d: BSERV or FDONE or DC while SCptr==NULL\n",
+				esp->esp_id));
+			what_next = do_reset_bus;
+			goto again;
 		}
-	case in_status:
-		if(esp->ireg & ESP_INTR_DC) {
-			panic("esp: penguin disconnects in status phase.");
-			goto esp_handle_done;
-		} else if (esp->ireg & ESP_INTR_FDONE) {
-			/* Status and Message now sit in the fifo for us. */
-			last_fflags = eregs->esp_fflags;
-			SCptr->SCp.phase   = in_finale;
-			last_status = SCptr->SCp.Status  = eregs->esp_fdata;
-			last_msg = SCptr->SCp.Message = eregs->esp_fdata;
-			eregs->esp_cmd = ESP_CMD_MOK;
-			ETRACE(("FINALE\n"));
-			goto esp_handle_done;
+	} else if(esp->ireg & ESP_INTR_SR) {
+		ESPLOG(("esp%d: SCSI bus reset interrupt\n", esp->esp_id));
+		what_next = do_reset_complete;
+	} else if(esp->ireg & (ESP_INTR_S | ESP_INTR_SATN)) {
+		ESPLOG(("esp%d: AIEEE we have been selected by another initiator!\n",
+			esp->esp_id));
+		what_next = do_reset_bus;
+		goto again;
+	} else if(esp->ireg & ESP_INTR_RSEL) {
+		if(!SCptr) {
+			/* This is ok. */
+			what_next = esp_do_reconnect(esp, eregs);
+		} else if(SCptr->SCp.phase & in_slct_mask) {
+			/* Only selection code knows how to clean
+			 * up properly.
+			 */
+			ESPDISC(("Reselected during selection attempt\n"));
+			what_next = esp_select_complete(esp, eregs);
 		} else {
-			panic("esp: penguin status phase.");
+			ESPLOG(("esp%d: Reselected while bus is busy\n",
+				esp->esp_id));
+			what_next = do_reset_bus;
+			goto again;
 		}
-	case in_finale:
-		if(esp->ireg & ESP_INTR_BSERV) {
-			panic("esp: penguin doesn't disconnect after status msg-ack.");
-			goto esp_handle_done;
-		} else if(esp->ireg & ESP_INTR_DC) {
-			/* Nexus is complete. */
-#ifdef THREADED_ESP_DRIVER
-			append_SC(esp->eatme_SC, esp->current_SC);
-			esp->current_SC = 0;
-			wake_up(esp_kernel_thread);
-#else
-			esp_done(esp, ((SCptr->SCp.Status & 0xff) |
-				 ((SCptr->SCp.Message & 0xff) << 8) |
-				 (DID_OK << 16)));
+	}
+
+	/* We're trying to fight stack problems, and inline as much as
+	 * possible without making this driver a mess. hate hate hate
+	 * This is tier-one in our dual level SCSI state machine.
+	 */
+again:
+	switch(what_next) {
+	case do_intr_end:
+		goto esp_handle_done;
+
+	case do_work_bus:
+		what_next = esp_work_bus(esp, eregs);
+		break;
+
+	case do_phase_determine:
+		what_next = esp_do_phase_determine(esp, eregs);
+		break;
+
+	case do_reset_bus:
+		ESPLOG(("esp%d: resetting bus...\n", esp->esp_id));
+		esp->resetting_bus = 1;
+		esp_cmd(esp, eregs, ESP_CMD_RS);
+		goto esp_handle_done;
+
+	case do_reset_complete:
+		/* Tricky, we don't want to cause any more commands to
+		 * go out until we clear all the live cmds by hand.
+		 */
+#ifdef CONFIG_SCSI_SUNESP
+		if(!DMA_ISBROKEN(esp))
 #endif
-			ETRACE(("NEXUS_COMPLETE\n"));
-			goto esp_handle_done;
-		} else {
-			printk("esp: wacky state while in in_finale phase.\n");
-			printk("esp_status: intr<%2x> stat<%2x> seq<%2x>\n",
-			       esp->ireg, esp->sreg, esp->seqreg);
-			panic("esp: penguin esp state.");
+			esp->dma_ints_off(esp);
+		if(esp->current_SC) {
+			Scsi_Cmnd *SCptr = esp->current_SC;
+
+#ifdef CONFIG_SCSI_SUNESP
+			if(!SCptr->use_sg)
+				mmu_release_scsi_one((char *)
+						     SCptr->SCp.have_data_in,
+						     SCptr->request_bufflen,
+						     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+			else
+				mmu_release_scsi_sgl((struct mmu_sglist *)
+						     SCptr->buffer,
+						     SCptr->use_sg - 1,
+						     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+#endif
+			SCptr->result = (DID_RESET << 16);
+			SCptr->scsi_done(SCptr);
+			cli();
+		}
+		esp->current_SC = NULL;
+		if(esp->disconnected_SC) {
+			Scsi_Cmnd *SCptr;
+			while((SCptr = remove_first_SC(&esp->disconnected_SC))) {
+				if(!SCptr->use_sg)
+#ifdef CONFIG_SCSI_SUNESP
+					mmu_release_scsi_one((char *)
+							     SCptr->SCp.have_data_in,
+							     SCptr->request_bufflen,
+							     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+				else
+					mmu_release_scsi_sgl((struct mmu_sglist *)
+							     SCptr->buffer,
+							     SCptr->use_sg - 1,
+							     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+#endif
+				SCptr->result = (DID_RESET << 16);
+				SCptr->scsi_done(SCptr);
+				cli();
+			}
+		}
+		esp->resetting_bus = 0;
+#ifdef CONFIG_SCSI_SUNESP
+		if(!DMA_ISBROKEN(esp))
+#endif
+			esp->dma_ints_on(esp);
+
+		if(esp->current_SC) {
+			printk("esp%d: weird weird weird, current_SC not NULL after "
+			       "SCSI bus reset.\n", esp->esp_id);
 			goto esp_handle_done;
 		}
-	default:
-		panic("esp: detected penguin phase.");
+
+		/* Now it is safe to execute more things. */
+		if(esp->issue_SC){
+			esp_exec_cmd(esp);
+		}
 		goto esp_handle_done;
-	}
-	panic("esp: Heading to the promised land.");
+
+	default:
+		/* state is completely lost ;-( */
+		ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n",
+			esp->esp_id));
+		what_next = do_reset_bus;
+		break;
+
+	};
+	goto again;
 
 esp_handle_done:
-	DMA_IRQ_EXIT(esp->dma, dregs);
+	if(esp->dma_irq_exit)
+		esp->dma_irq_exit(esp);
 	return;
 }
 
-static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
+void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
 {
 	struct Sparc_ESP *esp;
 
+	static int running = 0;
+	int again;
+
+	/* It is ok to take irq's on one esp while the other
+	 * is amidst the processing of a reset.
+	 */
+	running++;
+	if(running > esps_running)
+		ESPLOG(("esp_intr: yieee, recursive interrupt!\n"));
+
 	/* Handle all ESP interrupts showing */
+repeat:
+	again = 0;
 	for_each_esp(esp) {
-		if(DMA_IRQ_P(esp->dregs)) {
+		if(esp->dma_irq_p(esp)) {
+			again = 1;
+			ESPIRQ(("I%d(", esp->esp_id));
 			esp_handle(esp);
+			ESPIRQ((")"));
 		}
 	}
+	if(again)
+		goto repeat;
+
+	running--;
 }
diff -urN linux-2.0.15/drivers/scsi/esp.h linux-2.0.18/drivers/scsi/esp.h
--- linux-2.0.15/drivers/scsi/esp.h	Mon Aug 26 23:29:28 1996
+++ linux-2.0.18/drivers/scsi/esp.h	Sun Sep  8 14:25:21 1996
@@ -4,11 +4,97 @@
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  */
 
-#ifndef _SPARC_ESP_H
-#define _SPARC_ESP_H
+#ifndef _ESP_H
+#define _ESP_H
 
-/* For dvma controller register definitions. */
-#include <asm/dma.h>
+/* Macros for debugging messages */
+
+/* #define DEBUG_ESP */
+/* #define DEBUG_ESP_DATA */
+/* #define DEBUG_ESP_QUEUE */
+/* #define DEBUG_ESP_DISCONNECT */
+/* #define DEBUG_ESP_STATUS */
+/* #define DEBUG_ESP_PHASES */
+/* #define DEBUG_ESP_WORKBUS */
+/* #define DEBUG_STATE_MACHINE */
+/* #define DEBUG_ESP_CMDS */
+/* #define DEBUG_ESP_IRQS */
+/* #define DEBUG_SDTR */
+/* #define DEBUG_ESP_SG */
+
+/* Use the following to sprinkle debugging messages in a way whic
+ * suits you if combinations of the above become too verbose when
+ * trying to track down a specific problem.
+ */
+/* #define DEBUG_ESP_MISC */
+
+#if defined(DEBUG_ESP)
+#define ESPLOG(foo)  printk foo
+#else
+#define ESPLOG(foo)
+#endif /* (DEBUG_ESP) */
+
+#if defined(DEBUG_ESP_DATA)
+#define ESPDATA(foo)  printk foo
+#else
+#define ESPDATA(foo)
+#endif
+
+#if defined(DEBUG_ESP_QUEUE)
+#define ESPQUEUE(foo)  printk foo
+#else
+#define ESPQUEUE(foo)
+#endif
+
+#if defined(DEBUG_ESP_DISCONNECT)
+#define ESPDISC(foo)  printk foo
+#else
+#define ESPDISC(foo)
+#endif
+
+#if defined(DEBUG_ESP_STATUS)
+#define ESPSTAT(foo)  printk foo
+#else
+#define ESPSTAT(foo)
+#endif
+
+#if defined(DEBUG_ESP_PHASES)
+#define ESPPHASE(foo)  printk foo
+#else
+#define ESPPHASE(foo)
+#endif
+
+#if defined(DEBUG_ESP_WORKBUS)
+#define ESPBUS(foo)  printk foo
+#else
+#define ESPBUS(foo)
+#endif
+
+#if defined(DEBUG_ESP_IRQS)
+#define ESPIRQ(foo)  printk foo
+#else
+#define ESPIRQ(foo)
+#endif
+
+#if defined(DEBUG_SDTR)
+#define ESPSDTR(foo)  printk foo
+#else
+#define ESPSDTR(foo)
+#endif
+
+#if defined(DEBUG_ESP_MISC)
+#define ESPMISC(foo)  printk foo
+#else
+#define ESPMISC(foo)
+#endif
+
+#define INTERNAL_ESP_ERROR \
+        (panic ("Internal ESP driver error in file %s, line %d\n", \
+		__FILE__, __LINE__))
+
+#define INTERNAL_ESP_ERROR_NOPANIC \
+        (printk ("Internal ESP driver error in file %s, line %d\n", \
+		 __FILE__, __LINE__))
 
 /* The ESP SCSI controllers have their register sets in three
  * "classes":
@@ -24,7 +110,7 @@
  * apart with a big-endian ordering to the bytes.
  */
 
-struct Sparc_ESP_regs {
+struct ESP_regs {
                                 /* Access    Description              Offset */
     volatile unchar esp_tclow;  /* rw  Low bits of the transfer count 0x00   */
                                 unchar tlpad1[3];
@@ -68,7 +154,7 @@
 
 /* Various revisions of the ESP board. */
 enum esp_rev {
-  esp100     = 0x00,  /* NCR53C90  */
+  esp100     = 0x00,  /* NCR53C90 - very broken */
   esp100a    = 0x01,  /* NCR53C90A */
   esp236     = 0x02,
   fas236     = 0x03,
@@ -79,16 +165,16 @@
 
 /* We get one of these for each ESP probed. */
 struct Sparc_ESP {
-  struct Sparc_ESP *next;           /* Next ESP on probed or NULL */
-  struct Sparc_ESP_regs *eregs;           /* All esp registers */
-  struct Linux_SBus_DMA *dma;             /* Who I do transfers with. */
-  struct sparc_dma_registers *dregs;      /* And his registers. */
+  struct Sparc_ESP *next;                 /* Next ESP on probed or NULL */
+  struct ESP_regs *eregs;	          /* All esp registers */
+  struct Linux_DMA *dma;                  /* Who I do transfers with. */
+  void *dregs;		  		  /* And his registers. */
   struct Scsi_Host *ehost;                /* Backpointer to SCSI Host */
 
-  struct linux_sbus_device *edev;         /* Pointer to SBus entry */
+  void *edev;        		          /* Pointer to controller base/SBus */
   char prom_name[64];                     /* Name of ESP device from prom */
   int prom_node;                          /* Prom node where ESP found */
-  int esp_id;                             /* Same as esphost->host_id */
+  int esp_id;                             /* Unique per-ESP ID number */
 
   /* ESP Configuration Registers */
   unsigned char config1;                  /* Copy of the 1st config register */
@@ -98,13 +184,17 @@
   /* The current command we are sending to the ESP chip.  This esp_command
    * ptr needs to be mapped in DVMA area so we can send commands and read
    * from the ESP fifo without burning precious CPU cycles.  Programmed I/O
-   * sucks when we have the DVMA to do it for us.
+   * sucks when we have the DVMA to do it for us.  The ESP is stupid and will
+   * only send out 6, 10, and 12 byte SCSI commands, others we need to send
+   * one byte at a time.  esp_slowcmd being set says that we are doing one
+   * of the command types ESP doesn't understand, esp_scmdp keeps track of
+   * which byte we are sending, esp_scmdleft says how many bytes to go.
    */
   volatile unchar *esp_command;           /* Location of command */
-  int esp_clen;                           /* Length of this command */
-
-  /* To hold onto the dvma buffer ptr. */
-  char *dvma_hold;
+  unsigned char esp_clen;                 /* Length of this command */
+  unsigned char esp_slowcmd;
+  unsigned char *esp_scmdp;
+  unsigned char esp_scmdleft;
 
   /* The following are used to determine the cause of an IRQ. Upon every
    * IRQ entry we synchronize these with the hardware registers.
@@ -118,14 +208,34 @@
   unsigned int cfact;                    /* Clock conversion factor */
   unsigned int ccycle;                   /* One ESP clock cycle */
   unsigned int ctick;                    /* One ESP clock time */
-  unsigned int sync_defp;                /* Default negotiation period */
+  unsigned int radelay;                  /* FAST chip req/ack delay */
+  unsigned int neg_defp;                 /* Default negotiation period */
+  unsigned int sync_defp;                /* Default sync transfer period */
+  unsigned int max_period;               /* longest our period can be */
+  unsigned int min_period;               /* shortest period we can withstand */
+  unsigned char ccf;			 /* Clock conversion factor */
+  /* For slow to medium speed input clock rates we shoot for 5mb/s,
+   * but for high input clock rates we try to do 10mb/s although I
+   * don't think a transfer can even run that fast with an ESP even
+   * with DMA2 scatter gather pipelining.
+   */
+#define SYNC_DEFP_SLOW            0x32   /* 5mb/s  */
+#define SYNC_DEFP_FAST            0x19   /* 10mb/s */
+
+  unsigned int snip;                      /* Sync. negotiation in progress */
+  unsigned int targets_present;           /* targets spoken to before */
+
+  int current_transfer_size;              /* Set at beginning of data dma */
+
+  unchar espcmdlog[32];                   /* Log of current esp cmds sent. */
+  unchar espcmdent;                       /* Current entry in esp cmd log. */
 
   /* Misc. info about this ESP */
   enum esp_rev erev;                      /* ESP revision */
   int irq;                                /* SBus IRQ for this ESP */
   int scsi_id;                            /* Who am I as initiator? */
   int scsi_id_mask;                       /* Bitmask of 'me'. */
-  int diff;                               /* Differential SCSI? */
+  int diff;                               /* Differential SCSI bus? */
   int bursts;                             /* Burst sizes our DVMA supports */
 
   /* Our command queues, only one cmd lives in the current_SC queue. */
@@ -137,8 +247,45 @@
   Scsi_Cmnd *eatme_SC;           /* Cmds waiting for esp thread to process. */
 #endif
 
-  /* Abortion status */
-  int aborting, abortion_complete, abort_result;
+  /* Message goo */
+  unchar cur_msgout[16];
+  unchar cur_msgin[16];
+  unchar prevmsgout, prevmsgin;
+  unchar msgout_len, msgin_len;
+  unchar msgout_ctr, msgin_ctr;
+
+  /* States that we cannot keep in the per cmd structure because they
+   * cannot be assosciated with any specific command.
+   */
+  unchar being_reselected;
+  unchar resetting_bus;
+  unchar aborting_cmd;
+  unchar aborting_all;
+
+  /* Functions handling DMA
+   */ 
+  /* Required functions */
+  int  (*dma_bytes_sent)(struct Sparc_ESP *, int);
+  int  (*dma_can_transfer)(struct Sparc_ESP *, Scsi_Cmnd *);
+  void (*dma_dump_state)(struct Sparc_ESP *);
+  void (*dma_init_read)(struct Sparc_ESP *, char *, int);
+  void (*dma_init_write)(struct Sparc_ESP *, char *, int);
+  void (*dma_ints_off)(struct Sparc_ESP *);
+  void (*dma_ints_on)(struct Sparc_ESP *);
+  int  (*dma_irq_p)(struct Sparc_ESP *);
+  int  (*dma_ports_p)(struct Sparc_ESP *);
+  void (*dma_setup)(struct Sparc_ESP *, char *, int, int);
+
+  /* Optional functions (i.e. may be initialized to 0) */
+  void (*dma_barrier)(struct Sparc_ESP *);
+  void (*dma_drain)(struct Sparc_ESP *);
+  void (*dma_invalidate)(struct Sparc_ESP *);
+  void (*dma_irq_entry)(struct Sparc_ESP *);
+  void (*dma_irq_exit)(struct Sparc_ESP *);
+  void (*dma_led_off)(struct Sparc_ESP *);
+  void (*dma_led_on)(struct Sparc_ESP *);
+  void (*dma_poll)(struct Sparc_ESP *, unsigned char *);
+  void (*dma_reset)(struct Sparc_ESP *);
 };
 
 /* Bitfield meanings for the above registers. */
@@ -283,7 +430,12 @@
 #define ESP_STEP_PPC          0x03             /* Early phase chg caused cmnd
                                                 * bytes to be lost
                                                 */
-#define ESP_STEP_FINI         0x04             /* Command was sent ok */
+#define ESP_STEP_FINI4        0x04             /* Command was sent ok */
+
+/* Ho hum, some ESP's set the step register to this as well... */
+#define ESP_STEP_FINI5        0x05
+#define ESP_STEP_FINI6        0x06
+#define ESP_STEP_FINI7        0x07
 
 /* ESP chip-test register read-write */
 #define ESP_TEST_TARG         0x01             /* Target test mode */
@@ -299,6 +451,7 @@
 /* ESP fifo flags register read-only */
 /* Note that the following implies a 16 byte FIFO on the ESP. */
 #define ESP_FF_FBYTES         0x1f             /* Num bytes in FIFO */
+#define ESP_FF_ONOTZERO       0x20             /* offset ctr not zero (esp100) */
 #define ESP_FF_SSTEP          0xe0             /* Sequence step */
 
 /* ESP clock conversion factor register write-only */
@@ -311,40 +464,25 @@
 #define ESP_CCF_F6            0x06             /* 25.01MHz - 30MHz */
 #define ESP_CCF_F7            0x07             /* 30.01MHz - 35MHz */
 
-extern int esp_detect(struct SHT *);
-extern const char *esp_info(struct Scsi_Host *);
-extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-extern int esp_command(Scsi_Cmnd *);
-extern int esp_abort(Scsi_Cmnd *);
-extern int esp_reset(Scsi_Cmnd *, unsigned int);
+#define ESP_BUS_TIMEOUT        275             /* In milli-seconds */
+#define ESP_TIMEO_CONST       8192
+#define ESP_NEG_DEFP(mhz, cfact) \
+        ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact)))
+#define ESP_MHZ_TO_CYCLE(mhertz)  ((1000000000) / ((mhertz) / 1000))
+#define ESP_TICK(ccf, cycle)  ((7682 * (ccf) * (cycle) / 1000))
 
-extern struct proc_dir_entry proc_scsi_esp;
 
-#define SCSI_SPARC_ESP {                                                               \
-/* struct SHT *next */                                         NULL,                   \
-/* long *usage_count */                                        NULL,                   \
-/* struct proc_dir_entry *proc_dir */                          &proc_scsi_esp,         \
-/* int (*proc_info)(char *, char **, off_t, int, int, int) */  NULL,                   \
-/* const char *name */                                         "Sun ESP 100/100a/200", \
-/* int detect(struct SHT *) */                                 esp_detect,             \
-/* int release(struct Scsi_Host *) */                          NULL,                   \
-/* const char *info(struct Scsi_Host *) */                     esp_info,               \
-/* int command(Scsi_Cmnd *) */                                 esp_command,            \
-/* int queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)) */ esp_queue,              \
-/* int abort(Scsi_Cmnd *) */                                   esp_abort,              \
-/* int reset(Scsi_Cmnd *, int) */                              esp_reset,              \
-/* int slave_attach(int, int) */                               NULL,                   \
-/* int bios_param(Disk *, kdev_t, int[]) */                    NULL,                   \
-/* int can_queue */                                            10,                     \
-/* int this_id */                                              7,                      \
-/* short unsigned int sg_tablesize */                          SG_ALL,                 \
-/* short cmd_per_lun */                                        1,                      \
-/* unsigned char present */                                    0,                      \
-/* unsigned unchecked_isa_dma:1 */                             0,                      \
-/* unsigned use_clustering:1 */                                DISABLE_CLUSTERING, }
+extern struct proc_dir_entry proc_scsi_esp;
+extern int nesps, esps_in_use, esps_running;
 
 /* For our interrupt engine. */
 #define for_each_esp(esp) \
         for((esp) = espchain; (esp); (esp) = (esp)->next)
 
-#endif /* !(_SPARC_ESP_H) */
+
+/* External functions */
+extern struct Sparc_ESP *esp_allocate(Scsi_Host_Template *, 
+				      unsigned int, void *);
+extern void esp_initialize(struct Sparc_ESP *);
+extern void esp_intr(int, void *, struct pt_regs *);
+#endif /* !(_ESP_H) */
diff -urN linux-2.0.15/drivers/scsi/ncr53c8xx.c linux-2.0.18/drivers/scsi/ncr53c8xx.c
--- linux-2.0.15/drivers/scsi/ncr53c8xx.c	Mon Aug 26 22:12:09 1996
+++ linux-2.0.18/drivers/scsi/ncr53c8xx.c	Sat Sep  7 09:53:53 1996
@@ -40,7 +40,7 @@
 */
 
 /*
-**	21 July 1996, version 1.12b
+**	30 August 1996, version 1.12c
 **
 **	Supported SCSI-II features:
 **	    Synchronous negotiation
@@ -55,6 +55,8 @@
 **		53C815		(~53C810 with on board rom BIOS)
 **		53C820		(Wide, NCR BIOS in flash bios required)
 **		53C825		(Wide, ~53C820 with on board rom BIOS)
+**		53C860		(not yet tested)
+**		53C875		(not yet tested)
 **
 **	Other features:
 **		Memory mapped IO (linux-1.3.X only)
@@ -1492,7 +1494,7 @@
 static	void	ncr_complete	(ncb_p np, ccb_p cp);
 static	void	ncr_exception	(ncb_p np);
 static	void	ncr_free_ccb	(ncb_p np, ccb_p cp);
-static	void	ncr_getclock	(ncb_p np);
+static	void	ncr_getclock	(ncb_p np, u_char scntl3);
 static	ccb_p	ncr_get_ccb	(ncb_p np, u_long t,u_long l);
 static	void	ncr_init	(ncb_p np, char * msg, u_long code);
 static	int	ncr_intr	(ncb_p np);
@@ -3540,7 +3542,7 @@
 	**	Find the right value for scntl3.
 	*/
 
-	ncr_getclock (np);
+	ncr_getclock (np, INB(nc_scntl3));
 
 	/*
 	**	Reset chip.
@@ -4323,6 +4325,7 @@
 	lcb_p lp;
 	int target, lun;
 	int i;
+	u_char scntl3;
 
 	printf("%s: releasing host resources\n", ncr_name(np));
 
@@ -4369,12 +4372,15 @@
 
 	/*
 	**	Reset NCR chip
+	**	Preserve scntl3 for automatic clock detection.
 	*/
 
 	printf("%s: resetting chip\n", ncr_name(np));
+	scntl3 = INB (nc_scntl3);
 	OUTB (nc_istat,  SRST);
 	DELAY (1000);
 	OUTB (nc_istat,  0   );
+	OUTB (nc_scntl3, scntl3);
 
 	/*
 	**	Release Memory mapped IO region and IO mapped region
@@ -4816,8 +4822,8 @@
 	if ((ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion >= 0x10) ||
 	    ChipDevice == PCI_DEVICE_ID_NCR_53C875) {
 		OUTB(nc_dmode, 0xc0);	/* Set 16-transfer burst */
-		OUTB(nc_ctest5, 0x04);	/* Set DMA FIFO to 88 */
 #if 0
+		OUTB(nc_ctest5, 0x04);	/* Set DMA FIFO to 88 */
 		OUTB(nc_ctest5, 0x24);	/* Set DMA FIFO to 536 */
 		OUTB(nc_dmode, 0x40);	/* Set 64-transfer burst */
 		OUTB(nc_ctest3, 0x01);	/* Set write and invalidate */
@@ -6278,9 +6284,18 @@
 		/*
 		**	Check against controller limits.
 		*/
-		fak = (4ul * per - 1) / np->ns_sync - 3;
-		if (ofs && (fak>7))   {chg = 1; ofs = 0;}
-		if (!ofs) fak=7;
+		if (ofs != 0) {
+			fak = (4ul * per - 1) / np->ns_sync - 3;
+			if (fak>7) {
+				chg = 1;
+				ofs = 0;
+			}
+		}
+		if (ofs == 0) {
+			fak = 7;
+			per = 0;
+			tp->minsync = 0;
+		}
 
 		if (DEBUG_FLAGS & DEBUG_NEGO) {
 			PRINT_ADDR(cp->cmd);
@@ -7311,8 +7326,9 @@
 #endif /* NCR_CLOCK */
 
 
-static void ncr_getclock (ncb_p np)
+static void ncr_getclock (ncb_p np, u_char scntl3)
 {
+#if 0
 	u_char	tbl[5] = {6,2,3,4,6};
 	u_char	f;
 	u_char	ns_clock = (1000/NCR_CLOCK);
@@ -7335,6 +7351,25 @@
 	if (DEBUG_FLAGS & DEBUG_TIMING)
 		printf ("%s: sclk=%d async=%d sync=%d (ns) scntl3=0x%x\n",
 		ncr_name (np), ns_clock, np->ns_async, np->ns_sync, np->rv_scntl3);
+#else
+	/*
+	 *	For now just preserve the BIOS setting ...
+	 */
+
+	if ((scntl3 & 7) < 3) {
+		printf ("%s: assuming 40MHz clock\n", ncr_name(np));
+		scntl3 = 3; /* assume 40MHz if no value supplied by BIOS */
+	}
+
+	np->ns_sync   = 25;
+	np->ns_async  = 50;
+	np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x20 + (scntl3 & 0x7);
+
+	if (bootverbose) {
+		printf ("%s: initial value of SCNTL3 = %02x, final = %02x\n",
+			ncr_name(np), scntl3, np->rv_scntl3);
+	}
+#endif
 }
 
 /*===================== LINUX ENTRY POINTS SECTION ==========================*/
diff -urN linux-2.0.15/drivers/scsi/ncr53c8xx.h linux-2.0.18/drivers/scsi/ncr53c8xx.h
--- linux-2.0.15/drivers/scsi/ncr53c8xx.h	Thu Aug 29 21:52:44 1996
+++ linux-2.0.18/drivers/scsi/ncr53c8xx.h	Sun Sep  8 19:25:21 1996
@@ -196,7 +196,7 @@
 
 #if	LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
 
-#define NCR53C8XX {NULL,NULL,NULL,NULL,"ncr53c8xx (rel 1.12b)", ncr53c8xx_detect,\
+#define NCR53C8XX {NULL,NULL,NULL,NULL,"ncr53c8xx (rel 1.12c)", ncr53c8xx_detect,\
     	ncr53c8xx_release, /* info */ NULL, /* command, deprecated */ NULL, 		\
 	ncr53c8xx_queue_command, ncr53c8xx_abort, ncr53c8xx_reset,	\
         NULL /* slave attach */, scsicam_bios_param, /* can queue */ SCSI_NCR_CAN_QUEUE,\
@@ -207,7 +207,7 @@
 #else
 
 
-#define NCR53C8XX {NULL, NULL, "ncr53c8xx (rel 1.12b)", ncr53c8xx_detect,\
+#define NCR53C8XX {NULL, NULL, "ncr53c8xx (rel 1.12c)", ncr53c8xx_detect,\
     	ncr53c8xx_release, /* info */ NULL, /* command, deprecated */ NULL, 		\
 	ncr53c8xx_queue_command, ncr53c8xx_abort, ncr53c8xx_reset,	\
         NULL /* slave attach */, scsicam_bios_param, /* can queue */ SCSI_NCR_CAN_QUEUE,\
diff -urN linux-2.0.15/drivers/scsi/sg.c linux-2.0.18/drivers/scsi/sg.c
--- linux-2.0.15/drivers/scsi/sg.c	Mon Aug 26 22:12:19 1996
+++ linux-2.0.18/drivers/scsi/sg.c	Thu Sep  5 17:47:24 1996
@@ -275,8 +275,39 @@
      * See if the command completed normally, or whether something went
      * wrong.
      */
-    memcpy(device->header.sense_buffer, SCpnt->sense_buffer, sizeof(SCpnt->sense_buffer));
-    device->header.result = (SCpnt->sense_buffer[0] == 0 ? 0 : EIO);
+    memcpy(device->header.sense_buffer, SCpnt->sense_buffer,
+	   sizeof(SCpnt->sense_buffer));
+    switch (host_byte(SCpnt->result)) {
+    case DID_OK:
+      device->header.result = 0;
+      break;
+    case DID_NO_CONNECT:
+    case DID_BUS_BUSY:
+    case DID_TIME_OUT: 
+      device->header.result = EBUSY;
+      break;
+    case DID_BAD_TARGET: 
+    case DID_ABORT: 
+    case DID_PARITY: 
+    case DID_RESET:
+    case DID_BAD_INTR: 
+      device->header.result = EIO;
+      break;
+    case DID_ERROR:
+      /*
+       * There really should be DID_UNDERRUN and DID_OVERRUN error values,
+       * and a means for callers of scsi_do_cmd to indicate whether an
+       * underrun or overrun should signal an error.  Until that can be
+       * implemented, this kludge allows for returning useful error values
+       * except in cases that return DID_ERROR that might be due to an
+       * underrun.
+       */
+      if (SCpnt->sense_buffer[0] == 0 &&
+	  status_byte(SCpnt->result) == GOOD)
+	device->header.result = 0;
+      else device->header.result = EIO;
+      break;
+    }
 
     /*
      * Now wake up the process that is waiting for the
diff -urN linux-2.0.15/drivers/scsi/wd7000.c linux-2.0.18/drivers/scsi/wd7000.c
--- linux-2.0.15/drivers/scsi/wd7000.c	Mon Aug 26 22:12:25 1996
+++ linux-2.0.18/drivers/scsi/wd7000.c	Thu Sep  5 17:47:26 1996
@@ -98,6 +98,23 @@
  *  one of the FD boards, it would be nice to come up with a signature
  *  for it.
  *                                                           J.B. Jan 1994.
+ *
+ *
+ *  Revision: 08/24/1996. by Miroslav Zagorac <zaga@fly.cc.fer.hr>
+ *
+ *  Enhancement for wd7000_detect function has been made, so you don't have
+ *  to enter BIOS ROM adress and IO port in initialisation data (see struct
+ *  Config). We cannot detect IRQ and DMA for now, so we have to enter them
+ *  as arguments while wd_7000 is detected. If someone has IRQ and DMA set
+ *  to some other value, he can enter them in configuration without any
+ *  problem. Also I wrote a function wd7000_setup, so now you can enter
+ *  WD-7000 definition as kernel arguments, as in lilo.conf:
+ *
+ *     append="wd7000=IRQ,DMA"
+ *
+ *  PS: If card BIOS ROM is disabled, function wd7000_detect now will recognize
+ *      adapter, unlike the old one. Anyway, BIOS ROM from WD7000 adapter is
+ *      worthless for Linux. :)
  */
 
 #ifdef MODULE
@@ -121,7 +138,7 @@
 #include "hosts.h"
 #include "sd.h"
 
-#define ANY2SCSI_INLINE    /* undef this to use old macros */
+#define ANY2SCSI_INLINE		/* undef this to use old macros */
 #undef DEBUG
 
 #include "wd7000.h"
@@ -158,7 +175,7 @@
  */
 typedef volatile struct mailbox{
   unchar status;
-  unchar scbptr[3];             /* SCSI-style - MSB first (big endian) */
+  unchar scbptr[3];		/* SCSI-style - MSB first (big endian) */
 } Mailbox;
 
 /*
@@ -167,17 +184,17 @@
  *
  */
 typedef struct adapter {
-  struct Scsi_Host *sh;             /* Pointer to Scsi_Host structure */
-  int iobase;                       /* This adapter's I/O base address */
-  int irq;                          /* This adapter's IRQ level */
-  int dma;                          /* This adapter's DMA channel */
-  struct {                          /* This adapter's mailboxes */
-    Mailbox ogmb[OGMB_CNT];             /* Outgoing mailboxes */
-    Mailbox icmb[ICMB_CNT];             /* Incoming mailboxes */
+  struct Scsi_Host *sh;		/* Pointer to Scsi_Host structure */
+  int iobase;			/* This adapter's I/O base address */
+  int irq;			/* This adapter's IRQ level */
+  int dma;			/* This adapter's DMA channel */
+  struct {			/* This adapter's mailboxes */
+    Mailbox ogmb[OGMB_CNT];	/* Outgoing mailboxes */
+    Mailbox icmb[ICMB_CNT];	/* Incoming mailboxes */
   } mb;
-  int next_ogmb;                    /* to reduce contention at mailboxes */
-  unchar control;                   /* shadows CONTROL port value */
-  unchar rev1, rev2;                /* filled in by wd7000_revision */
+  int next_ogmb;		/* to reduce contention at mailboxes */
+  unchar control;		/* shadows CONTROL port value */
+  unchar rev1, rev2;		/* filled in by wd7000_revision */
 } Adapter;
 
 /*
@@ -186,22 +203,47 @@
  * Note that if SA_INTERRUPT is not used, wd7000_intr_handle must be
  * changed to pick up the IRQ level correctly.
  */
-Adapter *irq2host[16] = {NULL};  /* Possible IRQs are 0-15 */
+Adapter *irq2host[16] = {NULL};	/* Possible IRQs are 0-15 */
+
+/*
+ * (linear) base address for ROM BIOS
+ */
+static const long wd7000_biosaddr[] = {
+  0xc0000, 0xc2000, 0xc4000, 0xc6000, 0xc8000, 0xca000, 0xcc000, 0xce000,
+  0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000
+};
+#define NUM_ADDRS (sizeof(wd7000_biosaddr)/sizeof(long))
+
+static const unsigned short wd7000_iobase[] = {
+  0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338,
+  0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378,
+  0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8,
+  0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8
+};
+#define NUM_IOPORTS (sizeof(wd7000_iobase)/sizeof(unsigned short))
+
+static const short wd7000_irq[] = { 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 };
+#define NUM_IRQS (sizeof(wd7000_irq)/sizeof(short))
 
+static const short wd7000_dma[] = { 5, 6, 7 };
+#define NUM_DMAS (sizeof(wd7000_dma)/sizeof(short))
+  
 /*
  *  Standard Adapter Configurations - used by wd7000_detect
  */
 typedef struct {
-  const void *bios;             /* (linear) base address for ROM BIOS */
-  int iobase;                   /* I/O ports base address */
-  int irq;                      /* IRQ level */
-  int dma;                      /* DMA channel */
+  int irq;			/* IRQ level */
+  int dma;			/* DMA channel */
 } Config;
 
+/*
+ * Add here your configuration...
+ */
 static const Config configs[] = {
-  {(void *) 0xce000, 0x350, 15, 6},  /* defaults for single adapter */
-  {(void *) 0xc8000, 0x330, 11, 5},  /* defaults for second adapter */
-  {(void *) 0xd8000, 0x350, 15, 6},  /* Arghhh.... who added this ? */
+  { 15,  6 },			/* defaults for single adapter */
+  { 11,  5 },			/* defaults for second adapter */
+  {  9,  6 },			/* My configuretion (Zaga)     */
+  { -1, -1 }                    /* Empty slot                  */
 };
 #define NUM_CONFIGS (sizeof(configs)/sizeof(Config))
 
@@ -211,13 +253,13 @@
  *  added for the Future Domain version.
  */
 typedef struct signature {
-    const void *sig;           /* String to look for */
-    unsigned    ofs;           /* offset from BIOS base address */
-    unsigned    len;           /* length of string */
+    const void *sig;		/* String to look for */
+    unsigned    ofs;		/* offset from BIOS base address */
+    unsigned    len;		/* length of string */
 } Signature;
 
 static const Signature signatures[] = {
-  {"SSTBIOS",0x0000d,7}                  /* "SSTBIOS" @ offset 0x0000d */
+  { "SSTBIOS", 0x0000d, 7 }	/* "SSTBIOS" @ offset 0x0000d */
 };
 #define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))
 
@@ -226,19 +268,20 @@
  *  I/O Port Offsets and Bit Definitions
  *  4 addresses are used.  Those not defined here are reserved.
  */
-#define ASC_STAT        0       /* Status,  Read */
-#define ASC_COMMAND     0       /* Command, Write */
-#define ASC_INTR_STAT   1       /* Interrupt Status, Read */
-#define ASC_INTR_ACK    1       /* Acknowledge, Write */
-#define ASC_CONTROL     2       /* Control, Write */
-
-/* ASC Status Port
- */
-#define INT_IM		0x80		/* Interrupt Image Flag */
-#define CMD_RDY		0x40		/* Command Port Ready */
-#define CMD_REJ		0x20		/* Command Port Byte Rejected */
-#define ASC_INIT        0x10		/* ASC Initialized Flag */
-#define ASC_STATMASK    0xf0		/* The lower 4 Bytes are reserved */
+#define ASC_STAT        0	/* Status,  Read */
+#define ASC_COMMAND     0	/* Command, Write */
+#define ASC_INTR_STAT   1	/* Interrupt Status, Read */
+#define ASC_INTR_ACK    1	/* Acknowledge, Write */
+#define ASC_CONTROL     2	/* Control, Write */
+
+/*
+ * ASC Status Port
+ */
+#define INT_IM		0x80	/* Interrupt Image Flag */
+#define CMD_RDY		0x40	/* Command Port Ready */
+#define CMD_REJ		0x20	/* Command Port Byte Rejected */
+#define ASC_INIT        0x10	/* ASC Initialized Flag */
+#define ASC_STATMASK    0xf0	/* The lower 4 Bytes are reserved */
 
 /* COMMAND opcodes
  *
@@ -248,31 +291,31 @@
  *  discernible effect whatsoever.  I think they may be related to certain
  *  ICB commands, but again, the OEM manual doesn't make that clear.
  */
-#define NO_OP             0     /* NO-OP toggles CMD_RDY bit in ASC_STAT */
-#define INITIALIZATION    1     /* initialization (10 bytes) */
-#define DISABLE_UNS_INTR  2     /* disable unsolicited interrupts */
-#define ENABLE_UNS_INTR   3     /* enable unsolicited interrupts */
-#define INTR_ON_FREE_OGMB 4     /* interrupt on free OGMB */
-#define SOFT_RESET        5     /* SCSI bus soft reset */
-#define HARD_RESET_ACK    6     /* SCSI bus hard reset acknowledge */
-#define START_OGMB        0x80  /* start command in OGMB (n) */
-#define SCAN_OGMBS        0xc0  /* start multiple commands, signature (n) */
+#define NO_OP             0	/* NO-OP toggles CMD_RDY bit in ASC_STAT */
+#define INITIALIZATION    1	/* initialization (10 bytes) */
+#define DISABLE_UNS_INTR  2	/* disable unsolicited interrupts */
+#define ENABLE_UNS_INTR   3	/* enable unsolicited interrupts */
+#define INTR_ON_FREE_OGMB 4	/* interrupt on free OGMB */
+#define SOFT_RESET        5	/* SCSI bus soft reset */
+#define HARD_RESET_ACK    6	/* SCSI bus hard reset acknowledge */
+#define START_OGMB        0x80	/* start command in OGMB (n) */
+#define SCAN_OGMBS        0xc0	/* start multiple commands, signature (n) */
 				/*    where (n) = lower 6 bits */
 /* For INITIALIZATION:
  */
 typedef struct initCmd {
-  unchar op;                   /* command opcode (= 1) */
-  unchar ID;                   /* Adapter's SCSI ID */
-  unchar bus_on;               /* Bus on time, x 125ns (see below) */
-  unchar bus_off;              /* Bus off time, ""         ""      */
-  unchar rsvd;                 /* Reserved */
-  unchar mailboxes[3];         /* Address of Mailboxes, MSB first  */
-  unchar ogmbs;                /* Number of outgoing MBs, max 64, 0,1 = 1 */
-  unchar icmbs;                /* Number of incoming MBs,   ""       ""   */
+  unchar op;			/* command opcode (= 1) */
+  unchar ID;			/* Adapter's SCSI ID */
+  unchar bus_on;		/* Bus on time, x 125ns (see below) */
+  unchar bus_off;		/* Bus off time, ""         ""      */
+  unchar rsvd;			/* Reserved */
+  unchar mailboxes[3];		/* Address of Mailboxes, MSB first  */
+  unchar ogmbs;			/* Number of outgoing MBs, max 64, 0,1 = 1 */
+  unchar icmbs;			/* Number of incoming MBs,   ""       ""   */
 } InitCmd;
 
-#define BUS_ON            64    /* x 125ns = 8000ns (BIOS default) */
-#define BUS_OFF           15    /* x 125ns = 1875ns (BIOS default) */
+#define BUS_ON            64	/* x 125ns = 8000ns (BIOS default) */
+#define BUS_OFF           15	/* x 125ns = 1875ns (BIOS default) */
  
 /* Interrupt Status Port - also returns diagnostic codes at ASC reset
  *
@@ -301,29 +344,29 @@
 #define ASC_RES		0x01	/* ASC Reset		*/
 
 /*
-   Driver data structures:
-   - mb and scbs are required for interfacing with the host adapter.
-     An SCB has extra fields not visible to the adapter; mb's
-     _cannot_ do this, since the adapter assumes they are contiguous in
-     memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact
-     to access them.
-   - An icb is for host-only (non-SCSI) commands.  ICBs are 16 bytes each;
-     the additional bytes are used only by the driver.
-   - For now, a pool of SCBs are kept in global storage by this driver,
-     and are allocated and freed as needed.
-
-  The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command,
-  not when it has finished.  Since the SCB must be around for completion,
-  problems arise when SCBs correspond to OGMBs, which may be reallocated
-  earlier (or delayed unnecessarily until a command completes).
-  Mailboxes are used as transient data structures, simply for
-  carrying SCB addresses to/from the 7000-FASST2.
-
-  Note also since SCBs are not "permanently" associated with mailboxes,
-  there is no need to keep a global list of Scsi_Cmnd pointers indexed
-  by OGMB.   Again, SCBs reference their Scsi_Cmnds directly, so mailbox
-  indices need not be involved.
-*/
+ *   Driver data structures:
+ *   - mb and scbs are required for interfacing with the host adapter.
+ *     An SCB has extra fields not visible to the adapter; mb's
+ *     _cannot_ do this, since the adapter assumes they are contiguous in
+ *     memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact
+ *     to access them.
+ *   - An icb is for host-only (non-SCSI) commands.  ICBs are 16 bytes each;
+ *     the additional bytes are used only by the driver.
+ *   - For now, a pool of SCBs are kept in global storage by this driver,
+ *     and are allocated and freed as needed.
+ *
+ *  The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command,
+ *  not when it has finished.  Since the SCB must be around for completion,
+ *  problems arise when SCBs correspond to OGMBs, which may be reallocated
+ *  earlier (or delayed unnecessarily until a command completes).
+ *  Mailboxes are used as transient data structures, simply for
+ *  carrying SCB addresses to/from the 7000-FASST2.
+ *
+ *  Note also since SCBs are not "permanently" associated with mailboxes,
+ *  there is no need to keep a global list of Scsi_Cmnd pointers indexed
+ *  by OGMB.   Again, SCBs reference their Scsi_Cmnds directly, so mailbox
+ *  indices need not be involved.
+ */
 
 /*
  *  WD7000-specific scatter/gather element structure
@@ -483,10 +526,87 @@
 static int freescbs = MAX_SCBS;  /* free list counter */
 
 /*
+ *
+ */
+static short wd7000_setupIRQ[NUM_CONFIGS];
+static short wd7000_setupDMA[NUM_CONFIGS];
+static short wd7000_card_num = 0;
+
+/*
  *  END of data/declarations - code follows.
  */
 
 
+/*
+ * Note: You can now set these options from the kernel's "command line".
+ * The syntax is:
+ *
+ *     wd7000=IRQ,DMA
+ * eg:
+ *     wd7000=15,6
+ *
+ * will configure the driver for a WD-7000 controller
+ * using IRQ 15 with a DMA channel 6.
+ */
+void wd7000_setup (char *str, int *ints) {
+    short i, j;
+
+    if (wd7000_card_num >= NUM_CONFIGS) {
+        printk ("wd7000_setup: Too many \"wd7000=\" configurations in "
+	        "command line!\n");
+
+        return;
+    }
+
+    if (ints[0] != 2)
+	printk ("wd7000_setup: Error in command line!  "
+	        "Usage: wd7000=IRQ,DMA\n");
+    else {
+	for (i = 0; i < NUM_IRQS; i++)
+	    if (ints[1] == wd7000_irq[i])
+		break;
+
+	if (i == NUM_IRQS) {
+	    printk ("wd7000_setup: invalid IRQ.\n");
+	    return;
+	}
+	else
+ 	    wd7000_setupIRQ[wd7000_card_num] = ints[1];
+
+	for (i = 0; i < NUM_DMAS; i++)
+	    if (ints[2] == wd7000_dma[i])
+		break;
+
+	if (i == NUM_IRQS) {
+	    printk ("wd7000_setup: invalid DMA channel.\n");
+	    return;
+	}
+	else
+	    wd7000_setupDMA[wd7000_card_num] = ints[2];
+
+	if (wd7000_card_num)
+	    for (i = 0; i < (wd7000_card_num - 1); i++)
+	        for (j = i + 1; j < wd7000_card_num; j++)
+	            if (wd7000_setupIRQ[i] == wd7000_setupIRQ[j]) {
+	                printk ("wd7000_setup: duplicated IRQ!\n");
+		        return;
+	            }
+	            else if (wd7000_setupDMA[i] == wd7000_setupDMA[j]) {
+	                printk ("wd7000_setup: duplicated DMA channel!\n");
+		        return;
+	            }
+
+#ifdef DEBUG
+	printk ("wd7000_setup: IRQ=%d, DMA=%d\n",
+	        wd7000_setupIRQ[wd7000_card_num],
+		wd7000_setupDMA[wd7000_card_num]);
+#endif
+
+	wd7000_card_num++;
+    }
+}
+
+
 #ifdef ANY2SCSI_INLINE
 /*
    Since they're used a lot, I've redone the following from the macros
@@ -627,7 +747,8 @@
 	timeout = jiffies + WAITnexttimeout;
 	do {
 	    sti();	/* Yes this is really needed here */
-	    now = jiffies;   while (jiffies == now) /* wait a jiffy */;
+	    now = jiffies;
+	    while (jiffies == now); /* wait a jiffy */
 	    cli();
 	}  while (freescbs < needed && jiffies <= timeout);
 	/*
@@ -699,7 +820,7 @@
     Mailbox *ogmbs = host->mb.ogmb;
     int *next_ogmb = &(host->next_ogmb);
 #ifdef DEBUG
-    printk("wd7000 mail_out: %06x",(unsigned int) scbptr);
+    printk("wd7000 mail_out: 0x%06lx",(long) scbptr);
 #endif
     /* We first look for a free outgoing mailbox */
     save_flags(flags);
@@ -708,7 +829,7 @@
     for (i = 0; i < OGMB_CNT; i++) {
 	if (ogmbs[ogmb].status == 0)  {
 #ifdef DEBUG
-	    printk(" using OGMB %x",ogmb);
+	    printk(" using OGMB 0x%x",ogmb);
 #endif
 	    ogmbs[ogmb].status = 1;
 	    any2scsi((unchar *) ogmbs[ogmb].scbptr, (int) scbptr);
@@ -720,7 +841,7 @@
     }
     restore_flags(flags);
 #ifdef DEBUG
-    printk(", scb is %x",(unsigned int) scbptr);
+    printk(", scb is 0x%06lx",(long) scbptr);
 #endif
     if (i >= OGMB_CNT) {
 	/*
@@ -790,7 +911,7 @@
 	}
 #ifdef DEBUG
     if (scsierr||hosterr)
-	printk("\nSCSI command error: SCSI %02x host %04x return %d",
+	printk("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n",
 	       scsierr,in_error,hosterr);
 #endif
     return scsierr | (hosterr << 16);
@@ -800,7 +921,7 @@
 static void wd7000_scsi_done(Scsi_Cmnd * SCpnt)
 {
 #ifdef DEBUG
-    printk("wd7000_scsi_done: %06x\n",(unsigned int) SCpnt);
+    printk("wd7000_scsi_done: 0x%06x\n",(long) SCpnt);
 #endif
     SCpnt->SCp.phase = 0;
 }
@@ -819,12 +940,12 @@
     Mailbox *icmbs = host->mb.icmb;
 
 #ifdef DEBUG
-    printk("wd7000_intr_handle: irq = %d, host = %06x\n", irq, host);
+    printk("wd7000_intr_handle: irq = %d, host = 0x%06lx\n", irq, (long) host);
 #endif
 
     flag = inb(host->iobase+ASC_INTR_STAT);
 #ifdef DEBUG
-    printk("wd7000_intr_handle: intr stat = %02x\n",flag);
+    printk("wd7000_intr_handle: intr stat = 0x%02x\n",flag);
 #endif
 
     if (!(inb(host->iobase+ASC_STAT) & INT_IM))  {
@@ -847,7 +968,7 @@
 	/* The interrupt is for a mailbox */
 	if (!(flag & IMB_INTR)) {
 #ifdef DEBUG
-	    printk("wd7000_intr_handle: free outgoing mailbox");
+	    printk("wd7000_intr_handle: free outgoing mailbox\n");
 #endif
 	    /*
 	     * If sleep_on() and the "interrupt on free OGMB" command are
@@ -862,7 +983,7 @@
 	    icmb_status = icmbs[icmb].status;
 	    if (icmb_status & 0x80)  {  /* unsolicited - result in ICMB */
 #ifdef DEBUG
- 		printk("wd7000_intr_handle: unsolicited interrupt %02xh\n",
+ 		printk("wd7000_intr_handle: unsolicited interrupt 0x%02xh\n",
 		       icmb_status);
 #endif
 		wd7000_intr_ack(host);
@@ -983,7 +1104,7 @@
 	return 0;
     }
     if (make_code(icb.vue|(icb.status << 8),0))  {
-	printk("wd7000_diagnostics: failed (%02x,%02x)\n",
+	printk("wd7000_diagnostics: failed (0x%02x,0x%02x)\n",
 	       icb.vue, icb.status);
 	return 0;
     }
@@ -1011,6 +1132,7 @@
 
     if ((diag = inb(host->iobase+ASC_INTR_STAT)) != 1)  {
 	printk("wd7000_init: ");
+
 	switch (diag)  {
 	case 2:
 	  printk("RAM failure.\n");
@@ -1031,8 +1153,7 @@
 	  printk("ROM checksum error.\n");
 	  break;
 	default:
-	  printk("diagnostic code %02Xh received.\n", diag);
-	  break;
+	  printk("diagnostic code 0x%02Xh received.\n", diag);
 	}
 	return 0;
     }
@@ -1093,8 +1214,7 @@
 }
 
 
-int wd7000_detect(Scsi_Host_Template * tpnt)
-/* 
+/*
  *  Returns the number of adapters this driver is supporting.
  *
  *  The source for hosts.c says to wait to call scsi_register until 100%
@@ -1104,87 +1224,139 @@
  *  calling scsi_unregister.
  *
  */
+int wd7000_detect (Scsi_Host_Template *tpnt)
 {
-    int i,j, present = 0;
-    const Config *cfg;
-    const Signature *sig;
+    short present = 0, biosaddr_ptr, iobase_ptr, cfg_ptr, sig_ptr, i;
+    short biosptr[NUM_CONFIGS];
     Adapter *host = NULL;
     struct Scsi_Host *sh;
 
+    for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1);
+
     tpnt->proc_dir = &proc_scsi_wd7000;
 
-    /* Set up SCB free list, which is shared by all adapters */
-    init_scbs();
+    /*
+     * Set up SCB free list, which is shared by all adapters
+     */
+    init_scbs ();
+
+    for (cfg_ptr = 0; cfg_ptr < NUM_CONFIGS; cfg_ptr++) {
+        for (biosaddr_ptr = 0; biosaddr_ptr < NUM_ADDRS; biosaddr_ptr++)
+	    for (sig_ptr = 0; sig_ptr < NUM_SIGNATURES; sig_ptr++) {
+	        for (i = 0; i < cfg_ptr; i++)
+	            if (biosptr[i] == biosaddr_ptr)
+		        break;
+
+		if ((i == cfg_ptr) &&
+		    !memcmp ((void *) (wd7000_biosaddr[biosaddr_ptr] +
+		             signatures[sig_ptr].ofs), signatures[sig_ptr].sig,
+		             signatures[sig_ptr].len))
+		    goto bios_matched;
+	    }
+
+bios_matched:
 
-    cfg = configs;
-    for (i = 0; i < NUM_CONFIGS; i++)  {
-	sig = signatures;
-	for (j = 0; j < NUM_SIGNATURES; j++)  {
-	    if (!memcmp(cfg->bios+sig->ofs, sig->sig, sig->len))  {
-		/* matched this one */
 #ifdef DEBUG
-		printk("WD-7000 SST BIOS detected at %04X: checking...\n",
-		       (int) cfg->bios);
+	printk ("wd7000_detect: pass %d\n", cfg_ptr + 1);
+
+        if (biosaddr_ptr == NUM_ADDRS)
+	    printk ("WD-7000 SST BIOS not detected...\n");
+	else
+	    printk ("WD-7000 SST BIOS detected at 0x%lx: checking...\n",
+	            wd7000_biosaddr[biosaddr_ptr]);
 #endif
-		/*
-		 *  We won't explicitly test the configuration (in this
-		 *  version); instead, we'll just see if it works to
-		 *  setup the adapter; if it does, we'll use it.
-		 */
-		if (check_region(cfg->iobase, 4))  {  /* ports in use */
-		    printk("IO %xh already in use.\n", host->iobase);
-		    continue;
-		}
-		/*
-		 *  We register here, to get a pointer to the extra space,
-		 *  which we'll use as the Adapter structure (host) for
-		 *  this adapter.  It is located just after the registered
-		 *  Scsi_Host structure (sh), and is located by the empty
-		 *  array hostdata.
+
+	for (iobase_ptr = 0; iobase_ptr < NUM_IOPORTS; iobase_ptr++)
+	    if (! check_region (wd7000_iobase[iobase_ptr], 4)) {
+	        /*
+		 * ASC reset...
 		 */
-		sh = scsi_register(tpnt, sizeof(Adapter) );
-		host = (Adapter *) sh->hostdata;
+ 	        outb (ASC_RES, wd7000_iobase[iobase_ptr] + ASC_CONTROL);
+	        delay (1);
+	        outb (0, wd7000_iobase[iobase_ptr] + ASC_CONTROL);
+	        WAIT (wd7000_iobase[iobase_ptr] + ASC_STAT, ASC_STATMASK,
+		      CMD_RDY, 0);
+
+		if (inb (wd7000_iobase[iobase_ptr] + ASC_INTR_STAT) == 1) {
+		   /*
+		    *  We register here, to get a pointer to the extra space,
+		    *  which we'll use as the Adapter structure (host) for
+		    *  this adapter.  It is located just after the registered
+		    *  Scsi_Host structure (sh), and is located by the empty
+		    *  array hostdata.
+		    */
+	    	    sh = scsi_register (tpnt, sizeof (Adapter));
+	    	    host = (Adapter *) sh->hostdata;
+
 #ifdef DEBUG
-		printk("wd7000_detect: adapter allocated at %06x\n",
-		       (int)host);
+	    	    printk ("wd7000_detect: adapter allocated at 0x%x\n",
+	    		    (int) host);
 #endif
-		memset( host, 0, sizeof(Adapter) );
-		host->sh = sh;
-		host->irq = cfg->irq;
-		host->iobase = cfg->iobase;
-		host->dma = cfg->dma;
-		irq2host[host->irq] = host;
-
-		if (!wd7000_init(host))  {  /* Initialization failed */
-		    scsi_unregister (sh);
-		    continue;
-		}
 
-		/*
-		 *  OK from here - we'll use this adapter/configuration.
-		 */
-		wd7000_revision(host);   /* important for scatter/gather */
+	    	    memset (host, 0, sizeof (Adapter));
 
-		printk("Western Digital WD-7000 (%d.%d) ",
-		       host->rev1, host->rev2);
-		printk("using IO %xh IRQ %d DMA %d.\n",
-		       host->iobase, host->irq, host->dma);
-
-		request_region(host->iobase, 4,"wd7000"); /* Register our ports */
-		/*
-		 *  For boards before rev 6.0, scatter/gather isn't supported.
-		 */
-		if (host->rev1 < 6)  sh->sg_tablesize = SG_NONE;
+		    if (wd7000_card_num) {
+	    	        host->irq = wd7000_setupIRQ[--wd7000_card_num];
+	    	        host->dma = wd7000_setupDMA[wd7000_card_num];
+		    }
+		    else {
+	    	        host->irq = configs[cfg_ptr].irq;
+	    	        host->dma = configs[cfg_ptr].dma;
+		    }
+
+	    	    host->sh = sh;
+	    	    host->iobase = wd7000_iobase[iobase_ptr];
+	    	    irq2host[host->irq] = host;
 
-		present++;                      /* count it */
-		break;                          /* don't try any more sigs */
+#ifdef DEBUG
+		    printk ("wd7000_detect: Trying init WD-7000 card at IO "
+			    "0x%x, IRQ %d, DMA %d...\n",
+			    host->iobase, host->irq, host->dma);
+#endif
+
+	    	    if (! wd7000_init (host)) {  /* Initialization failed */
+	    		scsi_unregister (sh);
+
+	    		continue;
+	    	    }
+
+		    /*
+		     *  OK from here - we'll use this adapter/configuration.
+		     */
+		    wd7000_revision (host);   /* important for scatter/gather */
+
+                    /*
+		     * Register our ports.
+		     */
+    		    request_region (host->iobase, 4, "wd7000");
+
+    		    /*
+    		     *  For boards before rev 6.0, scatter/gather
+		     *  isn't supported.
+    		     */
+    		    if (host->rev1 < 6)
+   		       sh->sg_tablesize = SG_NONE;
+
+    		    present++;		/* count it */
+
+		    if (biosaddr_ptr != NUM_ADDRS)
+                        biosptr[cfg_ptr] = biosaddr_ptr;
+
+                    printk ("Western Digital WD-7000 (rev %d.%d) ",
+			    host->rev1, host->rev2);
+                    printk ("using IO 0x%xh, IRQ %d, DMA %d.\n", 
+	                    host->iobase, host->irq, host->dma);
+
+	    	    break;
+		}
+fail:
 	    }
-	    sig++;  /* try next signature with this configuration */
-	}
-	cfg++;      /* try next configuration */
     }
 
-    return present;
+    if (! present)
+       printk ("Failed initialization of WD-7000 SCSI card!\n");
+
+    return (present);
 }
 
 
diff -urN linux-2.0.15/drivers/sound/dmabuf.c linux-2.0.18/drivers/sound/dmabuf.c
--- linux-2.0.15/drivers/sound/dmabuf.c	Mon Aug 26 22:12:38 1996
+++ linux-2.0.18/drivers/sound/dmabuf.c	Thu Sep  5 17:47:30 1996
@@ -1182,7 +1182,7 @@
       if (!audio_devs[dev]->go)
 	tmout = 0;
       else
-	tmout = 2 * HZ;
+	tmout = 10 * HZ;
 
 
       {
diff -urN linux-2.0.15/drivers/sound/lowlevel/Makefile linux-2.0.18/drivers/sound/lowlevel/Makefile
--- linux-2.0.15/drivers/sound/lowlevel/Makefile	Mon Aug 26 22:12:52 1996
+++ linux-2.0.18/drivers/sound/lowlevel/Makefile	Sat Sep  7 09:53:58 1996
@@ -9,7 +9,7 @@
 endif
 
 lowlevel.o:	$(OBJS)
-	ld -r -o lowlevel.o $(OBJS)
+	$(LD) -r -o lowlevel.o $(OBJS)
 
 clean:
 	rm -f core x y z *~ *.o
diff -urN linux-2.0.15/fs/binfmt_elf.c linux-2.0.18/fs/binfmt_elf.c
--- linux-2.0.15/fs/binfmt_elf.c	Mon Aug 26 22:12:57 1996
+++ linux-2.0.18/fs/binfmt_elf.c	Sat Sep  7 09:54:01 1996
@@ -258,7 +258,7 @@
 			    eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr),
 			    elf_prot,
 			    elf_type,
-			    ELF_PAGESTART(eppnt->p_offset));
+			    eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr));
 	    
 	    if (error > -1024UL) {
 	      /* Real error */
@@ -585,7 +585,8 @@
 					elf_prot,
 					(MAP_FIXED | MAP_PRIVATE |
 					 MAP_DENYWRITE | MAP_EXECUTABLE),
-					ELF_PAGESTART(elf_ppnt->p_offset));
+					(elf_ppnt->p_offset -
+					 ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
 			
 #ifdef LOW_ELF_STACK
 			if (ELF_PAGESTART(elf_ppnt->p_vaddr) < elf_stack) 
@@ -802,10 +803,12 @@
 	/* Now use mmap to map the library into memory. */
 	error = do_mmap(file,
 			ELF_PAGESTART(elf_phdata->p_vaddr),
-			elf_phdata->p_filesz + ELF_PAGEOFFSET(elf_phdata->p_vaddr),
+			(elf_phdata->p_filesz +
+			 ELF_PAGEOFFSET(elf_phdata->p_vaddr)),
 			PROT_READ | PROT_WRITE | PROT_EXEC,
 			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
-			ELF_PAGESTART(elf_phdata->p_offset));
+			(elf_phdata->p_offset -
+			 ELF_PAGEOFFSET(elf_phdata->p_vaddr)));
 
 	k = elf_phdata->p_vaddr + elf_phdata->p_filesz;
 	if (k > elf_bss) elf_bss = k;
diff -urN linux-2.0.15/fs/buffer.c linux-2.0.18/fs/buffer.c
--- linux-2.0.15/fs/buffer.c	Fri Aug 30 10:39:32 1996
+++ linux-2.0.18/fs/buffer.c	Sat Sep  7 09:54:03 1996
@@ -1046,10 +1046,6 @@
 		return NULL;
 	bh = unused_list;
 	unused_list = bh->b_next_free;
-	bh->b_next_free = NULL;
-	bh->b_data = NULL;
-	bh->b_size = 0;
-	bh->b_state = 0;
 	return bh;
 }
 
@@ -1070,11 +1066,18 @@
 		bh = get_unused_buffer_head();
 		if (!bh)
 			goto no_grow;
+
+		bh->b_dev = B_FREE;  /* Flag as unused */
 		bh->b_this_page = head;
 		head = bh;
-		bh->b_data = (char *) (page+offset);
+
+		bh->b_state = 0;
+		bh->b_next_free = NULL;
+		bh->b_count = 0;
 		bh->b_size = size;
-		bh->b_dev = B_FREE;  /* Flag as unused */
+
+		bh->b_data = (char *) (page+offset);
+		bh->b_list = 0;
 	}
 	return head;
 /*
@@ -1128,13 +1131,11 @@
  * This function expects the page to be locked and may return before I/O is complete.
  * You then have to check page->locked, page->uptodate, and maybe wait on page->wait.
  */
-int brw_page(int rw, unsigned long address, kdev_t dev, int b[], int size, int bmap)
+int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap)
 {
 	struct buffer_head *bh, *prev, *next, *arr[MAX_BUF_PER_PAGE];
 	int block, nr;
-	struct page *page;
 
-	page = mem_map + MAP_NR(address);
 	if (!PageLocked(page))
 		panic("brw_page: page not locked for I/O");
 	clear_bit(PG_uptodate, &page->flags);
@@ -1144,7 +1145,7 @@
 	 * They do _not_ show up in the buffer hash table!
 	 * They are _not_ registered in page->buffers either!
 	 */
-	bh = create_buffers(address, size);
+	bh = create_buffers(page_address(page), size);
 	if (!bh) {
 		clear_bit(PG_locked, &page->flags);
 		wake_up(&page->wait);
@@ -1336,7 +1337,7 @@
 	} while (i > 0);
 
 	/* IO start */
-	brw_page(READ, page_address(page), inode->i_dev, nr, inode->i_sb->s_blocksize, 1);
+	brw_page(READ, page, inode->i_dev, nr, inode->i_sb->s_blocksize, 1);
 	return 0;
 }
 
@@ -1399,10 +1400,7 @@
 
 static inline int buffer_waiting(struct buffer_head * bh)
 {
-	struct wait_queue *wait = bh->b_wait;
-	struct wait_queue *head = WAIT_QUEUE_HEAD(&bh->b_wait);
-
-	return wait && wait != head;
+	return waitqueue_active(&bh->b_wait);
 }
 
 /*
@@ -1425,7 +1423,7 @@
 			return 0;
 		if (tmp->b_count || buffer_protected(tmp) ||
 		    buffer_dirty(tmp) || buffer_locked(tmp) ||
-		    buffer_waiting(bh))
+		    buffer_waiting(tmp))
 			return 0;
 		if (priority && buffer_touched(tmp))
 			return 0;
diff -urN linux-2.0.15/fs/ext2/super.c linux-2.0.18/fs/ext2/super.c
--- linux-2.0.15/fs/ext2/super.c	Thu Aug 29 21:24:13 1996
+++ linux-2.0.18/fs/ext2/super.c	Fri Sep  6 22:23:28 1996
@@ -15,8 +15,6 @@
 
 #include <linux/module.h>
 
-#include <linux/config.h>
-
 #include <stdarg.h>
 
 #include <asm/bitops.h>
diff -urN linux-2.0.15/fs/nfs/rpcsock.c linux-2.0.18/fs/nfs/rpcsock.c
--- linux-2.0.15/fs/nfs/rpcsock.c	Mon Aug 26 22:13:34 1996
+++ linux-2.0.18/fs/nfs/rpcsock.c	Sat Sep  7 09:54:07 1996
@@ -564,7 +564,7 @@
 	unsigned long	t0 = jiffies;
 
 	rsock->shutdown = 1;
-	while (rsock->pending || rsock->backlog) {
+	while (rsock->pending || waitqueue_active(&rsock->backlog)) {
 		interruptible_sleep_on(&rsock->shutwait);
 		if (current->signal & ~current->blocked)
 			return -EINTR;
diff -urN linux-2.0.15/fs/umsdos/inode.c linux-2.0.18/fs/umsdos/inode.c
--- linux-2.0.15/fs/umsdos/inode.c	Mon Aug 26 22:13:49 1996
+++ linux-2.0.18/fs/umsdos/inode.c	Sat Sep  7 09:54:09 1996
@@ -227,7 +227,7 @@
 	if (S_ISDIR(inode->i_mode)
 		&& (inode->u.umsdos_i.u.dir_info.creating != 0
 			|| inode->u.umsdos_i.u.dir_info.looking != 0
-			|| inode->u.umsdos_i.u.dir_info.p != NULL)){
+			|| waitqueue_active(&inode->u.umsdos_i.u.dir_info.p))){
 		Printk (("read inode %d %d %p\n"
 			,inode->u.umsdos_i.u.dir_info.creating
 			,inode->u.umsdos_i.u.dir_info.looking
diff -urN linux-2.0.15/include/asm-i386/pgtable.h linux-2.0.18/include/asm-i386/pgtable.h
--- linux-2.0.15/include/asm-i386/pgtable.h	Mon Aug 26 22:14:17 1996
+++ linux-2.0.18/include/asm-i386/pgtable.h	Sun Sep  8 12:16:18 1996
@@ -25,6 +25,7 @@
 #define flush_cache_range(mm, start, end)	do { } while (0)
 #define flush_cache_page(vma, vmaddr)		do { } while (0)
 #define flush_page_to_ram(page)			do { } while (0)
+#define flush_pages_to_ram(page)		do { } while (0)
 
 /*
  * TLB flushing:
@@ -53,6 +54,7 @@
 
 #define flush_tlb() __flush_tlb()
 #define flush_tlb_all() __flush_tlb()
+#define local_flush_tlb() __flush_tlb()
 
 static inline void flush_tlb_mm(struct mm_struct *mm)
 {
diff -urN linux-2.0.15/include/asm-m68k/atarihw.h linux-2.0.18/include/asm-m68k/atarihw.h
--- linux-2.0.15/include/asm-m68k/atarihw.h	Mon Aug 26 22:14:29 1996
+++ linux-2.0.18/include/asm-m68k/atarihw.h	Thu Sep  5 17:47:47 1996
@@ -321,7 +321,7 @@
  };
 # define scc ((*(volatile struct SCC*)SCC_BAS))
 
-/* The ESCC (Z85230) in an Atari ST. The channels are revered! */
+/* The ESCC (Z85230) in an Atari ST. The channels are reversed! */
 # define st_escc ((*(volatile struct SCC*)0xfffffa31))
 # define st_escc_dsr ((*(volatile char *)0xfffffa39))
 
diff -urN linux-2.0.15/include/asm-m68k/atariints.h linux-2.0.18/include/asm-m68k/atariints.h
--- linux-2.0.15/include/asm-m68k/atariints.h	Mon Aug 26 22:14:30 1996
+++ linux-2.0.18/include/asm-m68k/atariints.h	Sun Sep  8 14:31:32 1996
@@ -216,5 +216,6 @@
 }
 
 unsigned long atari_register_vme_int( void );
+void atari_unregister_vme_int( unsigned long );
 
 #endif /* linux/atariints.h */
diff -urN linux-2.0.15/include/asm-m68k/ide.h linux-2.0.18/include/asm-m68k/ide.h
--- linux-2.0.15/include/asm-m68k/ide.h	Thu Aug 29 22:58:31 1996
+++ linux-2.0.18/include/asm-m68k/ide.h	Sun Sep  8 15:12:57 1996
@@ -4,6 +4,8 @@
 /* Copyright(c) 1996 Kars de Jong */
 /* Based on the ide driver from 1.2.13pl8 */
 
+#include <linux/config.h>
+
 #ifdef CONFIG_AMIGA
 #include <asm/amigahw.h>
 #include <asm/amihdreg.h>
diff -urN linux-2.0.15/include/asm-m68k/string.h linux-2.0.18/include/asm-m68k/string.h
--- linux-2.0.15/include/asm-m68k/string.h	Mon Aug 26 22:14:38 1996
+++ linux-2.0.18/include/asm-m68k/string.h	Sun Sep  8 15:13:02 1996
@@ -1,6 +1,7 @@
 #ifndef _M68K_STRING_H_
 #define _M68K_STRING_H_
 
+#include <linux/config.h>
 #include <asm/page.h>
 
 #define __HAVE_ARCH_STRCPY
@@ -189,7 +190,7 @@
 }
 
 #define __HAVE_ARCH_MEMSET
-extern inline void * __memset_g(void * s,int c,size_t count)
+extern inline void * __memset_g(void * s, int c, size_t count)
 {
   void *xs = s;
   size_t temp;
@@ -247,17 +248,45 @@
  * get_empty_page() so I think it is worth it anyway. Besides, the
  * 680[46]0 do not really care about misaligned writes due to their
  * copy-back cache.
- *                                            30/06/96 - Jes Sorensen
+ *
+ * The optimized case for the 680[46]0 is implemented using the move16
+ * instruction. My tests showed that this implementation is 35-45%
+ * faster than the original implementation using movel, the only
+ * caveat is that the destination address must be 16-byte aligned.
+ *                                            01/09/96 - Jes Sorensen
  */
 extern inline void * __memset_page(void * s,int c,size_t count)
 {
   unsigned long data, tmp;
-  void *xs = s;
+  void *xs, *sp;
+
+  xs = sp = s;
 
   c = c & 255;
   data = c | (c << 8);
   data |= data << 16;
 
+#if defined(CONFIG_OPTIMIZE_040) || defined(CONFIG_OPTIMIZE_060)
+
+  if (((unsigned long) s) & 0x0f)
+	  memset(s, c, count);
+  else{
+	  *((unsigned long *)(s))++ = data;
+	  *((unsigned long *)(s))++ = data;
+	  *((unsigned long *)(s))++ = data;
+	  *((unsigned long *)(s))++ = data;
+
+	  __asm__ __volatile__("1:\t"
+			       "move16 %2@+,%0@+\n\t"
+			       "subqw  #8,%2\n\t"
+			       "subqw  #8,%2\n\t"
+			       "dbra   %1,1b\n\t"
+			       : "=a" (s), "=d" (tmp)
+			       : "a" (sp), "0" (s), "1" ((count - 16) / 16 - 1)
+			       );
+  }
+
+#else
   __asm__ __volatile__("1:\t"
 		       "movel %2,%0@+\n\t"
 		       "movel %2,%0@+\n\t"
@@ -271,6 +300,8 @@
 		       : "=a" (s), "=d" (tmp)
 		       : "d" (data), "0" (s), "1" (count / 32 - 1)
 		       );
+#endif
+
   return xs;
 }
 
@@ -282,20 +313,41 @@
 #define memset(s, c, count) \
 (__builtin_constant_p(count) ? \
  __memset_const((s),(c),(count)) : \
- __memset_g((s),(c),(count)))
+ memset((s),(c),(count)))
 
 #define __HAVE_ARCH_MEMCPY
 /*
  * __builtin_memcpy() does not handle page-sized memcpys very well,
  * thus following the same assumptions as for page-sized memsets, this
  * function copies page-sized areas using an unrolled loop, without
- * considering alignment. - Jes
+ * considering alignment.
+ *
+ * For the 680[46]0 only kernels we use the move16 instruction instead
+ * as it writes through the data-cache, invalidating the cache-lines
+ * touched. In this way we do not use up the entire data-cache (well,
+ * half of it on the 68060) by copying a page. An unrolled loop of two
+ * move16 instructions seem to the fastest. The only caveat is that
+ * both source and destination must be 16-byte aligned, if not we fall
+ * back to the generic memcpy function.  - Jes
  */
 extern inline void * __memcpy_page(void * to, const void * from, size_t count)
 {
   unsigned long tmp;
   void *xto = to;
 
+#if defined(CONFIG_OPTIMIZE_040) || defined(CONFIG_OPTIMIZE_060)
+
+  if (((unsigned long) to | (unsigned long) from) & 0x0f)
+	  return memcpy(to, from, count);
+
+  __asm__ __volatile__("1:\t"
+		       "move16 %1@+,%0@+\n\t"
+		       "move16 %1@+,%0@+\n\t"
+		       "dbra  %2,1b\n\t"
+		       : "=a" (to), "=a" (from), "=d" (tmp)
+		       : "0" (to), "1" (from) , "2" (count / 32 - 1)
+		       );
+#else
   __asm__ __volatile__("1:\t"
 		       "movel %1@+,%0@+\n\t"
 		       "movel %1@+,%0@+\n\t"
@@ -309,6 +361,7 @@
 		       : "=a" (to), "=a" (from), "=d" (tmp)
 		       : "0" (to), "1" (from) , "2" (count / 32 - 1)
 		       );
+#endif
   return xto;
 }
 
diff -urN linux-2.0.15/include/linux/ext2_fs.h linux-2.0.18/include/linux/ext2_fs.h
--- linux-2.0.15/include/linux/ext2_fs.h	Thu Aug 29 21:25:05 1996
+++ linux-2.0.18/include/linux/ext2_fs.h	Sun Sep  8 15:13:15 1996
@@ -558,8 +558,6 @@
 #endif
 }
 
-#include <linux/config.h>
-
 #define BYTE_SWAP(A) A
 
 #endif	/* __KERNEL__ */
diff -urN linux-2.0.15/include/linux/fs.h linux-2.0.18/include/linux/fs.h
--- linux-2.0.15/include/linux/fs.h	Fri Aug 30 08:25:09 1996
+++ linux-2.0.18/include/linux/fs.h	Sun Sep  8 18:31:30 1996
@@ -653,7 +653,7 @@
 extern int generic_readpage(struct inode *, struct page *);
 extern int generic_file_read(struct inode *, struct file *, char *, int);
 extern int generic_file_mmap(struct inode *, struct file *, struct vm_area_struct *);
-extern int brw_page(int, unsigned long, kdev_t, int [], int, int);
+extern int brw_page(int, struct page *, kdev_t, int [], int, int);
 
 extern void put_super(kdev_t dev);
 unsigned long generate_cluster(kdev_t dev, int b[], int size);
diff -urN linux-2.0.15/include/linux/isdn.h linux-2.0.18/include/linux/isdn.h
--- linux-2.0.15/include/linux/isdn.h	Mon Aug 26 22:16:03 1996
+++ linux-2.0.18/include/linux/isdn.h	Thu Sep  5 17:47:57 1996
@@ -1,4 +1,4 @@
-/* $Id: isdn.h,v 1.15 1996/06/15 14:56:57 fritz Exp $
+/* $Id: isdn.h,v 1.16 1996/08/12 16:20:56 hipp Exp $
  *
  * Main header for the Linux ISDN subsystem (linklevel).
  *
@@ -21,6 +21,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdn.h,v $
+ * Revision 1.16  1996/08/12 16:20:56  hipp
+ * renamed ppp_minor to ppp_slot
+ *
  * Revision 1.15  1996/06/15 14:56:57  fritz
  * Added version signatures for data structures used
  * by userlevel programs.
@@ -319,7 +322,7 @@
   struct enet_statistics stats;        /* Ethernet Statistics              */
   int                    isdn_device;  /* Index to isdn-device             */
   int                    isdn_channel; /* Index to isdn-channel            */
-  int			 ppp_minor;    /* PPPD device minor number         */
+  int			 ppp_slot;     /* PPPD device slot number          */
   int                    pre_device;   /* Preselected isdn-device          */
   int                    pre_channel;  /* Preselected isdn-channel         */
   int                    exclusive;    /* If non-zero idx to reserved chan.*/
@@ -549,6 +552,7 @@
   unsigned int maxcid;
   isdn_net_local *lp;
   int unit; 
+  int minor;
   long last_link_seqno;
   long mp_seqno;
   long range;
diff -urN linux-2.0.15/include/linux/major.h linux-2.0.18/include/linux/major.h
--- linux-2.0.15/include/linux/major.h	Mon Aug 26 22:16:09 1996
+++ linux-2.0.18/include/linux/major.h	Sat Sep  7 09:54:25 1996
@@ -77,7 +77,7 @@
   ((M) == SCSI_DISK_MAJOR	\
    || (M) == SCSI_CDROM_MAJOR)
 
-static inline int scsi_blk_major(int m) {
+static __inline__ int scsi_blk_major(int m) {
 	return SCSI_BLK_MAJOR(m);
 }
 
diff -urN linux-2.0.15/include/linux/pagemap.h linux-2.0.18/include/linux/pagemap.h
--- linux-2.0.15/include/linux/pagemap.h	Fri Aug 30 08:25:12 1996
+++ linux-2.0.18/include/linux/pagemap.h	Sun Sep  8 18:31:33 1996
@@ -42,13 +42,10 @@
 #undef s
 }
 
-#define page_hash(inode,offset) page_hash_table[_page_hashfn(inode,offset)]
+#define page_hash(inode,offset) (page_hash_table+_page_hashfn(inode,offset))
 
-static inline struct page * find_page(struct inode * inode, unsigned long offset)
+static inline struct page * __find_page(struct inode * inode, unsigned long offset, struct page *page)
 {
-	struct page *page;
-
-	page = page_hash(inode, offset);
 	goto inside;
 	for (;;) {
 		page = page->next_hash;
@@ -67,9 +64,14 @@
 	return page;
 }
 
+static inline struct page *find_page(struct inode * inode, unsigned long offset)
+{
+	return __find_page(inode, offset, *page_hash(inode, offset));
+}
+
 static inline void remove_page_from_hash_queue(struct page * page)
 {
-	struct page **p = &page_hash(page->inode,page->offset);
+	struct page **p = page_hash(page->inode,page->offset);
 
 	page_cache_size--;
 	if (page->next_hash)
@@ -81,10 +83,8 @@
 	page->next_hash = page->prev_hash = NULL;
 }
 
-static inline void add_page_to_hash_queue(struct inode * inode, struct page * page)
+static inline void __add_page_to_hash_queue(struct page * page, struct page **p)
 {
-	struct page **p = &page_hash(inode,page->offset);
-
 	page_cache_size++;
 	set_bit(PG_referenced, &page->flags);
 	page->age = PAGE_AGE_VALUE;
@@ -93,6 +93,12 @@
 		page->next_hash->prev_hash = page;
 	*p = page;
 }
+
+static inline void add_page_to_hash_queue(struct page * page, struct inode * inode, unsigned long offset)
+{
+	__add_page_to_hash_queue(page, page_hash(inode,offset));
+}
+
 
 static inline void remove_page_from_inode_queue(struct page * page)
 {
diff -urN linux-2.0.15/include/linux/sbpcd.h linux-2.0.18/include/linux/sbpcd.h
--- linux-2.0.15/include/linux/sbpcd.h	Mon Aug 26 22:16:27 1996
+++ linux-2.0.18/include/linux/sbpcd.h	Thu Sep  5 17:47:58 1996
@@ -134,16 +134,38 @@
 /* runtime by use of the CDROMAUDIOBUFSIZ ioctl.    */
 #define READ_AUDIO 0
 
+/* Optimizations for the Teac CD-55A drive read performance.
+ * SBP_TEAC_SPEED can be changed here, or one can set the 
+ * variable "teac" when loading as a module.
+ * Valid settings are:
+ *   0 - very slow - the recommended "DISTRIBUTION 1" setup.
+ *   1 - 2x performance with little overhead. No busy waiting.
+ *   2 - 4x performance with 5ms overhead per read. Busy wait.
+ *
+ * Setting SBP_TEAC_SPEED or the variable 'teac' to anything
+ * other than 0 may cause problems. If you run into them, first
+ * change SBP_TEAC_SPEED back to 0 and see if your drive responds
+ * normally. If yes, you are "allowed" to report your case - to help
+ * me with the driver, not to solve your hassle. Don´t mail if you
+ * simply are stuck into your own "tuning" experiments, you know?
+ */
+#define SBP_TEAC_SPEED 1
+
 /*==========================================================================*/
 /*==========================================================================*/
 /*
- * nothing to change below here if you are not experimenting
+ * nothing to change below here if you are not fully aware what you're doing
  */
 #ifndef _LINUX_SBPCD_H
 
 #define _LINUX_SBPCD_H
 /*==========================================================================*/
 /*==========================================================================*/
+/*
+ * driver's own read_ahead, data mode
+ */
+#define SBP_BUFFER_FRAMES 8 
+
 #define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */
 #undef  FUTURE
 #undef SAFE_MIXED
@@ -151,9 +173,14 @@
 #define TEST_UPC 0
 #define SPEA_TEST 0
 #define TEST_STI 0
+#define OLD_BUSY 0
 #undef PATH_CHECK
 #ifndef SOUND_BASE
 #define SOUND_BASE 0
+#endif
+#if DISTRIBUTION
+#undef SBP_TEAC_SPEED
+#define SBP_TEAC_SPEED 0
 #endif
 /*==========================================================================*/
 /*
diff -urN linux-2.0.15/include/linux/sched.h linux-2.0.18/include/linux/sched.h
--- linux-2.0.15/include/linux/sched.h	Fri Aug 30 08:25:11 1996
+++ linux-2.0.18/include/linux/sched.h	Sun Sep  8 18:31:32 1996
@@ -260,8 +260,8 @@
 #define PF_DUMPCORE	0x00000200	/* dumped core */
 #define PF_SIGNALED	0x00000400	/* killed by a signal */
 
-#define PF_STARTING	0x00000100	/* being created */
-#define PF_EXITING	0x00000200	/* getting shut down */
+#define PF_STARTING	0x00000002	/* being created */
+#define PF_EXITING	0x00000004	/* getting shut down */
 
 #define PF_USEDFPU	0x00100000	/* Process used the FPU this quantum (SMP only) */
 #define PF_DTRACE	0x00200000	/* delayed trace (used on m68k) */
diff -urN linux-2.0.15/include/linux/soundcard.h linux-2.0.18/include/linux/soundcard.h
--- linux-2.0.15/include/linux/soundcard.h	Mon Aug 26 22:16:34 1996
+++ linux-2.0.18/include/linux/soundcard.h	Thu Sep  5 17:48:02 1996
@@ -742,15 +742,13 @@
 /* Note!	Number 31 cannot be used since the sign bit is reserved */
 
 
-#ifdef NO_LONGER_AVAILABLE
 /*
- * The following unsupported macros will be removed from the API in near
- * future.
+ * The following unsupported macros are no longer functional.
+ * Use SOUND_MIXER_PRIVATE# macros in future.
  */
-#define SOUND_MIXER_ENHANCE	29	/* Enhanced stereo (0, 40, 60 or 80) */
-#define SOUND_MIXER_MUTE	28	/* 0 or 1 */
-#define SOUND_MIXER_LOUD	30	/* 0 or 1 */
-#endif
+#define SOUND_MIXER_ENHANCE	31
+#define SOUND_MIXER_MUTE	31
+#define SOUND_MIXER_LOUD	31
 
 
 #define SOUND_DEVICE_LABELS	{"Vol  ", "Bass ", "Trebl", "Synth", "Pcm  ", "Spkr ", "Line ", \
@@ -790,11 +788,10 @@
 #define SOUND_MASK_LINE2	(1 << SOUND_MIXER_LINE2)
 #define SOUND_MASK_LINE3	(1 << SOUND_MIXER_LINE3)
 
-#ifdef NO_LONGER_AVAILABLE
+/* Obsolete macros */
 #define SOUND_MASK_MUTE		(1 << SOUND_MIXER_MUTE)
 #define SOUND_MASK_ENHANCE	(1 << SOUND_MIXER_ENHANCE)
 #define SOUND_MASK_LOUD		(1 << SOUND_MIXER_LOUD)
-#endif
 
 #define MIXER_READ(dev)		_IOR('M', dev, int)
 #define SOUND_MIXER_READ_VOLUME		MIXER_READ(SOUND_MIXER_VOLUME)
@@ -814,11 +811,11 @@
 #define SOUND_MIXER_READ_LINE1		MIXER_READ(SOUND_MIXER_LINE1)
 #define SOUND_MIXER_READ_LINE2		MIXER_READ(SOUND_MIXER_LINE2)
 #define SOUND_MIXER_READ_LINE3		MIXER_READ(SOUND_MIXER_LINE3)
-#ifdef NO_LONGER_AVAILABLE
+
+/* Obsolete macros */
 #define SOUND_MIXER_READ_MUTE		MIXER_READ(SOUND_MIXER_MUTE)
 #define SOUND_MIXER_READ_ENHANCE	MIXER_READ(SOUND_MIXER_ENHANCE)
 #define SOUND_MIXER_READ_LOUD		MIXER_READ(SOUND_MIXER_LOUD)
-#endif
 
 #define SOUND_MIXER_READ_RECSRC		MIXER_READ(SOUND_MIXER_RECSRC)
 #define SOUND_MIXER_READ_DEVMASK	MIXER_READ(SOUND_MIXER_DEVMASK)
@@ -844,11 +841,11 @@
 #define SOUND_MIXER_WRITE_LINE1		MIXER_WRITE(SOUND_MIXER_LINE1)
 #define SOUND_MIXER_WRITE_LINE2		MIXER_WRITE(SOUND_MIXER_LINE2)
 #define SOUND_MIXER_WRITE_LINE3		MIXER_WRITE(SOUND_MIXER_LINE3)
-#ifdef NO_LONGER_AVAILABLE
+
+/* Obsolete macros */
 #define SOUND_MIXER_WRITE_MUTE		MIXER_WRITE(SOUND_MIXER_MUTE)
 #define SOUND_MIXER_WRITE_ENHANCE	MIXER_WRITE(SOUND_MIXER_ENHANCE)
 #define SOUND_MIXER_WRITE_LOUD		MIXER_WRITE(SOUND_MIXER_LOUD)
-#endif
 
 #define SOUND_MIXER_WRITE_RECSRC	MIXER_WRITE(SOUND_MIXER_RECSRC)
 
diff -urN linux-2.0.15/include/linux/wait.h linux-2.0.18/include/linux/wait.h
--- linux-2.0.15/include/linux/wait.h	Fri Aug 30 08:24:58 1996
+++ linux-2.0.18/include/linux/wait.h	Thu Sep  5 17:48:04 1996
@@ -20,6 +20,12 @@
 	*q = WAIT_QUEUE_HEAD(q);
 }
 
+static inline int waitqueue_active(struct wait_queue **q)
+{
+	struct wait_queue *head = *q;
+	return head && head != WAIT_QUEUE_HEAD(q);
+}
+
 struct semaphore {
 	int count;
 	struct wait_queue * wait;
diff -urN linux-2.0.15/include/net/tcp.h linux-2.0.18/include/net/tcp.h
--- linux-2.0.15/include/net/tcp.h	Fri Aug 30 10:09:17 1996
+++ linux-2.0.18/include/net/tcp.h	Sun Sep  8 19:20:17 1996
@@ -161,6 +161,7 @@
 
 extern void tcp_enqueue_partial(struct sk_buff *, struct sock *);
 extern struct sk_buff * tcp_dequeue_partial(struct sock *);
+extern void tcp_shrink_skb(struct sock *,struct sk_buff *,u32);
 
 /* tcp_input.c */
 extern void tcp_cache_zap(void);
diff -urN linux-2.0.15/init/main.c linux-2.0.18/init/main.c
--- linux-2.0.15/init/main.c	Mon Aug 26 22:17:03 1996
+++ linux-2.0.18/init/main.c	Sat Sep  7 09:54:28 1996
@@ -98,6 +98,7 @@
 extern void fdomain_setup(char *str, int *ints);
 extern void in2000_setup(char *str, int *ints);
 extern void NCR53c406a_setup(char *str, int *ints);
+extern void wd7000_setup(char *str, int *ints);
 extern void ppa_setup(char *str, int *ints);
 extern void scsi_luns_setup(char *str, int *ints);
 extern void sound_setup(char *str, int *ints);
@@ -340,6 +341,9 @@
 #ifdef CONFIG_SCSI_IN2000
 	{ "in2000=", in2000_setup},
 #endif
+#ifdef CONFIG_SCSI_7000FASST
+	{ "wd7000=", wd7000_setup},
+#endif
 #ifdef CONFIG_SCSI_PPA
         { "ppa=", ppa_setup },
 #endif
@@ -977,8 +981,17 @@
 		}
 	}
 #endif
+	
+	/*
+	 *	This keeps serial console MUCH cleaner, but does assume
+	 *	the console driver checks there really is a video device
+	 *	attached (Sparc effectively does).
+	 */
 
-	(void) open("/dev/tty1",O_RDWR,0);
+	if ((open("/dev/tty1",O_RDWR,0) < 0) &&
+	    (open("/dev/ttyS0",O_RDWR,0) < 0))
+		printk("Unable to open a initial console.\n");
+			
 	(void) dup(0);
 	(void) dup(0);
 
diff -urN linux-2.0.15/ipc/msg.c linux-2.0.18/ipc/msg.c
--- linux-2.0.15/ipc/msg.c	Mon Aug 26 22:17:05 1996
+++ linux-2.0.18/ipc/msg.c	Thu Sep  5 17:48:10 1996
@@ -193,8 +193,7 @@
 	msq->msg_lspid = current->pid;
 	msq->msg_stime = CURRENT_TIME;
 	restore_flags(flags);
-	if (msq->rwait)
-		wake_up (&msq->rwait);
+	wake_up (&msq->rwait);
 	return 0;
 }
 
@@ -350,8 +349,7 @@
 			msghdrs--; 
 			msq->msg_cbytes -= nmsg->msg_ts;
 			restore_flags(flags);
-			if (msq->wwait)
-				wake_up (&msq->wwait);
+			wake_up (&msq->wwait);
 			/*
 			 * Calls from kernel level (IPC_KERNELD set)
 			 * wants the message copied to kernel space!
@@ -438,8 +436,7 @@
 	msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL);
 	if (!msq) {
 		msgque[id] = (struct msqid_ds *) IPC_UNUSED;
-		if (msg_lock)
-			wake_up (&msg_lock);
+		wake_up (&msg_lock);
 		return -ENOMEM;
 	}
 	ipcp = &msq->msg_perm;
@@ -459,8 +456,7 @@
 		max_msqid = id;
 	msgque[id] = msq;
 	used_queues++;
-	if (msg_lock)
-		wake_up (&msg_lock);
+	wake_up (&msg_lock);
 	return (unsigned int) msq->msg_perm.seq * MSGMNI + id;
 }
 
@@ -525,11 +521,9 @@
 		while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED));
 	msgque[id] = (struct msqid_ds *) IPC_UNUSED;
 	used_queues--;
-	while (msq->rwait || msq->wwait) {
-		if (msq->rwait)
-			wake_up (&msq->rwait); 
-		if (msq->wwait)
-			wake_up (&msq->wwait);
+	while (waitqueue_active(&msq->rwait) || waitqueue_active(&msq->wwait)) {
+		wake_up (&msq->rwait); 
+		wake_up (&msq->wwait);
 		schedule(); 
 	}
 	for (msgp = msq->msg_first; msgp; msgp = msgh ) {
diff -urN linux-2.0.15/ipc/sem.c linux-2.0.18/ipc/sem.c
--- linux-2.0.15/ipc/sem.c	Mon Aug 26 22:17:05 1996
+++ linux-2.0.18/ipc/sem.c	Thu Sep  5 17:48:11 1996
@@ -103,8 +103,7 @@
 	if (!sma) {
 		semary[id] = (struct semid_ds *) IPC_UNUSED;
 		used_sems -= nsems;
-		if (sem_lock)
-			wake_up (&sem_lock);
+		wake_up (&sem_lock);
 		return -ENOMEM;
 	}
 	memset (sma, 0, size);
@@ -124,8 +123,7 @@
 		max_semid = id;
 	used_semids++;
 	semary[id] = sma;
-	if (sem_lock)
-		wake_up (&sem_lock);
+	wake_up (&sem_lock);
 	return (unsigned int) sma->sem_perm.seq * SEMMNI + id;
 }
 
diff -urN linux-2.0.15/ipc/shm.c linux-2.0.18/ipc/shm.c
--- linux-2.0.15/ipc/shm.c	Mon Aug 26 22:17:05 1996
+++ linux-2.0.18/ipc/shm.c	Thu Sep  5 17:48:12 1996
@@ -92,16 +92,14 @@
 	shp = (struct shmid_ds *) kmalloc (sizeof (*shp), GFP_KERNEL);
 	if (!shp) {
 		shm_segs[id] = (struct shmid_ds *) IPC_UNUSED;
-		if (shm_lock)
-			wake_up (&shm_lock);
+		wake_up (&shm_lock);
 		return -ENOMEM;
 	}
 
 	shp->shm_pages = (ulong *) kmalloc (numpages*sizeof(ulong),GFP_KERNEL);
 	if (!shp->shm_pages) {
 		shm_segs[id] = (struct shmid_ds *) IPC_UNUSED;
-		if (shm_lock)
-			wake_up (&shm_lock);
+		wake_up (&shm_lock);
 		kfree(shp);
 		return -ENOMEM;
 	}
@@ -125,8 +123,7 @@
 		max_shmid = id;
 	shm_segs[id] = shp;
 	used_segs++;
-	if (shm_lock)
-		wake_up (&shm_lock);
+	wake_up (&shm_lock);
 	return (unsigned int) shp->shm_perm.seq * SHMMNI + id;
 }
 
diff -urN linux-2.0.15/kernel/sched.c linux-2.0.18/kernel/sched.c
--- linux-2.0.15/kernel/sched.c	Fri Aug 30 08:24:59 1996
+++ linux-2.0.18/kernel/sched.c	Sat Sep  7 09:54:31 1996
@@ -1180,8 +1180,10 @@
 
 	p->policy = policy;
 	p->rt_priority = lp.sched_priority;
+	cli();
 	if (p->next_run)
 		move_last_runqueue(p);
+	sti();
 	schedule();
 
 	return 0;
@@ -1237,8 +1239,9 @@
 
 asmlinkage int sys_sched_yield(void)
 {
+	cli();
 	move_last_runqueue(current);
-
+	sti();
 	return 0;
 }
 
diff -urN linux-2.0.15/mm/filemap.c linux-2.0.18/mm/filemap.c
--- linux-2.0.15/mm/filemap.c	Mon Aug 26 22:17:16 1996
+++ linux-2.0.18/mm/filemap.c	Sat Sep  7 09:54:34 1996
@@ -255,13 +255,14 @@
 }
 
 static inline void add_to_page_cache(struct page * page,
-	struct inode * inode, unsigned long offset)
+	struct inode * inode, unsigned long offset,
+	struct page **hash)
 {
 	page->count++;
 	page->flags &= ~((1 << PG_uptodate) | (1 << PG_error));
 	page->offset = offset;
 	add_page_to_inode_queue(inode, page);
-	add_page_to_hash_queue(inode, page);
+	__add_page_to_hash_queue(page, hash);
 }
 
 /*
@@ -272,32 +273,31 @@
 static unsigned long try_to_read_ahead(struct inode * inode, unsigned long offset, unsigned long page_cache)
 {
 	struct page * page;
+	struct page ** hash;
 
 	offset &= PAGE_MASK;
-	if (!page_cache) {
+	switch (page_cache) {
+	case 0:
 		page_cache = __get_free_page(GFP_KERNEL);
 		if (!page_cache)
-			return 0;
-	}
-	if (offset >= inode->i_size)
-		return page_cache;
-#if 1
-	page = find_page(inode, offset);
-	if (page) {
+			break;
+	default:
+		if (offset >= inode->i_size)
+			break;
+		hash = page_hash(inode, offset);
+		page = __find_page(inode, offset, *hash);
+		if (!page) {
+			/*
+			 * Ok, add the new page to the hash-queues...
+			 */
+			page = mem_map + MAP_NR(page_cache);
+			add_to_page_cache(page, inode, offset, hash);
+			inode->i_op->readpage(inode, page);
+			page_cache = 0;
+		}
 		release_page(page);
-		return page_cache;
 	}
-	/*
-	 * Ok, add the new page to the hash-queues...
-	 */
-	page = mem_map + MAP_NR(page_cache);
-	add_to_page_cache(page, inode, offset);
-	inode->i_op->readpage(inode, page);
-	free_page(page_cache);
-	return 0;
-#else
 	return page_cache;
-#endif
 }
 
 /* 
@@ -457,13 +457,12 @@
 #endif
 
 static inline unsigned long generic_file_readahead(int reada_ok, struct file * filp, struct inode * inode,
-	unsigned long pos, struct page * page,
+	unsigned long ppos, struct page * page,
 	unsigned long page_cache)
 {
 	unsigned long max_ahead, ahead;
-	unsigned long raend, ppos;
+	unsigned long raend;
 
-	ppos = pos & PAGE_MASK;
 	raend = filp->f_raend & PAGE_MASK;
 	max_ahead = 0;
 
@@ -607,7 +606,7 @@
 	} else {
 		unsigned long needed;
 
-		needed = ((pos + count) & PAGE_MASK) - (pos & PAGE_MASK);
+		needed = ((pos + count) & PAGE_MASK) - ppos;
 
 		if (filp->f_ramax < needed)
 			filp->f_ramax = needed;
@@ -619,7 +618,7 @@
 	}
 
 	for (;;) {
-		struct page *page;
+		struct page *page, **hash;
 
 		if (pos >= inode->i_size)
 			break;
@@ -627,7 +626,8 @@
 		/*
 		 * Try to find the data in the page cache..
 		 */
-		page = find_page(inode, pos & PAGE_MASK);
+		hash = page_hash(inode, pos & PAGE_MASK);
+		page = __find_page(inode, pos & PAGE_MASK, *hash);
 		if (!page)
 			goto no_cached_page;
 
@@ -640,7 +640,7 @@
  * the page has been rewritten.
  */
 		if (PageUptodate(page) || PageLocked(page))
-			page_cache = generic_file_readahead(reada_ok, filp, inode, pos, page, page_cache);
+			page_cache = generic_file_readahead(reada_ok, filp, inode, pos & PAGE_MASK, page, page_cache);
 		else if (reada_ok && filp->f_ramax > MIN_READAHEAD)
 				filp->f_ramax = MIN_READAHEAD;
 
@@ -696,7 +696,7 @@
 		 */
 		page = mem_map + MAP_NR(page_cache);
 		page_cache = 0;
-		add_to_page_cache(page, inode, pos & PAGE_MASK);
+		add_to_page_cache(page, inode, pos & PAGE_MASK, hash);
 
 		/*
 		 * Error handling is tricky. If we get a read error,
@@ -764,7 +764,7 @@
 static unsigned long filemap_nopage(struct vm_area_struct * area, unsigned long address, int no_share)
 {
 	unsigned long offset;
-	struct page * page;
+	struct page * page, **hash;
 	struct inode * inode = area->vm_inode;
 	unsigned long old_page, new_page;
 
@@ -776,7 +776,8 @@
 	/*
 	 * Do we have something in the page cache already?
 	 */
-	page = find_page(inode, offset);
+	hash = page_hash(inode, offset);
+	page = __find_page(inode, offset, *hash);
 	if (!page)
 		goto no_cached_page;
 
@@ -841,7 +842,7 @@
 	 */
 	page = mem_map + MAP_NR(new_page);
 	new_page = 0;
-	add_to_page_cache(page, inode, offset);
+	add_to_page_cache(page, inode, offset, hash);
 
 	if (inode->i_op->readpage(inode, page) != 0)
 		goto failure;
diff -urN linux-2.0.15/mm/mmap.c linux-2.0.18/mm/mmap.c
--- linux-2.0.15/mm/mmap.c	Mon Aug 26 22:17:17 1996
+++ linux-2.0.18/mm/mmap.c	Sat Sep  7 09:54:35 1996
@@ -144,7 +144,7 @@
 	if ((len = PAGE_ALIGN(len)) == 0)
 		return addr;
 
-	if (addr > TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE-len)
+	if (len > TASK_SIZE || addr > TASK_SIZE-len)
 		return -EINVAL;
 
 	/* offset overflow? */
@@ -198,8 +198,6 @@
 
 	if (flags & MAP_FIXED) {
 		if (addr & ~PAGE_MASK)
-			return -EINVAL;
-		if (len > TASK_SIZE || addr > TASK_SIZE - len)
 			return -EINVAL;
 	} else {
 		addr = get_unmapped_area(addr, len);
diff -urN linux-2.0.15/mm/page_alloc.c linux-2.0.18/mm/page_alloc.c
--- linux-2.0.15/mm/page_alloc.c	Mon Aug 26 22:17:17 1996
+++ linux-2.0.18/mm/page_alloc.c	Sun Sep  8 11:43:38 1996
@@ -321,9 +321,6 @@
 		return;
 	}
 	read_swap_page(entry, (char *) page);
-#if 0 /* Is this really needed here, hasn't it been solved elsewhere? */
-	flush_page_to_ram(page);
-#endif
 	if (pte_val(*page_table) != entry) {
 		free_page(page);
 		return;
diff -urN linux-2.0.15/mm/page_io.c linux-2.0.18/mm/page_io.c
--- linux-2.0.15/mm/page_io.c	Mon Aug 26 22:17:18 1996
+++ linux-2.0.18/mm/page_io.c	Sat Sep  7 09:54:36 1996
@@ -168,9 +168,10 @@
  * asynchronous function now --- we must call wait_on_page afterwards
  * if synchronous IO is required.  
  */
-void ll_rw_page(int rw, kdev_t dev, unsigned long page, char * buffer)
+void ll_rw_page(int rw, kdev_t dev, unsigned long offset, char * buffer)
 {
-	int block = page;
+	int block = offset;
+	struct page *page;
 
 	switch (rw) {
 		case READ:
@@ -185,7 +186,8 @@
 		default:
 			panic("ll_rw_page: bad block dev cmd, must be R/W");
 	}
-	if (set_bit(PG_locked, &mem_map[MAP_NR(buffer)].flags))
+	page = mem_map + MAP_NR(buffer);
+	if (set_bit(PG_locked, &page->flags))
 		panic ("ll_rw_page: page already locked");
-	brw_page(rw, (unsigned long) buffer, dev, &block, PAGE_SIZE, 0);
+	brw_page(rw, page, dev, &block, PAGE_SIZE, 0);
 }
diff -urN linux-2.0.15/net/core/net_alias.c linux-2.0.18/net/core/net_alias.c
--- linux-2.0.15/net/core/net_alias.c	Mon Aug 26 22:17:35 1996
+++ linux-2.0.18/net/core/net_alias.c	Thu Sep  5 17:48:16 1996
@@ -1298,6 +1298,7 @@
    */
   
 #ifndef ALIAS_USER_LAND_DEBUG
+#ifdef CONFIG_PROC_FS
   proc_net_register(&(struct proc_dir_entry) {
     PROC_NET_ALIAS_TYPES, 11, "alias_types",
     S_IFREG | S_IRUGO, 1, 0, 0,
@@ -1310,6 +1311,7 @@
     0, &proc_net_inode_operations,
     net_alias_getinfo
   });
+#endif
 #endif
   
 }
diff -urN linux-2.0.15/net/ipv4/icmp.c linux-2.0.18/net/ipv4/icmp.c
--- linux-2.0.15/net/ipv4/icmp.c	Mon Aug 26 22:17:41 1996
+++ linux-2.0.18/net/ipv4/icmp.c	Sat Sep  7 09:54:42 1996
@@ -618,7 +618,9 @@
 	 *	Build and send the packet.
 	 */
 
-	icmp_build_xmit(&icmp_param, saddr, iph->saddr, ((iph->tos & 0x38) | 6));
+	icmp_build_xmit(&icmp_param, saddr, iph->saddr, 
+			icmp_pointers[type].error ? 
+			(iph->tos & 0x1E) | 0xC0 : iph->tos);
 }
 
 
diff -urN linux-2.0.15/net/ipv4/raw.c linux-2.0.18/net/ipv4/raw.c
--- linux-2.0.15/net/ipv4/raw.c	Mon Aug 26 22:17:47 1996
+++ linux-2.0.18/net/ipv4/raw.c	Sat Sep  7 09:54:43 1996
@@ -236,6 +236,10 @@
 		memcpy(&sin, usin, sizeof(sin));
 		if (sin.sin_family && sin.sin_family != AF_INET) 
 			return(-EINVAL);
+		/*
+		 *	Protocol type is host ordered byte.
+		 */
+		sin.sin_port=ntohs(sin.sin_port);
 	}
 	else 
 	{
diff -urN linux-2.0.15/net/ipv4/tcp.c linux-2.0.18/net/ipv4/tcp.c
--- linux-2.0.15/net/ipv4/tcp.c	Mon Aug 26 22:17:48 1996
+++ linux-2.0.18/net/ipv4/tcp.c	Sat Sep  7 09:54:44 1996
@@ -958,6 +958,7 @@
 			{
 				if (copied)
 					return copied;
+				send_sig(SIGPIPE,current,0);
 				return -EPIPE;
 			}
 
diff -urN linux-2.0.15/net/ipv4/tcp_input.c linux-2.0.18/net/ipv4/tcp_input.c
--- linux-2.0.15/net/ipv4/tcp_input.c	Mon Aug 26 22:17:49 1996
+++ linux-2.0.18/net/ipv4/tcp_input.c	Thu Sep  5 17:48:20 1996
@@ -978,10 +978,11 @@
 	/*
 	 * Maybe we can take some stuff off of the write queue,
 	 * and put it onto the xmit queue.
-	 * FIXME: (?) There is bizarre case being tested here, to check if
+	 * There is bizarre case being tested here, to check if
 	 * the data at the head of the queue ends before the start of
-	 * the sequence we already ACKed. This does not appear to be
-	 * a case that can actually occur. Why are we testing it?
+	 * the sequence we already ACKed. This is not an error,
+	 * it can occur when we send a packet directly off of the write_queue
+	 * in a zero window probe.
 	 */
 
 	if (!skb_queue_empty(&sk->write_queue) &&
diff -urN linux-2.0.15/net/ipv4/tcp_output.c linux-2.0.18/net/ipv4/tcp_output.c
--- linux-2.0.15/net/ipv4/tcp_output.c	Fri Aug 30 10:39:33 1996
+++ linux-2.0.18/net/ipv4/tcp_output.c	Thu Sep  5 17:48:23 1996
@@ -325,35 +325,38 @@
 		skb_unlink(skb);
 		
 		/*
-		 *	See if we really need to send the packet. 
+		 *	See if we really need to send the whole packet. 
 		 */
 		 
-		if (before(skb->end_seq, sk->rcv_ack_seq +1)) 
-		{
+		if (before(skb->end_seq, sk->rcv_ack_seq +1)) {
 			/*
-			 *	This is acked data. We can discard it. This 
-			 *	cannot currently occur.
+			 *	This is acked data. We can discard it.
+			 *	This implies the packet was sent out
+			 *	of the write queue by a zero window probe.
 			 */
 			 
 			sk->retransmits = 0;
 			kfree_skb(skb, FREE_WRITE);
 			if (!sk->dead) 
 				sk->write_space(sk);
-		} 
-		else
-		{
+		} else {
 			struct tcphdr *th;
 			struct iphdr *iph;
 			int size;
-/*
- * put in the ack seq and window at this point rather than earlier,
- * in order to keep them monotonic.  We really want to avoid taking
- * back window allocations.  That's legal, but RFC1122 says it's frowned on.
- * Ack and window will in general have changed since this packet was put
- * on the write queue.
- */
+
 			iph = skb->ip_hdr;
 			th = (struct tcphdr *)(((char *)iph) +(iph->ihl << 2));
+
+                        /* See if we need to shrink the leading packet on
+                         * the retransmit queue. Strictly speaking, we
+                         * should never need to do this, but some buggy TCP
+                         * implementations get confused if you send them
+                         * a packet that contains both old and new data. (Feh!)
+                         * Soooo, we have this uglyness here.
+                         */
+			if (after(sk->rcv_ack_seq,skb->seq+th->syn+th->fin))
+				tcp_shrink_skb(sk,skb,sk->rcv_ack_seq);
+
 			size = skb->len - (((unsigned char *) th) - skb->data);
 #ifndef CONFIG_NO_PATH_MTU_DISCOVERY
 			if (size > sk->mtu - sizeof(struct iphdr))
@@ -363,6 +366,13 @@
 			}
 #endif
 			
+/*
+ * put in the ack seq and window at this point rather than earlier,
+ * in order to keep them monotonic.  We really want to avoid taking
+ * back window allocations.  That's legal, but RFC1122 says it's frowned on.
+ * Ack and window will in general have changed since this packet was put
+ * on the write queue.
+ */
 			th->ack_seq = htonl(sk->acked_seq);
 			th->window = htons(tcp_select_window(sk));
 
@@ -1189,4 +1199,57 @@
 	sk->retransmits++;
 	sk->prot->retransmits ++;
 	tcp_reset_xmit_timer (sk, TIME_PROBE0, sk->rto);
+}
+
+/*
+ * Remove the portion of a packet that has already been sent.
+ * Needed to deal with buggy TCP implementations that can't deal
+ * with seeing a packet that contains some data that has already
+ * been received.
+ */
+void tcp_shrink_skb(struct sock *sk, struct sk_buff *skb, u32 ack)
+{
+	struct iphdr *iph;
+	struct tcphdr *th;
+	unsigned char *old, *new;
+	unsigned long len;
+	int diff;
+
+	/*
+	 *	Recover the buffer pointers
+	 */
+	 
+	iph = (struct iphdr *)skb->ip_hdr;
+	th = (struct tcphdr *)(((char *)iph) +(iph->ihl << 2));
+
+	/* how much data are we droping from the tcp frame */
+	diff = ack - skb->seq;
+	/* how much data are we keeping in the tcp frame */
+	len = (skb->end_seq - (th->fin + th->syn)) - ack;
+
+	/* pointers to new start of remaining data, and old start */
+	new = (unsigned char *)th + th->doff*4;
+	old = new+diff;
+
+	/* Update our starting seq number */
+	skb->seq = ack;
+	th->seq = htonl(ack);
+	iph->tot_len = htons(ntohs(iph->tot_len)-diff);
+
+	/* Get the partial checksum for the IP options */
+	if (th->doff*4 - sizeof(*th) > 0)
+		skb->csum = csum_partial((void *)(th+1),
+				th->doff*4-sizeof(*th),0);
+	else
+		skb->csum = 0;
+
+	/* Copy the good data down and get it's checksum */
+	skb->csum = csum_partial_copy((void *)old,(void *)new,len,skb->csum);
+
+	/* shorten the skb */
+	skb_trim(skb,skb->len-diff);
+	 
+	/* Checksum the shrunk buffer */
+	tcp_send_check(th, sk->saddr, sk->daddr, 
+		   th->doff * 4 + len , skb);
 }
diff -urN linux-2.0.15/net/unix/af_unix.c linux-2.0.18/net/unix/af_unix.c
--- linux-2.0.15/net/unix/af_unix.c	Mon Aug 26 22:17:56 1996
+++ linux-2.0.18/net/unix/af_unix.c	Sat Sep  7 09:54:47 1996
@@ -863,6 +863,12 @@
 		return -EINVAL;
 	}
 		
+	if(sk->shutdown&SEND_SHUTDOWN)
+	{
+		send_sig(SIGPIPE,current,0);
+		return -EPIPE;
+	}
+	
 	if(sunaddr!=NULL)
 	{
 		if(sock->type==SOCK_STREAM)
@@ -973,10 +979,27 @@
 				sock->state=SS_UNCONNECTED;
 				sti();
 				kfree_skb(skb, FREE_WRITE);
+				/*
+				 *	Check with 1003.1g - what should
+				 *	datagram error
+				 */
+				if (!sent)
+					sent = -ECONNRESET;
+				return sent;
+			}
+			/*
+			 *	Stream sockets SIGPIPE
+			 */
+			if(sock->type==SOCK_STREAM && other->dead)
+			{
+				kfree_skb(skb, FREE_WRITE);
+				sti();
 				if(!sent)
-					return -ECONNRESET;
-				else
-					return sent;
+				{
+					send_sig(SIGPIPE,current,0);
+					sent = -EPIPE;
+				}
+				return sent;
 			}
 		}
 		else
