diff -u --recursive --new-file v2.3.5/linux/CREDITS linux/CREDITS
--- v2.3.5/linux/CREDITS	Mon May 31 22:28:04 1999
+++ linux/CREDITS	Tue Jun  8 10:27:18 1999
@@ -16,6 +16,14 @@
 S: (ask for current address)
 S: Finland
 
+N: Dragos Acostachioaie
+E: dragos@iname.com
+W: http://www.arbornet.org/~dragos
+D: /proc/sysvipc
+S: C. Negri 6, bl. D3
+S: Iasi 6600
+S: Romania
+
 N: Dave Airlie
 E: airlied@linux.ie
 W: http://www.csn.ul.ie/~airlied
@@ -821,7 +829,7 @@
 S: Germany
 
 N: Michael Hipp
-E: mhipp@student.uni-tuebingen.de
+E: hippm@informatik.uni-tuebingen.de
 D: drivers for the racal ni5210 & ni6510 Ethernet-boards
 S: Talstr. 1
 S: D - 72072 Tuebingen
@@ -1349,8 +1357,11 @@
 
 N: Arnaldo Carvalho de Melo
 E: acme@conectiva.com.br
+W: http://www.conectiva.com.br/~acme
 D: wanrouter hacking
-D: cyclades 2X sync card driver (still in early devel stage)
+D: Cyclom 2X synchronous card driver
+D: i18n for minicom, net-tools, util-linux, fetchmail, etc
+S: Conectiva Informatica LTDA
 S: R. Prof. Rubens Elke Braga, 558 - Parolin
 S: 80220-320 Curitiba - Parana
 S: Brazil
@@ -1929,6 +1940,17 @@
 S: 1050 Woodduck Avenue
 S: Santa Clara, California 95051
 S: USA
+
+N: Marcelo W. Tosatti
+E: marcelo@conectiva.com.br
+W: http://lie-br.conectiva.com.br/~marcelo/
+D: Miscellaneous kernel hacker
+D: Cyclom 2X driver hacker
+D: linuxconf apache & proftpd module maintainer
+S: Conectiva Informatica LTDA
+S: R. Prof. Rubens Elke Braga, 558 - Parolin
+S: 80220-320 Curitiba - Parana
+S: Brazil
 
 N: Stefan Traby
 E: stefan@quant-x.com
diff -u --recursive --new-file v2.3.5/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.3.5/linux/Documentation/Configure.help	Mon May 31 22:28:04 1999
+++ linux/Documentation/Configure.help	Wed Jun  9 16:59:15 1999
@@ -3958,34 +3958,49 @@
   say M here and read Documentation/modules.txt. The module will be
   called aic7xxx.o.
 
-Override driver defaults for commands per LUN
-CONFIG_OVERRIDE_CMDS
-  Say Y here if you want to override the default maximum number of
-  commands that a single device on the aic7xxx controller is allowed
-  to have active at one time. This option only affects tagged queueing
-  capable devices. The driver uses a value of 24 by default.
-  If you say Y here, you can adjust the number of commands per LUN
-  with the following configuration option.
+Enable or Disable Tagged Command Queueing by default
+CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+  This option causes the aic7xxx driver to attempt to use tagged command
+  queueing on any devices that claim to support it.  If this is set to yes,
+  you can still turn off TCQ on troublesome devices with the use of the
+  tag_info boot parameter.  See /usr/src/linux/drivers/scsi/README.aic7xxx
+  for more information on that and other aic7xxx setup commands.  If this
+  option is turned off, you may still enable TCQ on known good devices by
+  use of the tag_info boot parameter.
   
-  If unsure, say N.
-
-Maximum number of commands per LUN
-CONFIG_AIC7XXX_CMDS_PER_LUN
-  Specify the maximum number of commands you would like to allocate
-  per LUN (a LUN is a Logical Unit Number -- some physical SCSI
-  devices, e.g. CD jukeboxes, act logically as several separate units,
-  each of which gets its own number).
+  If you are unsure about your devices then it is safest to say N here.
+  
+  However, TCQ can increase performance on some hard drives by as much
+  as 50% or more, so I would recommend that if you say N here, that you
+  at least read the README.aic7xxx file so you will know how to enable
+  this option manually should your drives prove to be safe in regards
+  to TCQ.
+
+  Conversely, certain drives are known to lock up or cause bus resets when
+  TCQ is enabled on them.  If you have a Western Digital Enterprise SCSI
+  drive for instance, then don't even bother to enable TCQ on it as the
+  drive will become unreliable, and it will actually reduce performance.
+
+Default number of TCQ commands per device
+CONFIG_AIC7XXX_CMDS_PER_DEVICE
+  Specify the number of commands you would like to allocate per SCSI
+  device when Tagged Command Queueing (TCQ) is enabled on that device.
 
-  Reasonable figures are in the range of 14 to 32 commands per device,
+  Reasonable figures are in the range of 8 to 24 commands per device,
   but depending on hardware could be increased or decreased from that
   figure. If the number is too high for any particular device, the
   driver will automatically compensate usually after only 10 minutes
-  of uptime and will issue a message to alert you to the fact that the
-  number of commands for that device has been reduced. It will not
-  hinder performance if some of your devices eventually have their
-  commands per LUN reduced, but is a waste of memory if all of your
-  devices end up reducing this number down to a more reasonable
-  figure. Default: 24
+  of uptime. It will not hinder performance if some of your devices
+  eventually have their command depth reduced, but is a waste of memory
+  if all of your devices end up reducing this number down to a more
+  reasonable figure.
+  
+  NOTE: Certain very broken drives are known to lock up when given more
+  commands than they like to deal with.  Quantum Fireball drives are the
+  most common in this category.  For the Quantum Fireball drives I would
+  suggest no more than 8 commands per device.
+
+  Default: 8
 
 Collect statistics to report in /proc
 CONFIG_AIC7XXX_PROC_STATS
@@ -6369,6 +6384,20 @@
   module, say M here and read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt.
 
+SKnet MCA support
+CONFIG_SKMC
+  This are Micro Channel ethernet adapters.  You need to set CONFIG_MCA
+  to use this driver.  It's both available as an in-kernel driver and
+  as a module ( = code which can be inserted in and removed from the
+  running kernel whenever you want). If you want to compile it as a module,
+  say M here and read Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt. If you plan to use more than
+  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
+  available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.  Supported
+  cards are the SKnet Junior MC2 and the SKnet MC2(+).  Distinguishing
+  both cards is done automatically.  Note that using multiple boards
+  of different type hasn't been tested with this driver.
+
 EISA, VLB, PCI and on board controllers
 CONFIG_NET_EISA
   This is another class of network cards which attach directly to the
@@ -8936,8 +8965,8 @@
 
 Zilog serial support
 CONFIG_SUN_ZS
-  This driver does not exist at this point, so you might as well 
-  say N.
+  If you are asked this question, something is wrong with config scripts.
+  Zilog serial driver is always enabled in sparc architecture.
 
 Double Talk PC internal speech card support
 CONFIG_DTLK
@@ -10338,8 +10367,14 @@
   inserted in and removed from the running kernel whenever you want),
   say M and read Documentation/modules.txt. If unsure, say Y.
 
-#Mostek real time clock support
-#CONFIG_SUN_MOSTEK_RTC
+Mostek real time clock support
+CONFIG_SUN_MOSTEK_RTC
+  The Mostek RTC chip is used on all knows Sun computers except
+  some JavaStation-s. For a JavaStation you need to say Y both here
+  and to CONFIG_RTC.
+
+  Say Y here unless you are building a special purpose kernel.
+
 #
 #Siemens SAB82532 serial support
 #CONFIG_SAB82532
diff -u --recursive --new-file v2.3.5/linux/Documentation/filesystems/hpfs.txt linux/Documentation/filesystems/hpfs.txt
--- v2.3.5/linux/Documentation/filesystems/hpfs.txt	Mon May 17 09:55:20 1999
+++ linux/Documentation/filesystems/hpfs.txt	Fri Jun  4 01:06:29 1999
@@ -1,4 +1,4 @@
-Read/Write HPFS 1.99b
+Read/Write HPFS 2.00
 1998-1999, Mikulas Patocka
 
 email: mikulas@artax.karlin.mff.cuni.cz
@@ -267,6 +267,8 @@
 	file
      Now it tries to truncate the file if there's not enough space when deleting
      Removed a lot of redundat code
+2.00 Fixed a bug in rename (it was there since 1.96)
+     Better anti-fragmentation strategy
 
 
  vim: set textwidth=80:
diff -u --recursive --new-file v2.3.5/linux/Documentation/mtrr.txt linux/Documentation/mtrr.txt
--- v2.3.5/linux/Documentation/mtrr.txt	Mon May 17 09:55:20 1999
+++ linux/Documentation/mtrr.txt	Thu Jun  3 08:17:28 1999
@@ -1,15 +1,25 @@
 MTRR (Memory Type Range Register) control
-16 May 1999
+3 Jun 1999
 Richard Gooch
 <rgooch@atnf.csiro.au>
 
-  On Intel Pentium Pro/Pentium II systems the Memory Type Range
-  Registers (MTRRs) may be used to control processor access to memory
-  ranges. This is most useful when you have a video (VGA) card on a
-  PCI or AGP bus. Enabling write-combining allows bus write transfers
-  to be combined into a larger transfer before bursting over the
-  PCI/AGP bus. This can increase performance of image write operations
-  2.5 times or more.
+  On Intel P6 family processors (Pentium Pro, Pentium II and later)
+  the Memory Type Range Registers (MTRRs) may be used to control
+  processor access to memory ranges. This is most useful when you have
+  a video (VGA) card on a PCI or AGP bus. Enabling write-combining
+  allows bus write transfers to be combined into a larger transfer
+  before bursting over the PCI/AGP bus. This can increase performance
+  of image write operations 2.5 times or more.
+
+  The Cyrix 6x86, 6x86MX and M II processors have Address Range
+  Registers (ARRs) which provide a similar functionality to MTRRs. For
+  these, the ARRs are used to emulate the MTRRs.
+
+  The AMD K6-2 (stepping 8 and above) and K6-3 processors have two
+  MTRRs. These are supported.
+  
+  The Centaur C6 (WinChip) has 8 MCRs, allowing write-combining. These
+  are supported.
 
   The CONFIG_MTRR option creates a /proc/mtrr file which may be used
   to manipulate your MTRRs. Typically the X server should use
diff -u --recursive --new-file v2.3.5/linux/MAINTAINERS linux/MAINTAINERS
--- v2.3.5/linux/MAINTAINERS	Mon May 17 09:55:20 1999
+++ linux/MAINTAINERS	Thu Jun  3 08:26:42 1999
@@ -192,6 +192,13 @@
 M:	jam@acm.org
 S:	Maintained
 
+CYCLADES 2X SYNC CARD DRIVER
+P:	Arnaldo Carvalho de Melo
+M:	acme@conectiva.com.br
+W:	http://www.conectiva.com.br/~acme
+L:	cycsyn-devel@bazar.conectiva.com.br
+S:	Maintained
+
 CYCLADES ASYNC MUX DRIVER
 P:	Ivan Passos
 M:	Ivan Passos <ivan@cyclades.com>
@@ -390,6 +397,11 @@
 M:	Paul.Russell@rustcorp.com.au
 W:	http://www.rustcorp.com/linux/ipchains
 S:	Supported
+
+IP MASQUERADING:
+P:	Juanjo Ciarlante
+M:	jjciarla@raiz.uncu.edu.ar
+S:	Maintained
 
 IPX/SPX NETWORK LAYER
 P:	Jay Schulist
diff -u --recursive --new-file v2.3.5/linux/Makefile linux/Makefile
--- v2.3.5/linux/Makefile	Wed Jun  2 14:44:39 1999
+++ linux/Makefile	Wed Jun  2 14:40:19 1999
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 3
-SUBLEVEL = 5
+SUBLEVEL = 6
 EXTRAVERSION =
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
diff -u --recursive --new-file v2.3.5/linux/arch/alpha/kernel/ptrace.c linux/arch/alpha/kernel/ptrace.c
--- v2.3.5/linux/arch/alpha/kernel/ptrace.c	Mon May 31 22:28:04 1999
+++ linux/arch/alpha/kernel/ptrace.c	Mon Jun  7 11:15:33 1999
@@ -246,26 +246,6 @@
 	flush_tlb();
 }
 
-static struct vm_area_struct *
-find_extend_vma(struct task_struct * tsk, unsigned long addr)
-{
-	struct vm_area_struct * vma;
-
-	addr &= PAGE_MASK;
-	vma = find_vma(tsk->mm,addr);
-	if (!vma)
-		return NULL;
-	if (vma->vm_start <= addr)
-		return vma;
-	if (!(vma->vm_flags & VM_GROWSDOWN))
-		return NULL;
-	if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
-		return NULL;
-	vma->vm_offset -= vma->vm_start - addr;
-	vma->vm_start = addr;
-	return vma;
-}
-
 /*
  * This routine checks the page boundaries, and that the offset is
  * within the task area. It then calls get_long() to read a long.
diff -u --recursive --new-file v2.3.5/linux/arch/alpha/lib/memcpy.c linux/arch/alpha/lib/memcpy.c
--- v2.3.5/linux/arch/alpha/lib/memcpy.c	Tue Apr  7 08:05:05 1998
+++ linux/arch/alpha/lib/memcpy.c	Mon Jun  7 11:37:13 1999
@@ -21,30 +21,44 @@
  * This should be done in one go with ldq_u*2/mask/stq_u. Do it
  * with a macro so that we can fix it up later..
  */
-#define ALIGN_DEST_TO8(d,s,n) \
+#define ALIGN_DEST_TO8_UP(d,s,n) \
 	while (d & 7) { \
 		if (n <= 0) return; \
 		n--; \
 		*(char *) d = *(char *) s; \
 		d++; s++; \
 	}
+#define ALIGN_DEST_TO8_DN(d,s,n) \
+	while (d & 7) { \
+		if (n <= 0) return; \
+		n--; \
+		d--; s--; \
+		*(char *) d = *(char *) s; \
+	}
 
 /*
  * This should similarly be done with ldq_u*2/mask/stq. The destination
  * is aligned, but we don't fill in a full quad-word
  */
-#define DO_REST(d,s,n) \
+#define DO_REST_UP(d,s,n) \
 	while (n > 0) { \
 		n--; \
 		*(char *) d = *(char *) s; \
 		d++; s++; \
 	}
+#define DO_REST_DN(d,s,n) \
+	while (n > 0) { \
+		n--; \
+		d--; s--; \
+		*(char *) d = *(char *) s; \
+	}
 
 /*
  * This should be done with ldq/mask/stq. The source and destination are
  * aligned, but we don't fill in a full quad-word
  */
-#define DO_REST_ALIGNED(d,s,n) DO_REST(d,s,n)
+#define DO_REST_ALIGNED_UP(d,s,n) DO_REST_UP(d,s,n)
+#define DO_REST_ALIGNED_DN(d,s,n) DO_REST_DN(d,s,n)
 
 /*
  * This does unaligned memory copies. We want to avoid storing to
@@ -53,9 +67,10 @@
  *
  * Note the ordering to try to avoid load (and address generation) latencies.
  */
-static inline void __memcpy_unaligned(unsigned long d, unsigned long s, long n)
+static inline void __memcpy_unaligned_up (unsigned long d, unsigned long s,
+					  long n)
 {
-	ALIGN_DEST_TO8(d,s,n);
+	ALIGN_DEST_TO8_UP(d,s,n);
 	n -= 8;			/* to avoid compare against 8 in the loop */
 	if (n >= 0) {
 		unsigned long low_word, high_word;
@@ -77,7 +92,17 @@
 		} while (n >= 0);
 	}
 	n += 8;
-	DO_REST(d,s,n);
+	DO_REST_UP(d,s,n);
+}
+
+static inline void __memcpy_unaligned_dn (unsigned long d, unsigned long s,
+					  long n)
+{
+	/* I don't understand AXP assembler well enough for this. -Tim */
+	s += n;
+	d += n;
+	while (n--)
+		* (char *) --d = * (char *) --s;
 }
 
 /*
@@ -88,9 +113,10 @@
  *
  * Note the ordering to try to avoid load (and address generation) latencies.
  */
-static inline void __memcpy_aligned(unsigned long d, unsigned long s, long n)
+static inline void __memcpy_aligned_up (unsigned long d, unsigned long s,
+					long n)
 {
-	ALIGN_DEST_TO8(d,s,n);
+	ALIGN_DEST_TO8_UP(d,s,n);
 	n -= 8;
 	while (n >= 0) {
 		unsigned long tmp;
@@ -101,18 +127,58 @@
 		d += 8;
 	}
 	n += 8;
-	DO_REST_ALIGNED(d,s,n);
+	DO_REST_ALIGNED_UP(d,s,n);
+}
+static inline void __memcpy_aligned_dn (unsigned long d, unsigned long s,
+					long n)
+{
+	s += n;
+	d += n;
+	ALIGN_DEST_TO8_DN(d,s,n);
+	n -= 8;
+	while (n >= 0) {
+		unsigned long tmp;
+		s -= 8;
+		__asm__("ldq %0,%1":"=r" (tmp):"m" (*(unsigned long *) s));
+		n -= 8;
+		d -= 8;
+		*(unsigned long *) d = tmp;
+	}
+	n += 8;
+	DO_REST_ALIGNED_DN(d,s,n);
 }
 
 void * memcpy(void * dest, const void *src, size_t n)
 {
 	if (!(((unsigned long) dest ^ (unsigned long) src) & 7)) {
-		__memcpy_aligned((unsigned long) dest, (unsigned long) src, n);
+		__memcpy_aligned_up ((unsigned long) dest, (unsigned long) src,
+				     n);
 		return dest;
 	}
-	__memcpy_unaligned((unsigned long) dest, (unsigned long) src, n);
+	__memcpy_unaligned_up ((unsigned long) dest, (unsigned long) src, n);
 	return dest;
 }
 
 /* For backward modules compatibility, define __memcpy.  */
 asm("__memcpy = memcpy; .globl __memcpy");
+
+void *memmove (void *dest, const void *src, size_t n)
+{
+	if (dest <= src) {
+		if (!(((unsigned long) dest ^ (unsigned long) src) & 7))
+			__memcpy_aligned_up ((unsigned long) dest,
+					     (unsigned long) src, n);
+		else
+			__memcpy_unaligned_up ((unsigned long) dest,
+					       (unsigned long) src, n);
+	}
+	else {
+		if (!(((unsigned long) dest ^ (unsigned long) src) & 7))
+			__memcpy_aligned_dn ((unsigned long) dest,
+					     (unsigned long) src, n);
+		else
+			__memcpy_unaligned_dn ((unsigned long) dest,
+					       (unsigned long) src, n);
+	}
+	return dest;
+}
diff -u --recursive --new-file v2.3.5/linux/arch/arm/kernel/ptrace.c linux/arch/arm/kernel/ptrace.c
--- v2.3.5/linux/arch/arm/kernel/ptrace.c	Mon May 31 22:28:04 1999
+++ linux/arch/arm/kernel/ptrace.c	Mon Jun  7 11:15:33 1999
@@ -164,25 +164,6 @@
 	flush_tlb();
 }
 
-static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
-{
-	struct vm_area_struct * vma;
-
-	addr &= PAGE_MASK;
-	vma = find_vma(tsk->mm,addr);
-	if (!vma)
-		return NULL;
-	if (vma->vm_start <= addr)
-		return vma;
-	if (!(vma->vm_flags & VM_GROWSDOWN))
-		return NULL;
-	if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
-		return NULL;
-	vma->vm_offset -= vma->vm_start - addr;
-	vma->vm_start = addr;
-	return vma;
-}
-
 /*
  * This routine checks the page boundaries, and that the offset is
  * within the task area. It then calls get_long() to read a long.
diff -u --recursive --new-file v2.3.5/linux/arch/i386/Makefile linux/arch/i386/Makefile
--- v2.3.5/linux/arch/i386/Makefile	Mon May 17 09:55:20 1999
+++ linux/arch/i386/Makefile	Mon Jun  7 17:02:23 1999
@@ -67,6 +67,13 @@
 
 MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
 
+vmlinux: arch/i386/vmlinux.lds
+
+arch/i386/vmlinux.lds: arch/i386/vmlinux.lds.S FORCE
+	gcc -E -C -P -I$(HPATH) -imacros $(HPATH)/asm-i386/page_offset.h -Ui386 arch/i386/vmlinux.lds.S >arch/i386/vmlinux.lds
+
+FORCE: ;
+
 zImage: vmlinux
 	@$(MAKEBOOT) zImage
 
diff -u --recursive --new-file v2.3.5/linux/arch/i386/boot/bootsect.S linux/arch/i386/boot/bootsect.S
--- v2.3.5/linux/arch/i386/boot/bootsect.S	Wed Jun 24 14:30:08 1998
+++ linux/arch/i386/boot/bootsect.S	Tue Jun  8 10:47:57 1999
@@ -58,12 +58,12 @@
 	mov	ds,ax
 	mov	ax,#INITSEG
 	mov	es,ax
-	mov	cx,#256
+	mov	cx,#128
 	sub	si,si
 	sub	di,di
 	cld
 	rep
-	movsw
+	movsd
 	jmpi	go,INITSEG
 
 ! ax and es already contain INITSEG
diff -u --recursive --new-file v2.3.5/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.3.5/linux/arch/i386/config.in	Fri May 14 18:55:11 1999
+++ linux/arch/i386/config.in	Mon Jun  7 17:02:23 1999
@@ -33,6 +33,10 @@
   define_bool CONFIG_X86_GOOD_APIC y
 fi
 
+choice 'Maximum Physical Memory' \
+	"1GB		CONFIG_1GB \
+	 2GB		CONFIG_2GB" 1GB
+
 bool 'Math emulation' CONFIG_MATH_EMULATION
 bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR
 bool 'Symmetric multi-processing support' CONFIG_SMP
@@ -113,6 +117,8 @@
 fi
 
 endmenu
+
+source drivers/i2o/Config.in
 
 source drivers/pnp/Config.in
 
diff -u --recursive --new-file v2.3.5/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.3.5/linux/arch/i386/defconfig	Wed Jun  2 14:44:39 1999
+++ linux/arch/i386/defconfig	Mon Jun  7 22:12:01 1999
@@ -21,6 +21,8 @@
 CONFIG_X86_POPAD_OK=y
 CONFIG_X86_TSC=y
 CONFIG_X86_GOOD_APIC=y
+CONFIG_1GB=y
+# CONFIG_2GB is not set
 # CONFIG_MATH_EMULATION is not set
 # CONFIG_MTRR is not set
 CONFIG_SMP=y
@@ -58,6 +60,16 @@
 # CONFIG_APM is not set
 
 #
+# I2O device support
+#
+# CONFIG_I2O is not set
+# CONFIG_I2O_PCI is not set
+# CONFIG_I2O_BLOCK is not set
+# CONFIG_I2O_LAN is not set
+# CONFIG_I2O_SCSI is not set
+# CONFIG_I2O_PROC is not set
+
+#
 # Plug and Play support
 #
 # CONFIG_PNP is not set
@@ -243,6 +255,11 @@
 # CONFIG_HAMRADIO is not set
 
 #
+# IrDA subsystem support
+#
+# CONFIG_IRDA is not set
+
+#
 # ISDN subsystem
 #
 # CONFIG_ISDN is not set
@@ -308,6 +325,7 @@
 # CONFIG_USB_AUDIO is not set
 # CONFIG_USB_ACM is not set
 # CONFIG_USB_PRINTER is not set
+# CONFIG_USB_SCSI is not set
 
 #
 # Filesystems
diff -u --recursive --new-file v2.3.5/linux/arch/i386/kernel/mca.c linux/arch/i386/kernel/mca.c
--- v2.3.5/linux/arch/i386/kernel/mca.c	Mon May 10 13:00:10 1999
+++ linux/arch/i386/kernel/mca.c	Mon Jun  7 16:17:59 1999
@@ -112,21 +112,25 @@
 static ssize_t proc_mca_read(struct file*, char*, size_t, loff_t *);
 
 static struct file_operations proc_mca_operations = {
-	NULL,			/* array_lseek */
-	proc_mca_read,		/* array_read */
-	NULL,			/* array_write */
-	NULL,			/* array_readdir */
-	NULL,			/* array_poll */
-	NULL,			/* array_ioctl */
+	NULL,			/* llseek */
+	proc_mca_read,		/* read */
+	NULL,			/* write */
+	NULL,			/* readdir */
+	NULL,			/* poll */
+	NULL,			/* ioctl */
 	NULL,			/* mmap */
-	NULL,			/* no special open code */
+	NULL,			/* open */
 	NULL,			/* flush */
-	NULL,			/* no special release code */
-	NULL			/* can't fsync */
+	NULL,			/* release */
+	NULL,			/* fsync */
+	NULL,			/* fascync */
+	NULL,			/* check_media_change */
+	NULL,			/* revalidate */
+	NULL			/* lock */
 };
 
 static struct inode_operations proc_mca_inode_operations = {
-	&proc_mca_operations,	/* default base directory file-ops */
+	&proc_mca_operations,	/* default file-ops */
 	NULL,			/* create */
 	NULL,			/* lookup */
 	NULL,			/* link */
@@ -142,7 +146,10 @@
 	NULL,			/* writepage */
 	NULL,			/* bmap */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL,			/* updatepage */
+	NULL			/* revalidate */
 };
 #endif
 
@@ -220,18 +227,19 @@
 	if(!MCA_bus)
 		return;
 	printk("Micro Channel bus detected.\n");
-	save_flags(flags);
-	cli();
 
 	/* Allocate MCA_info structure (at address divisible by 8) */
 
-	mca_info = kmalloc(sizeof(struct MCA_info), GFP_KERNEL);
+	mca_info = (struct MCA_info *)kmalloc(sizeof(struct MCA_info), GFP_KERNEL);
 
 	if(mca_info == NULL) {
 		printk("Failed to allocate memory for mca_info!");
-		restore_flags(flags);
 		return;
 	}
+	memset(mca_info, 0, sizeof(struct MCA_info));
+
+	save_flags(flags);
+	cli();
 
 	/* Make sure adapter setup is off */
 
@@ -705,12 +713,15 @@
 		mca_info->slot[i].dev = 0;
 
 		if(!mca_isadapter(i)) continue;
-		node = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
+
+		node = (struct proc_dir_entry *)kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
 
 		if(node == NULL) {
 			printk("Failed to allocate memory for MCA proc-entries!");
 			return;
 		}
+		memset(node, 0, sizeof(struct proc_dir_entry));
+
 		if(i < MCA_MAX_SLOT_NR) {
 			node->low_ino = PROC_MCA_SLOT + i;
 			node->namelen = sprintf(mca_info->slot[i].procname,
@@ -854,7 +865,7 @@
 	type = inode->i_ino;
 	pid = type >> 16;
 	type &= 0x0000ffff;
-	start = 0;
+	start = NULL;
 	dp = (struct proc_dir_entry *) inode->u.generic_ip;
 	length = mca_fill((char *) page, pid, type,
 			  &start, ppos, count);
@@ -862,7 +873,7 @@
 		free_page(page);
 		return length;
 	}
-	if(start != 0) {
+	if(start != NULL) {
 		/* We have had block-adjusting processing! */
 
 		copy_to_user(buf, start, length);
diff -u --recursive --new-file v2.3.5/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v2.3.5/linux/arch/i386/kernel/ptrace.c	Wed Mar 24 13:18:46 1999
+++ linux/arch/i386/kernel/ptrace.c	Mon Jun  7 11:15:33 1999
@@ -172,25 +172,6 @@
 	flush_tlb();
 }
 
-static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
-{
-	struct vm_area_struct * vma;
-
-	addr &= PAGE_MASK;
-	vma = find_vma(tsk->mm,addr);
-	if (!vma)
-		return NULL;
-	if (vma->vm_start <= addr)
-		return vma;
-	if (!(vma->vm_flags & VM_GROWSDOWN))
-		return NULL;
-	if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
-		return NULL;
-	vma->vm_offset -= vma->vm_start - addr;
-	vma->vm_start = addr;
-	return vma;
-}
-
 /*
  * This routine checks the page boundaries, and that the offset is
  * within the task area. It then calls get_long() to read a long.
diff -u --recursive --new-file v2.3.5/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.3.5/linux/arch/i386/kernel/setup.c	Sat May 15 23:46:03 1999
+++ linux/arch/i386/kernel/setup.c	Tue Jun  8 10:42:46 1999
@@ -12,6 +12,8 @@
  * 
  *  Force Centaur C6 processors to report MTRR capability.
  *      Bart Hartgers <bart@etpmod.phys.tue.nl>, May 199.
+ *
+ *  Intel Mobile Pentium II detection fix. Sean Gilley, June 1999.
  */
 
 /*
@@ -688,7 +690,7 @@
 	    NULL, NULL, NULL, NULL }},
 	{ X86_VENDOR_INTEL,	6,
 	  { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)", 
-	    NULL, "Pentium II (Deschutes)", "Celeron (Mendocino)", NULL,
+            NULL, "Pentium II (Deschutes)", "Mobile Pentium II", NULL,
 	    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }},
 	{ X86_VENDOR_AMD,	4,
 	  { NULL, NULL, NULL, "486 DX/2", NULL, NULL, NULL, "486 DX/2-WB",
@@ -794,13 +796,19 @@
 			if (c->x86_model <= 16)
 				p = cpu_models[i].model_names[c->x86_model];
 
-			/* Names for the Pentium II processors */
+			/* Names for the Pentium II Celeron processors 
+                           detectable only by also checking the cache size */
 			if ((cpu_models[i].vendor == X86_VENDOR_INTEL)
-			    && (cpu_models[i].x86 == 6) 
-			    && (c->x86_model == 5)
-			    && (c->x86_cache_size == 0)) {
-				p = "Celeron (Covington)";
-			}
+			    && (cpu_models[i].x86 == 6)){ 
+				if(c->x86_model == 6 && c->x86_cache_size == 128) {
+                            		p = "Celeron (Mendocino)"; 
+                          	}
+			  	else { 
+                            	if (c->x86_model == 5 && c->x86_cache_size == 0) {
+				  	p = "Celeron (Covington)";
+			    	}
+                        }
+                    }
 		}
 			
 	}
diff -u --recursive --new-file v2.3.5/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v2.3.5/linux/arch/i386/kernel/signal.c	Tue Dec  1 11:28:24 1998
+++ linux/arch/i386/kernel/signal.c	Mon Jun  7 16:14:06 1999
@@ -698,6 +698,7 @@
 			default:
 				lock_kernel();
 				sigaddset(&current->signal, signr);
+				recalc_sigpending(current);
 				current->flags |= PF_SIGNALED;
 				do_exit(exit_code);
 				/* NOTREACHED */
diff -u --recursive --new-file v2.3.5/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v2.3.5/linux/arch/i386/mm/init.c	Thu Jan 21 11:28:40 1999
+++ linux/arch/i386/mm/init.c	Tue Jun  8 10:49:48 1999
@@ -390,6 +390,7 @@
 	int datapages = 0;
 	int initpages = 0;
 	unsigned long tmp;
+	unsigned long endbase;
 
 	end_mem &= PAGE_MASK;
 	high_memory = (void *) end_mem;
@@ -417,8 +418,10 @@
 	 * IBM messed up *AGAIN* in their thinkpad: 0xA0000 -> 0x9F000.
 	 * They seem to have done something stupid with the floppy
 	 * controller as well..
+	 * The amount of available base memory is in WORD 40:13.
 	 */
-	while (start_low_mem < 0x9f000+PAGE_OFFSET) {
+	endbase = PAGE_OFFSET + ((*(unsigned short *)__va(0x413) * 1024) & PAGE_MASK);
+	while (start_low_mem < endbase) {
 		clear_bit(PG_reserved, &mem_map[MAP_NR(start_low_mem)].flags);
 		start_low_mem += PAGE_SIZE;
 	}
diff -u --recursive --new-file v2.3.5/linux/arch/i386/mm/ioremap.c linux/arch/i386/mm/ioremap.c
--- v2.3.5/linux/arch/i386/mm/ioremap.c	Fri Nov 27 15:03:14 1998
+++ linux/arch/i386/mm/ioremap.c	Thu Jun  3 14:17:51 1999
@@ -93,12 +93,17 @@
 {
 	void * addr;
 	struct vm_struct * area;
-	unsigned long offset;
+	unsigned long offset, last_addr;
+
+	/* Don't allow wraparound or zero size */
+	last_addr = phys_addr + size - 1;
+	if (!size || last_addr < phys_addr)
+		return NULL;
 
 	/*
 	 * Don't remap the low PCI/ISA area, it's always mapped..
 	 */
-	if (phys_addr >= 0xA0000 && (phys_addr+size) <= 0x100000)
+	if (phys_addr >= 0xA0000 && last_addr < 0x100000)
 		return phys_to_virt(phys_addr);
 
 	/*
@@ -112,13 +117,7 @@
 	 */
 	offset = phys_addr & ~PAGE_MASK;
 	phys_addr &= PAGE_MASK;
-	size = PAGE_ALIGN(size + offset);
-
-	/*
-	 * Don't allow mappings that wrap..
-	 */
-	if (!size || size > phys_addr + size)
-		return NULL;
+	size = PAGE_ALIGN(last_addr) - phys_addr;
 
 	/*
 	 * Ok, go for it..
diff -u --recursive --new-file v2.3.5/linux/arch/i386/vmlinux.lds linux/arch/i386/vmlinux.lds
--- v2.3.5/linux/arch/i386/vmlinux.lds	Sun Dec 27 22:45:13 1998
+++ linux/arch/i386/vmlinux.lds	Tue Jun  8 23:03:37 1999
@@ -1,12 +1,12 @@
 /* ld script to make i386 Linux kernel
- * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
  */
 OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
 OUTPUT_ARCH(i386)
 ENTRY(_start)
 SECTIONS
 {
-  . = 0xC0000000 + 0x100000;
+  . = 0xC0000000  + 0x100000;
   _text = .;			/* Text and read-only data */
   .text : {
 	*(.text)
diff -u --recursive --new-file v2.3.5/linux/arch/i386/vmlinux.lds.S linux/arch/i386/vmlinux.lds.S
--- v2.3.5/linux/arch/i386/vmlinux.lds.S	Wed Dec 31 16:00:00 1969
+++ linux/arch/i386/vmlinux.lds.S	Mon Jun  7 17:02:23 1999
@@ -0,0 +1,69 @@
+/* ld script to make i386 Linux kernel
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
+ */
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+SECTIONS
+{
+  . = PAGE_OFFSET_RAW + 0x100000;
+  _text = .;			/* Text and read-only data */
+  .text : {
+	*(.text)
+	*(.fixup)
+	*(.gnu.warning)
+	} = 0x9090
+  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
+  .rodata : { *(.rodata) }
+  .kstrtab : { *(.kstrtab) }
+
+  . = ALIGN(16);		/* Exception table */
+  __start___ex_table = .;
+  __ex_table : { *(__ex_table) }
+  __stop___ex_table = .;
+
+  __start___ksymtab = .;	/* Kernel symbol table */
+  __ksymtab : { *(__ksymtab) }
+  __stop___ksymtab = .;
+
+  _etext = .;			/* End of text section */
+
+  .data : {			/* Data */
+	*(.data)
+	CONSTRUCTORS
+	}
+
+  _edata = .;			/* End of data section */
+
+  . = ALIGN(8192);		/* init_task */
+  .data.init_task : { *(.data.init_task) }
+
+  . = ALIGN(4096);		/* Init code and data */
+  __init_begin = .;
+  .text.init : { *(.text.init) }
+  .data.init : { *(.data.init) }
+  . = ALIGN(4096);
+  __init_end = .;
+
+  . = ALIGN(32);
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+  . = ALIGN(4096);
+  .data.page_aligned : { *(.data.idt) }
+
+
+  __bss_start = .;		/* BSS */
+  .bss : {
+	*(.bss)
+	}
+  _end = . ;
+
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+}
diff -u --recursive --new-file v2.3.5/linux/arch/m68k/kernel/ptrace.c linux/arch/m68k/kernel/ptrace.c
--- v2.3.5/linux/arch/m68k/kernel/ptrace.c	Tue May 11 09:57:14 1999
+++ linux/arch/m68k/kernel/ptrace.c	Mon Jun  7 11:15:33 1999
@@ -196,25 +196,6 @@
 	flush_tlb_all();
 }
 
-static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
-{
-	struct vm_area_struct * vma;
-
-	addr &= PAGE_MASK;
-	vma = find_vma(tsk->mm, addr);
-	if (!vma)
-		return NULL;
-	if (vma->vm_start <= addr)
-		return vma;
-	if (!(vma->vm_flags & VM_GROWSDOWN))
-		return NULL;
-	if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
-		return NULL;
-	vma->vm_offset -= vma->vm_start - addr;
-	vma->vm_start = addr;
-	return vma;
-}
-
 /*
  * This routine checks the page boundaries, and that the offset is
  * within the task area. It then calls get_long() to read a long.
diff -u --recursive --new-file v2.3.5/linux/arch/mips/kernel/irixelf.c linux/arch/mips/kernel/irixelf.c
--- v2.3.5/linux/arch/mips/kernel/irixelf.c	Wed Nov 11 11:49:59 1998
+++ linux/arch/mips/kernel/irixelf.c	Thu Jun  3 23:15:29 1999
@@ -132,9 +132,7 @@
 	end = PAGE_ALIGN(end);
 	if (end <= start) 
 		return;
-	do_mmap(NULL, start, end - start,
-		PROT_READ | PROT_WRITE | PROT_EXEC,
-		MAP_FIXED | MAP_PRIVATE, 0);
+	do_brk(start, end - start);
 }
 
 
@@ -394,9 +392,7 @@
 
 	/* Map the last of the bss segment */
 	if (last_bss > len) {
-		do_mmap(NULL, len, (last_bss - len),
-			PROT_READ|PROT_WRITE|PROT_EXEC,
-			MAP_FIXED|MAP_PRIVATE, 0);
+		do_brk(len, (last_bss - len));
 	}
 	kfree(elf_phdata);
 
@@ -589,8 +585,7 @@
 	unsigned long v;
 	struct prda *pp;
 
-	v =  do_mmap (NULL, PRDA_ADDRESS, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
-		      MAP_FIXED | MAP_PRIVATE, 0);
+	v =  do_brk (PRDA_ADDRESS, PAGE_SIZE);
 	
 	if (v < 0)
 		return;
@@ -931,9 +926,7 @@
 	len = (elf_phdata->p_filesz + elf_phdata->p_vaddr+ 0xfff) & 0xfffff000;
 	bss = elf_phdata->p_memsz + elf_phdata->p_vaddr;
 	if (bss > len)
-	  do_mmap(NULL, len, bss-len,
-		  PROT_READ|PROT_WRITE|PROT_EXEC,
-		  MAP_FIXED|MAP_PRIVATE, 0);
+	  do_brk(len, bss-len);
 	kfree(elf_phdata);
 	return 0;
 }
diff -u --recursive --new-file v2.3.5/linux/arch/mips/kernel/ptrace.c linux/arch/mips/kernel/ptrace.c
--- v2.3.5/linux/arch/mips/kernel/ptrace.c	Mon May 31 22:28:04 1999
+++ linux/arch/mips/kernel/ptrace.c	Mon Jun  7 11:15:33 1999
@@ -143,25 +143,6 @@
 	flush_tlb_page(vma, addr);
 }
 
-static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
-{
-	struct vm_area_struct * vma;
-
-	addr &= PAGE_MASK;
-	vma = find_vma(tsk->mm, addr);
-	if (!vma)
-		return NULL;
-	if (vma->vm_start <= addr)
-		return vma;
-	if (!(vma->vm_flags & VM_GROWSDOWN))
-		return NULL;
-	if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
-		return NULL;
-	vma->vm_offset -= vma->vm_start - addr;
-	vma->vm_start = addr;
-	return vma;
-}
-
 /*
  * This routine checks the page boundaries, and that the offset is
  * within the task area. It then calls get_long() to read a long.
diff -u --recursive --new-file v2.3.5/linux/arch/mips/kernel/sysirix.c linux/arch/mips/kernel/sysirix.c
--- v2.3.5/linux/arch/mips/kernel/sysirix.c	Sat May  8 11:14:01 1999
+++ linux/arch/mips/kernel/sysirix.c	Thu Jun  3 23:15:29 1999
@@ -571,7 +571,7 @@
 	 * Check against existing mmap mappings.
 	 */
 	if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE)) {
-		return -ENOMEM;
+		ret = -ENOMEM;
 		goto out;
 	}
 
@@ -579,7 +579,7 @@
 	 * Check if we have enough memory..
 	 */
 	if (!vm_enough_memory((newbrk-oldbrk) >> PAGE_SHIFT)) {
-		return -ENOMEM;
+		ret = -ENOMEM;
 		goto out;
 	}
 
@@ -587,10 +587,7 @@
 	 * Ok, looks good - let it rip.
 	 */
 	mm->brk = brk;
-	do_mmap(NULL, oldbrk, newbrk-oldbrk,
-		PROT_READ|PROT_WRITE|PROT_EXEC,
-		MAP_FIXED|MAP_PRIVATE, 0);
-
+	do_brk(oldbrk, newbrk-oldbrk);
 	ret = 0;
 
 out:
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/chrpboot/Makefile linux/arch/ppc/chrpboot/Makefile
--- v2.3.5/linux/arch/ppc/chrpboot/Makefile	Mon May 31 22:28:04 1999
+++ linux/arch/ppc/chrpboot/Makefile	Mon Jun  7 12:11:51 1999
@@ -17,7 +17,7 @@
 	$(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $<
 
 CFLAGS	= -O -fno-builtin -DSTDC_HEADERS -I$(TOPDIR)/include
-LD_ARGS = -T ../vmlinux.lds -Ttext 0x00800000
+LD_ARGS = -Ttext 0x00400000
 OBJCOPY = $(CROSS_COMPILE)objcopy
 
 OBJS = crt0.o start.o main.o misc.o ../coffboot/string.o ../coffboot/zlib.o image.o # initrd.o
@@ -65,9 +65,10 @@
 initrd.o: ramdisk.image.gz piggyback
 	./piggyback initrd < ramdisk.image.gz | $(AS) -o initrd.o
 
-zImage: $(OBJS) no_initrd.o
+zImage: $(OBJS) no_initrd.o mknote
 	$(LD) $(LD_ARGS) -o $@ $(OBJS) no_initrd.o $(LIBS)
-	objcopy zImage zImage
+	./mknote > note
+	$(OBJCOPY) $@ $@ --add-section=.note=note -R .comment
 
 zImage.initrd: $(OBJS) initrd.o
 	$(LD) $(LD_ARGS) -o $@ $(OBJS) initrd.o $(LIBS)
@@ -92,8 +93,7 @@
 
 
 clean:
-	rm -f piggyback
-	rm -f $(OBJS) zImage
+	rm -f piggyback note mknote $(OBJS) zImage
 
 fastdep:
 	$(TOPDIR)/scripts/mkdep *.[Sch] > .depend
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/chrpboot/main.c linux/arch/ppc/chrpboot/main.c
--- v2.3.5/linux/arch/ppc/chrpboot/main.c	Wed Sep 30 10:14:16 1998
+++ linux/arch/ppc/chrpboot/main.c	Mon Jun  7 12:11:51 1999
@@ -17,9 +17,9 @@
 #define get_32be(x)	(*(unsigned *)(x))
 
 #define RAM_START	0x00000000
-#define RAM_END		0x00800000	/* only 8M mapped with BATs */
+#define RAM_END		(8<<20)
 
-#define RAM_FREE	0x00540000	/* after image of chrpboot */
+#define RAM_FREE	(6<<20)		/* after image of chrpboot */
 #define PROG_START	0x00010000
 
 char *avail_ram;
@@ -38,16 +38,16 @@
     void *dst;
     unsigned char *im;
     unsigned initrd_start, initrd_size;
+    extern char _start;
     
-    printf("chrpboot starting\n\r");
-    /* setup_bats(); */
+    printf("chrpboot starting: loaded at 0x%x\n\r", &_start);
 
     if (initrd_len) {
 	initrd_size = initrd_len;
 	initrd_start = (RAM_END - initrd_size) & ~0xFFF;
 	a1 = initrd_start;
 	a2 = initrd_size;
-	printf("initial ramdisk at %x (%u bytes)\n\r", initrd_start,
+	printf("initial ramdisk at 0x%x (%u bytes)\n\r", initrd_start,
 	       initrd_size);
 	memcpy((char *)initrd_start, initrd_data, initrd_size);
 	end_avail = (char *)initrd_start;
@@ -58,25 +58,19 @@
     dst = (void *) PROG_START;
 
     if (im[0] == 0x1f && im[1] == 0x8b) {
-	void *cp = (void *) RAM_FREE;
-	avail_ram = (void *) (RAM_FREE + ((len + 7) & -8));
-	memcpy(cp, im, len);
-	printf("gunzipping... ");
-	gunzip(dst, 0x400000, cp, &len);
-	printf("done\n\r");
-
+	avail_ram = (char *)RAM_FREE;
+	printf("gunzipping (0x%x <- 0x%x:0x%0x)...", dst, im, im+len);
+	gunzip(dst, 0x400000, im, &len);
+	printf("done %u bytes\n\r", len);
     } else {
 	memmove(dst, im, len);
     }
 
     flush_cache(dst, len);
-
-    sa = PROG_START+12;
+    
+    sa = *(unsigned long *)PROG_START+PROG_START;
     printf("start address = 0x%x\n\r", sa);
 
-#if 0
-    pause();
-#endif
     (*(void (*)())sa)(a1, a2, prom, 0, 0);
 
     printf("returned?\n\r");
@@ -150,7 +144,7 @@
     s.avail_out = dstlen;
     r = inflate(&s, Z_FINISH);
     if (r != Z_OK && r != Z_STREAM_END) {
-	printf("inflate returned %d\n\r", r);
+	printf("inflate returned %d msg: %s\n\r", r, s.msg);
 	exit();
     }
     *lenp = s.next_out - (unsigned char *) dst;
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/chrpboot/mknote.c linux/arch/ppc/chrpboot/mknote.c
--- v2.3.5/linux/arch/ppc/chrpboot/mknote.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/chrpboot/mknote.c	Mon Jun  7 12:11:51 1999
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) Cort Dougan 1999.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Generate a note section as per the CHRP specification.
+ *
+ */
+
+#include <stdio.h>
+
+#define PL(x) printf("%c%c%c%c", ((x)>>24)&0xff, ((x)>>16)&0xff, ((x)>>8)&0xff, (x)&0xff );
+
+int main(void)
+{
+/* header */
+	/* namesz */
+	PL(strlen("PowerPC")+1);
+	/* descrsz */
+	PL(6*4);
+	/* type */
+	PL(0x1275);
+	/* name */
+	printf("PowerPC"); printf("%c", 0);
+	
+/* descriptor */
+	/* real-mode */
+	PL(0xffffffff);
+	/* real-base */
+	PL(0x00c00000);
+	/* real-size */
+	PL(0xffffffff);
+	/* virt-base */
+	PL(0xffffffff);
+	/* virt-size */
+	PL(0xffffffff);
+	/* load-base */
+	PL(0x4000);
+	return 0;
+}
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/coffboot/zlib.c linux/arch/ppc/coffboot/zlib.c
--- v2.3.5/linux/arch/ppc/coffboot/zlib.c	Wed Sep 30 10:14:16 1998
+++ linux/arch/ppc/coffboot/zlib.c	Mon Jun  7 12:11:51 1999
@@ -11,7 +11,7 @@
  * - added Z_PACKET_FLUSH (see zlib.h for details)
  * - added inflateIncomp
  *
- * $Id: zlib.c,v 1.2 1998/09/03 17:40:53 cort Exp $
+ * $Id: zlib.c,v 1.3 1999/05/27 22:22:54 cort Exp $
  */
 
 /*+++++*/
@@ -649,6 +649,11 @@
 /*   load local pointers */
 #define LOAD {LOADIN LOADOUT}
 
+/*
+ * The IBM 150 firmware munges the data right after _etext[].  This
+ * protects it. -- Cort
+ */
+local uInt protect_mask[] = {0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 ,0 ,0};
 /* And'ing with mask[n] masks the lower n bits */
 local uInt inflate_mask[] = {
     0x0000,
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/common_defconfig linux/arch/ppc/common_defconfig
--- v2.3.5/linux/arch/ppc/common_defconfig	Mon May 31 22:28:04 1999
+++ linux/arch/ppc/common_defconfig	Mon Jun  7 12:11:51 1999
@@ -9,14 +9,13 @@
 CONFIG_6xx=y
 # CONFIG_PPC64 is not set
 # CONFIG_8xx is not set
-CONFIG_PMAC=y
+# CONFIG_PMAC is not set
 # CONFIG_PREP is not set
 # CONFIG_CHRP is not set
-# CONFIG_ALL_PPC is not set
+CONFIG_ALL_PPC=y
 # CONFIG_APUS is not set
 # CONFIG_MBX is not set
 # CONFIG_SMP is not set
-CONFIG_MACH_SPECIFIC=y
 CONFIG_6xx=y
 
 #
@@ -51,6 +50,7 @@
 # CONFIG_TOTALMP is not set
 CONFIG_BOOTX_TEXT=y
 # CONFIG_MOTOROLA_HOTSWAP is not set
+# CONFIG_CMDLINE_BOOL is not set
 
 #
 # Plug and Play support
@@ -60,7 +60,7 @@
 #
 # Block devices
 #
-# CONFIG_BLK_DEV_FD is not set
+CONFIG_BLK_DEV_FD=y
 CONFIG_BLK_DEV_IDE=y
 
 #
@@ -77,7 +77,7 @@
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_RZ1000 is not set
 # CONFIG_BLK_DEV_IDEPCI is not set
-# CONFIG_BLK_DEV_SL82C105 is not set
+CONFIG_BLK_DEV_SL82C105=y
 CONFIG_BLK_DEV_IDE_PMAC=y
 CONFIG_BLK_DEV_IDEDMA_PMAC=y
 CONFIG_BLK_DEV_IDEDMA=y
@@ -117,7 +117,7 @@
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_MROUTE is not set
 CONFIG_IP_ALIAS=y
-# CONFIG_SYN_COOKIES is not set
+CONFIG_SYN_COOKIES=y
 
 #
 # (it is safe to leave these untouched)
@@ -199,7 +199,14 @@
 # CONFIG_SCSI_SYM53C416 is not set
 # CONFIG_SCSI_NCR53C7xx is not set
 # CONFIG_SCSI_NCR53C8XX is not set
-# CONFIG_SCSI_SYM53C8XX is not set
+CONFIG_SCSI_SYM53C8XX=y
+CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
+CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
+CONFIG_SCSI_NCR53C8XX_SYNC=20
+# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
+# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
+# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
+# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
 # CONFIG_SCSI_PCI2220I is not set
@@ -237,7 +244,7 @@
 # CONFIG_ACENIC is not set
 # CONFIG_NET_ISA is not set
 CONFIG_NET_EISA=y
-# CONFIG_PCNET32 is not set
+CONFIG_PCNET32=y
 # CONFIG_AC3200 is not set
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
@@ -297,12 +304,16 @@
 CONFIG_FB_CONTROL=y
 CONFIG_FB_PLATINUM=y
 CONFIG_FB_VALKYRIE=y
-CONFIG_FB_ATY=y
+# CONFIG_FB_ATY is not set
 CONFIG_FB_IMSTT=y
 CONFIG_FB_CT65550=y
 # CONFIG_FB_S3TRIO is not set
-# CONFIG_FB_MATROX is not set
-CONFIG_FB_ATY=y
+CONFIG_FB_MATROX=y
+# CONFIG_FB_MATROX_MILLENIUM is not set
+CONFIG_FB_MATROX_MYSTIQUE=y
+# CONFIG_FB_MATROX_G100 is not set
+# CONFIG_FB_MATROX_MULTIHEAD is not set
+# CONFIG_FB_ATY is not set
 # CONFIG_FB_VIRTUAL is not set
 # CONFIG_FBCON_ADVANCED is not set
 CONFIG_FBCON_CFB8=y
@@ -324,12 +335,22 @@
 #
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
-# CONFIG_SERIAL is not set
+CONFIG_SERIAL=m
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
-# CONFIG_MOUSE is not set
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
 # CONFIG_QIC02_TAPE is not set
 # CONFIG_WATCHDOG is not set
 CONFIG_NVRAM=y
@@ -448,7 +469,34 @@
 # CONFIG_SOUND_SONICVIBES is not set
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
-# CONFIG_SOUND_OSS is not set
+CONFIG_SOUND_OSS=y
+# CONFIG_SOUND_DMAP is not set
+# CONFIG_SOUND_PAS is not set
+# CONFIG_SOUND_SB is not set
+# CONFIG_SOUND_ADLIB is not set
+# CONFIG_SOUND_GUS is not set
+# CONFIG_SOUND_MPU401 is not set
+# CONFIG_SOUND_PSS is not set
+# CONFIG_SOUND_MSS is not set
+# CONFIG_SOUND_SSCAPE is not set
+# CONFIG_SOUND_TRIX is not set
+# CONFIG_SOUND_MAD16 is not set
+# CONFIG_SOUND_WAVEFRONT is not set
+CONFIG_SOUND_CS4232=m
+# CONFIG_SOUND_OPL3SA2 is not set
+# CONFIG_SOUND_MAUI is not set
+# CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_AD1816 is not set
+# CONFIG_SOUND_OPL3SA1 is not set
+# CONFIG_SOUND_SOFTOSS is not set
+# CONFIG_SOUND_YM3812 is not set
+# CONFIG_SOUND_VMIDI is not set
+# CONFIG_SOUND_UART6850 is not set
+
+#
+# Additional low level sound drivers
+#
+# CONFIG_LOWLEVEL_SOUND is not set
 
 #
 # Kernel hacking
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.3.5/linux/arch/ppc/config.in	Mon May 31 22:28:04 1999
+++ linux/arch/ppc/config.in	Tue Jun  8 10:52:26 1999
@@ -176,6 +176,7 @@
 endmenu
 
 source drivers/char/Config.in
+source drivers/usb/Config.in
 source fs/Config.in
 
 mainmenu_option next_comment
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/defconfig linux/arch/ppc/defconfig
--- v2.3.5/linux/arch/ppc/defconfig	Mon May 31 22:28:04 1999
+++ linux/arch/ppc/defconfig	Mon Jun  7 12:11:51 1999
@@ -9,14 +9,13 @@
 CONFIG_6xx=y
 # CONFIG_PPC64 is not set
 # CONFIG_8xx is not set
-CONFIG_PMAC=y
+# CONFIG_PMAC is not set
 # CONFIG_PREP is not set
 # CONFIG_CHRP is not set
-# CONFIG_ALL_PPC is not set
+CONFIG_ALL_PPC=y
 # CONFIG_APUS is not set
 # CONFIG_MBX is not set
 # CONFIG_SMP is not set
-CONFIG_MACH_SPECIFIC=y
 CONFIG_6xx=y
 
 #
@@ -51,6 +50,7 @@
 # CONFIG_TOTALMP is not set
 CONFIG_BOOTX_TEXT=y
 # CONFIG_MOTOROLA_HOTSWAP is not set
+# CONFIG_CMDLINE_BOOL is not set
 
 #
 # Plug and Play support
@@ -60,7 +60,7 @@
 #
 # Block devices
 #
-# CONFIG_BLK_DEV_FD is not set
+CONFIG_BLK_DEV_FD=y
 CONFIG_BLK_DEV_IDE=y
 
 #
@@ -77,7 +77,7 @@
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_RZ1000 is not set
 # CONFIG_BLK_DEV_IDEPCI is not set
-# CONFIG_BLK_DEV_SL82C105 is not set
+CONFIG_BLK_DEV_SL82C105=y
 CONFIG_BLK_DEV_IDE_PMAC=y
 CONFIG_BLK_DEV_IDEDMA_PMAC=y
 CONFIG_BLK_DEV_IDEDMA=y
@@ -117,7 +117,7 @@
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_MROUTE is not set
 CONFIG_IP_ALIAS=y
-# CONFIG_SYN_COOKIES is not set
+CONFIG_SYN_COOKIES=y
 
 #
 # (it is safe to leave these untouched)
@@ -199,7 +199,14 @@
 # CONFIG_SCSI_SYM53C416 is not set
 # CONFIG_SCSI_NCR53C7xx is not set
 # CONFIG_SCSI_NCR53C8XX is not set
-# CONFIG_SCSI_SYM53C8XX is not set
+CONFIG_SCSI_SYM53C8XX=y
+CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
+CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
+CONFIG_SCSI_NCR53C8XX_SYNC=20
+# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
+# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
+# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
+# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
 # CONFIG_SCSI_PCI2220I is not set
@@ -237,7 +244,7 @@
 # CONFIG_ACENIC is not set
 # CONFIG_NET_ISA is not set
 CONFIG_NET_EISA=y
-# CONFIG_PCNET32 is not set
+CONFIG_PCNET32=y
 # CONFIG_AC3200 is not set
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
@@ -297,12 +304,16 @@
 CONFIG_FB_CONTROL=y
 CONFIG_FB_PLATINUM=y
 CONFIG_FB_VALKYRIE=y
-CONFIG_FB_ATY=y
+# CONFIG_FB_ATY is not set
 CONFIG_FB_IMSTT=y
 CONFIG_FB_CT65550=y
 # CONFIG_FB_S3TRIO is not set
-# CONFIG_FB_MATROX is not set
-CONFIG_FB_ATY=y
+CONFIG_FB_MATROX=y
+# CONFIG_FB_MATROX_MILLENIUM is not set
+CONFIG_FB_MATROX_MYSTIQUE=y
+# CONFIG_FB_MATROX_G100 is not set
+# CONFIG_FB_MATROX_MULTIHEAD is not set
+# CONFIG_FB_ATY is not set
 # CONFIG_FB_VIRTUAL is not set
 # CONFIG_FBCON_ADVANCED is not set
 CONFIG_FBCON_CFB8=y
@@ -324,12 +335,22 @@
 #
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
-# CONFIG_SERIAL is not set
+CONFIG_SERIAL=m
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
-# CONFIG_MOUSE is not set
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
 # CONFIG_QIC02_TAPE is not set
 # CONFIG_WATCHDOG is not set
 CONFIG_NVRAM=y
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/kernel/chrp_pci.c linux/arch/ppc/kernel/chrp_pci.c
--- v2.3.5/linux/arch/ppc/kernel/chrp_pci.c	Mon May 31 22:28:04 1999
+++ linux/arch/ppc/kernel/chrp_pci.c	Mon Jun  7 12:11:51 1999
@@ -167,6 +167,62 @@
 	return PCIBIOS_SUCCESSFUL;
 }
 
+
+int rtas_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
+				    unsigned char offset, unsigned char *val)
+{
+	unsigned long addr = (offset&0xff) | ((dev_fn&0xff)<<8) | ((bus & 0xff)<<16);
+	if ( call_rtas( "read-pci-config", 2, 2, (ulong *)&val, addr, 1 ) != 0 )
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int rtas_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
+				    unsigned char offset, unsigned short *val)
+{
+	unsigned long addr = (offset&0xff) | ((dev_fn&0xff)<<8) | ((bus & 0xff)<<16);
+	if ( call_rtas( "read-pci-config", 2, 2, (ulong *)&val, addr, 2 ) != 0 )
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+int rtas_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
+				     unsigned char offset, unsigned int *val)
+{
+	unsigned long addr = (offset&0xff) | ((dev_fn&0xff)<<8) | ((bus & 0xff)<<16);
+	if ( call_rtas( "read-pci-config", 2, 2, (ulong *)&val, addr, 4 ) != 0 )
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int rtas_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
+				     unsigned char offset, unsigned char val)
+{
+	unsigned long addr = (offset&0xff) | ((dev_fn&0xff)<<8) | ((bus & 0xff)<<16);
+	if ( call_rtas( "write-pci-config", 3, 1, NULL, addr, 1, (ulong)val ) != 0 )
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int rtas_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
+				     unsigned char offset, unsigned short val)
+{
+	unsigned long addr = (offset&0xff) | ((dev_fn&0xff)<<8) | ((bus & 0xff)<<16);
+	if ( call_rtas( "write-pci-config", 3, 1, NULL, addr, 2, (ulong)val ) != 0 )
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int rtas_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
+				      unsigned char offset, unsigned int val)
+{
+	unsigned long addr = (offset&0xff) | ((dev_fn&0xff)<<8) | ((bus & 0xff)<<16);
+	if ( call_rtas( "write-pci-config", 3, 1, NULL, addr, 4, (ulong)val ) != 0 )
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	return PCIBIOS_SUCCESSFUL;
+}
+
     /*
      *  Temporary fixes for PCI devices. These should be replaced by OF query
      *  code -- Geert
@@ -256,6 +312,7 @@
 
 decl_config_access_method(grackle);
 decl_config_access_method(indirect);
+decl_config_access_method(rtas);
 
 void __init
 chrp_setup_pci_ptrs(void)
@@ -276,7 +333,7 @@
 		{
 			/* find out how many pythons */
 			while ( (py = py->next) ) python_busnr++;
-                        set_config_access_method(python);
+			set_config_access_method(python);
 			/*
 			 * We base these values on the machine type but should
 			 * try to read them from the python controller itself.
@@ -297,10 +354,22 @@
                 }
                 else
                 {
-			pci_dram_offset = 0;
-			isa_mem_base = 0xf7000000;
-			isa_io_base = 0xf8000000;
-			set_config_access_method(gg2);
+			if ( !strncmp("IBM,7043-150", get_property(find_path_device("/"), "name", NULL),12) )
+			{
+				pci_dram_offset = 0;
+				isa_mem_base = 0x80000000;
+				isa_io_base = 0xfe000000;
+				pci_config_address = (unsigned int *)0xfec00000;
+				pci_config_data = (unsigned char *)0xfee00000;
+				set_config_access_method(indirect);
+			}
+			else
+			{
+				pci_dram_offset = 0;
+				isa_mem_base = 0xf7000000;
+				isa_io_base = 0xf8000000;
+				set_config_access_method(gg2);
+			}
                 }
         }
 	
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/kernel/chrp_setup.c linux/arch/ppc/kernel/chrp_setup.c
--- v2.3.5/linux/arch/ppc/kernel/chrp_setup.c	Mon May 31 22:28:04 1999
+++ linux/arch/ppc/kernel/chrp_setup.c	Mon Jun  7 12:11:51 1999
@@ -32,6 +32,7 @@
 #include <linux/console.h>
 #include <linux/pci.h>
 #include <linux/openpic.h>
+#include <linux/version.h>
 
 #include <asm/mmu.h>
 #include <asm/processor.h>
@@ -65,6 +66,7 @@
 
 unsigned long chrp_get_rtc_time(void);
 int chrp_set_rtc_time(unsigned long nowtime);
+unsigned long rtas_event_scan_rate = 0, rtas_event_scan_ct = 0;
 void chrp_calibrate_decr(void);
 void chrp_time_init(void);
 
@@ -235,6 +237,7 @@
 	   chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
 {
 	extern char cmd_line[];
+	struct device_node *device;
 
 	/* init to some ~sane value until calibrate_delay() runs */
 	loops_per_sec = 50000000;
@@ -274,57 +277,73 @@
 			find_path_device("/"), "platform-open-pic", NULL);
 		OpenPIC = ioremap((unsigned long)OpenPIC, sizeof(struct OpenPIC));
 	}
-	
+
 	/*
 	 *  Fix the Super I/O configuration
 	 */
-	sio_init();
+	/*sio_init();*/
 #ifdef CONFIG_DUMMY_CONSOLE
 	conswitchp = &dummy_con;
 #endif
-	/* my starmax 6000 needs this but the longtrail shouldn't do it -- Cort */
-	if ( !strncmp("MOT", get_property(find_path_device("/"),
-					  "model", NULL),3) )
-		*memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p);
+	*memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p);
+
+	/* Get the event scan rate for the rtas so we know how
+	 * often it expects a heartbeat. -- Cort
+	 */
+	if ( rtas_data )
+	{
+		struct property *p;
+		device = find_devices("rtas");
+		for ( p = device->properties;
+		      strncmp(p->name, "rtas-event-scan-rate", 20) && p ;
+		      p = p->next )
+			/* nothing */ ;
+		if ( p && *(unsigned long *)p->value )
+		{
+			rtas_event_scan_rate = (HZ/(*(unsigned long *)p->value)*30)-1;
+			rtas_event_scan_ct = 1;
+			printk("RTAS Event Scan Rate: %lu (%lu jiffies)\n",
+			       *(unsigned long *)p->value, rtas_event_scan_rate );
+		}
+	}
 }
 
 void
+chrp_event_scan(void)
+{
+	unsigned char log[1024];
+	if ( rtas_event_scan_rate && (rtas_event_scan_ct-- <= 0) )
+	{
+		call_rtas( "event-scan", 4, 1, NULL, 0x0, 1, __pa(log), 1024 );
+		rtas_event_scan_ct = rtas_event_scan_rate;
+	}
+}
+	
+void
 chrp_restart(char *cmd)
 {
-#if 0
-	extern unsigned int rtas_entry, rtas_data, rtas_size;
 	printk("RTAS system-reboot returned %d\n",
 	       call_rtas("system-reboot", 0, 1, NULL));
-	printk("rtas_entry: %08lx rtas_data: %08lx rtas_size: %08lx\n",
-	       rtas_entry,rtas_data,rtas_size);
 	for (;;);
-#else
-	printk("System Halted\n");
-	while(1);
-#endif
 }
 
 void
 chrp_power_off(void)
 {
-	/* RTAS doesn't seem to work on Longtrail.
-	   For now, do it the same way as the PReP. */
-#if 0
-	extern unsigned int rtas_entry, rtas_data, rtas_size;
+	/* allow power on only with power button press */
+#define	PWR_FIELD(x) (0x8000000000000000 >> ((x)-96))
 	printk("RTAS power-off returned %d\n",
-	       call_rtas("power-off", 2, 1, NULL, 0, 0));
-	printk("rtas_entry: %08lx rtas_data: %08lx rtas_size: %08lx\n",
-	       rtas_entry,rtas_data,rtas_size);
+	       call_rtas("power-off", 2, 1, NULL,
+			 ((PWR_FIELD(96)|PWR_FIELD(97))>>32)&0xffffffff,
+			 (PWR_FIELD(96)|PWR_FIELD(97))&0xffffffff));
+#undef PWR_FIELD	
 	for (;;);
-#else
-	chrp_restart(NULL);
-#endif
 }
 
 void
 chrp_halt(void)
 {
-	chrp_restart(NULL);
+	chrp_power_off();
 }
 
 u_int
@@ -653,5 +672,21 @@
         ppc_ide_md.ide_init_hwif = chrp_ide_init_hwif_ports;
 
         ppc_ide_md.io_base = _IO_BASE;
-#endif		
+#endif
+	/*
+	 * Print the banner, then scroll down so boot progress
+	 * can be printed.  -- Cort 
+	 */
+	chrp_progress("Linux/PPC "UTS_RELEASE"\n");
+}
+
+void chrp_progress(char *s)
+{
+	extern unsigned int rtas_data;
+	
+	if ( (_machine != _MACH_chrp) || !rtas_data )
+		return;
+	call_rtas( "display-character", 1, 1, NULL, '\r' );
+	while ( *s )
+		call_rtas( "display-character", 1, 1, NULL, *s++ );
 }
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S
--- v2.3.5/linux/arch/ppc/kernel/head.S	Mon May 31 22:28:04 1999
+++ linux/arch/ppc/kernel/head.S	Mon Jun  7 12:11:51 1999
@@ -1,7 +1,7 @@
 /*
  *  arch/ppc/kernel/head.S
  *
- *  $Id: head.S,v 1.131 1999/05/14 22:37:21 cort Exp $
+ *  $Id: head.S,v 1.133 1999/05/20 05:13:08 cort Exp $
  *
  *  PowerPC version 
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
@@ -2615,7 +2615,6 @@
  */
 	.globl	enter_rtas
 enter_rtas:
-	stwu	r1,-16(r1)
 	mflr	r0
 	stw	r0,20(r1)
 	lis	r4,rtas_data@ha
@@ -2636,7 +2635,6 @@
 	andi.	r9,r9,MSR_ME|MSR_RI
 	sync			/* disable interrupts so SRR0/1 */
 	mtmsr	r0		/* don't get trashed */
-	li	r6,0	
 	mtlr	r6
 	mtspr	SPRG2,r7
 	mtspr	SRR0,r8
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/kernel/idle.c linux/arch/ppc/kernel/idle.c
--- v2.3.5/linux/arch/ppc/kernel/idle.c	Thu Apr 29 12:39:01 1999
+++ linux/arch/ppc/kernel/idle.c	Mon Jun  7 12:11:51 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: idle.c,v 1.61 1999/03/18 04:15:45 cort Exp $
+ * $Id: idle.c,v 1.62 1999/05/24 05:43:18 cort Exp $
  *
  * Idle daemon for PowerPC.  Idle daemon will handle any action
  * that needs to be taken when the system becomes idle.
@@ -50,6 +50,7 @@
 	/* endless loop with no priority at all */
 	current->priority = 0;
 	current->counter = -100;
+	init_idle();	
 	for (;;)
 	{
 		__sti();
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c
--- v2.3.5/linux/arch/ppc/kernel/irq.c	Thu Apr 29 12:39:01 1999
+++ linux/arch/ppc/kernel/irq.c	Mon Jun  7 12:11:51 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: irq.c,v 1.105 1999/03/25 19:51:51 cort Exp $
+ * $Id: irq.c,v 1.106 1999/05/25 21:16:04 cort Exp $
  *
  *  arch/ppc/kernel/irq.c
  *
@@ -65,7 +65,6 @@
 void enable_irq(unsigned int irq_nr);
 void disable_irq(unsigned int irq_nr);
 
-/* Fixme - Need to figure out a way to get rid of this - Corey */
 volatile unsigned char *chrp_int_ack_special;
 
 #ifdef CONFIG_APUS
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/kernel/misc.S linux/arch/ppc/kernel/misc.S
--- v2.3.5/linux/arch/ppc/kernel/misc.S	Mon May 31 22:28:04 1999
+++ linux/arch/ppc/kernel/misc.S	Mon Jun  7 12:11:51 1999
@@ -866,7 +866,11 @@
 	.long sys_getresuid	/* 165 */
 	.long sys_query_module
 	.long sys_poll
+#ifdef CONFIG_NFS	
 	.long sys_nfsservctl
+#else
+	.long sys_ni_syscall
+#endif		
 	.long sys_setresgid
 	.long sys_getresgid	/* 170 */
 	.long sys_prctl
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c
--- v2.3.5/linux/arch/ppc/kernel/prom.c	Tue May 11 08:24:32 1999
+++ linux/arch/ppc/kernel/prom.c	Mon Jun  7 12:11:51 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: prom.c,v 1.54 1999/05/10 04:43:46 cort Exp $
+ * $Id: prom.c,v 1.60 1999/05/25 01:42:41 cort Exp $
  *
  * Procedures for interfacing to the Open Firmware PROM on
  * Power Macintosh computers.
@@ -24,6 +24,7 @@
 #include <asm/io.h>
 #include <asm/smp.h>
 #include <asm/bootx.h>
+#include <asm/system.h>
 
 /*
  * Properties whose value is longer than this get excluded from our
@@ -412,6 +413,9 @@
 	mem = copy_device_tree(mem, mem + (1<<20));
 	prom_print(RELOC("done\n"));
 
+
+	RELOC(klimit) = (char *) (mem - offset);
+	
 	prom_rtas = call_prom(RELOC("finddevice"), 1, 1, RELOC("/rtas"));
 	if (prom_rtas != (void *) -1) {
 		RELOC(rtas_size) = 0;
@@ -421,9 +425,19 @@
 		if (RELOC(rtas_size) == 0) {
 			RELOC(rtas_data) = 0;
 		} else {
-			mem = (mem + 4095) & -4096; /* round to page bdry */
+			/*
+			 * We do _not_ want the rtas_data inside the klimit
+			 * boundry since it'll be squashed when we do the
+			 * relocate of the kernel on chrp right after prom_init()
+			 * in head.S.  So, we just pick a spot in memory.
+			 * -- Cort
+			 */
+#if 0
+			mem = (mem + 4095) & -4096;
 			RELOC(rtas_data) = mem + KERNELBASE;
 			mem += RELOC(rtas_size);
+#endif
+			RELOC(rtas_data) = (6<<20) + KERNELBASE;
 		}
 		prom_rtas = call_prom(RELOC("open"), 1, 1, RELOC("/rtas"));
 		{
@@ -448,7 +462,7 @@
 		else
 			prom_print(RELOC(" done\n"));
 	}
-	RELOC(klimit) = (char *) (mem - offset);
+
 #ifdef CONFIG_SMP
 	/*
 	 * With CHRP SMP we need to use the OF to start the other
@@ -1289,7 +1303,7 @@
 	  unsigned long *outputs, ...)
 {
 	va_list list;
-	int i;
+	int i, s;
 	struct device_node *rtas;
 	int *tokp;
 	union {
@@ -1305,16 +1319,19 @@
 		printk(KERN_ERR "No RTAS service called %s\n", service);
 		return -1;
 	}
-	u.words[0] = __pa(*tokp);
+	u.words[0] = *tokp;
 	u.words[1] = nargs;
 	u.words[2] = nret;
 	va_start(list, outputs);
 	for (i = 0; i < nargs; ++i)
 		u.words[i+3] = va_arg(list, unsigned long);
 	va_end(list);
+	
+	s = _disable_interrupts();
 	spin_lock(&rtas_lock);
 	enter_rtas((void *)__pa(&u));
 	spin_unlock(&rtas_lock);
+	_enable_interrupts(s);
 	if (nret > 1 && outputs != NULL)
 		for (i = 0; i < nret-1; ++i)
 			outputs[i] = u.words[i+nargs+4];
@@ -1326,8 +1343,7 @@
 abort()
 {
 #ifdef CONFIG_XMON
-	extern void xmon(void *);
-	xmon(0);
+	xmon(NULL);
 #endif
 	prom_exit();
 }
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/kernel/ptrace.c linux/arch/ppc/kernel/ptrace.c
--- v2.3.5/linux/arch/ppc/kernel/ptrace.c	Mon May 31 22:28:04 1999
+++ linux/arch/ppc/kernel/ptrace.c	Mon Jun  7 11:15:33 1999
@@ -190,25 +190,6 @@
 	flush_tlb_all();
 }
 
-static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
-{
-	struct vm_area_struct * vma;
-
-	addr &= PAGE_MASK;
-	vma = find_vma(tsk->mm,addr);
-	if (!vma)
-		return NULL;
-	if (vma->vm_start <= addr)
-		return vma;
-	if (!(vma->vm_flags & VM_GROWSDOWN))
-		return NULL;
-	if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
-		return NULL;
-	vma->vm_offset -= vma->vm_start - addr;
-	vma->vm_start = addr;
-	return vma;
-}
-
 /*
  * This routine checks the page boundaries, and that the offset is
  * within the task area. It then calls get_long() to read a long.
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/kernel/smp.c linux/arch/ppc/kernel/smp.c
--- v2.3.5/linux/arch/ppc/kernel/smp.c	Thu Apr 29 12:39:01 1999
+++ linux/arch/ppc/kernel/smp.c	Mon Jun  7 12:11:51 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: smp.c,v 1.49 1999/03/18 04:16:31 cort Exp $
+ * $Id: smp.c,v 1.52 1999/05/23 22:43:51 cort Exp $
  *
  * Smp support for ppc.
  *
@@ -388,9 +388,12 @@
 
 void __init smp_callin(void)
 {
+	int i;
+	
 	printk("SMP %d: smp_callin()\n",current->processor);
         smp_store_cpu_info(current->processor);
 	set_dec(decrementer_count);
+	
 #if 0
 	current->mm->mmap->vm_page_prot = PAGE_SHARED;
 	current->mm->mmap->vm_start = PAGE_OFFSET;
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/kernel/syscalls.c linux/arch/ppc/kernel/syscalls.c
--- v2.3.5/linux/arch/ppc/kernel/syscalls.c	Mon May 31 22:28:04 1999
+++ linux/arch/ppc/kernel/syscalls.c	Mon Jun  7 12:11:51 1999
@@ -182,18 +182,14 @@
 	int fd[2];
 	int error;
 
-	error = verify_area(VERIFY_WRITE, fildes, 8);
-	if (error)
-		return error;
 	lock_kernel();
 	error = do_pipe(fd);
 	unlock_kernel();
-	if (error)
-		return error;
-	if (__put_user(fd[0],0+fildes)
-	    || __put_user(fd[1],1+fildes))
-		return -EFAULT;	/* should we close the fds? */
-	return 0;
+	if (!error) {
+		if (copy_to_user(fildes, fd, 2*sizeof(int)))
+			error = -EFAULT;
+	}
+	return error;
 }
 
 asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len,
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c
--- v2.3.5/linux/arch/ppc/kernel/time.c	Thu Apr 29 12:39:01 1999
+++ linux/arch/ppc/kernel/time.c	Mon Jun  7 12:11:51 1999
@@ -1,5 +1,5 @@
 /*
- * $Id: time.c,v 1.47 1999/03/18 05:11:11 cort Exp $
+ * $Id: time.c,v 1.48 1999/05/22 19:35:57 cort Exp $
  * Common time routines among all ppc machines.
  *
  * Written by Cort Dougan (cort@cs.nmt.edu) to merge
@@ -126,13 +126,17 @@
 	smp_local_timer_interrupt(regs);
 #endif		
 
-	/* Fixme - make this more generic - Corey */
 #ifdef CONFIG_APUS
 	{
 		extern void apus_heartbeat (void);
 		apus_heartbeat ();
 	}
 #endif
+#if defined(CONFIG_ALL_PPC) || defined(CONFIG_CHRP)
+	if ( _machine == _MACH_chrp )
+		chrp_event_scan();
+#endif	
+
 	hardirq_exit(cpu);
 }
 
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/mm/init.c linux/arch/ppc/mm/init.c
--- v2.3.5/linux/arch/ppc/mm/init.c	Mon May 31 22:28:04 1999
+++ linux/arch/ppc/mm/init.c	Mon Jun  7 12:11:51 1999
@@ -1,5 +1,5 @@
 /*
- *  $Id: init.c,v 1.165 1999/05/14 22:37:29 cort Exp $
+ *  $Id: init.c,v 1.166 1999/05/22 18:18:30 cort Exp $
  *
  *  PowerPC version 
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
@@ -402,7 +402,7 @@
 	for (i = 0; i < size; i += PAGE_SIZE)
 		map_page(&init_task, v+i, p+i, flags);
 out:	
-	return (void *) (v + (p & ~PAGE_MASK));
+	return (void *) (v + (addr & ~PAGE_MASK));
 }
 
 void iounmap(void *addr)
diff -u --recursive --new-file v2.3.5/linux/arch/ppc/xmon/xmon.c linux/arch/ppc/xmon/xmon.c
--- v2.3.5/linux/arch/ppc/xmon/xmon.c	Mon May 31 22:28:04 1999
+++ linux/arch/ppc/xmon/xmon.c	Mon Jun  7 12:11:51 1999
@@ -999,7 +999,7 @@
 	int c;
 
 	c = inchar();
-	if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n')
+	if ((isxdigit(c) && (c != 'f') && (c != 'd')) || (c == '\n'))
 		termch = c;
 	scanhex(&adrs);
 	if( termch != '\n')
diff -u --recursive --new-file v2.3.5/linux/arch/sparc/Makefile linux/arch/sparc/Makefile
--- v2.3.5/linux/arch/sparc/Makefile	Sun Oct  4 10:22:42 1998
+++ linux/arch/sparc/Makefile	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.39 1998/09/16 12:31:31 jj Exp $
+# $Id: Makefile,v 1.41 1999/06/04 13:29:05 jj Exp $
 # sparc/Makefile
 #
 # Makefile for the architecture dependent flags and dependencies on the
@@ -15,7 +15,7 @@
 # Uncomment the first CFLAGS if you are doing kgdb source level
 # debugging of the kernel to get the proper debugging information.
 
-IS_EGCS := $(shell if $(CC) --version 2>&1 | grep 'egcs' > /dev/null; then echo y; else echo n; fi)
+IS_EGCS := $(shell if $(CC) -c -m32 -o _tmp.o arch/sparc/math-emu/fnegs.c >/dev/null 2>&1; then echo y; else echo n; fi; rm -f _tmp.o)
 NEW_GAS := $(shell if $(LD) --version 2>&1 | grep 'elf64_sparc' > /dev/null; then echo y; else echo n; fi)
 
 ifeq ($(NEW_GAS),y)
diff -u --recursive --new-file v2.3.5/linux/arch/sparc/kernel/ebus.c linux/arch/sparc/kernel/ebus.c
--- v2.3.5/linux/arch/sparc/kernel/ebus.c	Tue Oct 27 09:52:20 1998
+++ linux/arch/sparc/kernel/ebus.c	Wed Jun  9 14:44:25 1999
@@ -1,9 +1,10 @@
-/* $Id: ebus.c,v 1.2 1998/10/07 11:35:16 jj Exp $
+/* $Id: ebus.c,v 1.3 1999/06/03 15:02:09 davem Exp $
  * ebus.c: PCI to EBus bridge device.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
  *
  * Adopted for sparc by V. Roganov and G. Raiko.
+ * Fixes for different platforms by Pete Zaitcev.
  */
 
 #include <linux/config.h>
@@ -25,9 +26,9 @@
 #undef DEBUG_FILL_EBUS_DEV
 
 #ifdef PROM_DEBUG
-#define dprintf prom_printf
+#define dprintk prom_printf
 #else
-#define dprintf printk
+#define dprintk printk
 #endif
 
 struct linux_ebus *ebus_chain = 0;
@@ -48,6 +49,9 @@
 extern int envctrl_init(void);
 #endif
 
+/* We are together with pcic.c under CONFIG_PCI. */
+extern unsigned int pcic_pin_to_irq(unsigned int, char *name);
+
 static inline unsigned long ebus_alloc(size_t size)
 {
 	return (unsigned long)kmalloc(size, GFP_ATOMIC);
@@ -66,6 +70,7 @@
 	strcpy(dev->prom_name, lbuf);
 
 	len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs));
+	if (len == -1) len = 0;
 	dev->num_addrs = len / sizeof(regs[0]);
 
 	for (i = 0; i < dev->num_addrs; i++) {
@@ -77,22 +82,36 @@
 		dev->base_address[i] = dev->parent->base_address[regs[i]];
 	}
 
+	/*
+	 * Houston, we have a problem...
+	 * Sometimes PROM supplies absolutely meaningless properties.
+	 * Still, we take what it gives since we have nothing better.
+	 * Children of ebus may be wired on any input pin of PCIC.
+	 */
 	len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs));
 	if ((len == -1) || (len == 0)) {
 		dev->num_irqs = 0;
-		/*
-		 * Oh, well, some PROMs don't export interrupts
-		 * property to children of EBus devices...
-		 *
-		 * Be smart about PS/2 keyboard and mouse.
-		 */
-		if (!strcmp(dev->parent->prom_name, "8042")) {
+		dev->irqs[0] = 0;
+		if (dev->parent->num_irqs != 0) {
 			dev->num_irqs = 1;
 			dev->irqs[0] = dev->parent->irqs[0];
+/* P3 remove */ printk("EBUS: dev %s irq %d from parent\n", dev->prom_name, dev->irqs[0]);
 		}
 	} else {
 		dev->num_irqs = len / sizeof(irqs[0]);
-		printk("FIXME: %s irq(%d)\n", dev->prom_name, irqs[0]);
+		if (irqs[0] == 0 || irqs[0] >= 8) {
+			/*
+			 * XXX Zero is a valid pin number...
+			 * This works as long as Ebus is not wired to INTA#.
+			 */
+			printk("EBUS: %s got bad irq %d from PROM\n",
+			    dev->prom_name, irqs[0]);
+			dev->num_irqs = 0;
+			dev->irqs[0] = 0;
+		} else {
+			dev->irqs[0] = pcic_pin_to_irq(irqs[0], dev->prom_name);
+/* P3 remove */ printk("EBUS: dev %s irq %d from PROM\n", dev->prom_name, dev->irqs[0]);
+		}
 	}
 
 #ifdef DEBUG_FILL_EBUS_DEV
@@ -131,7 +150,30 @@
 	dev->num_addrs = len / sizeof(struct linux_prom_registers);
 
 	for (i = 0; i < dev->num_addrs; i++) {
-		n = (regs[i].which_io - 0x10) >> 2;
+		/*
+		 * XXX Collect JE-1 PROM
+		 * 
+		 * Example - JS-E with 3.11:
+		 *  /ebus
+		 *      regs 
+		 *        0x00000000, 0x0, 0x00000000, 0x0, 0x00000000,
+		 *        0x82000010, 0x0, 0xf0000000, 0x0, 0x01000000,
+		 *        0x82000014, 0x0, 0x38800000, 0x0, 0x00800000,
+		 *      ranges
+		 *        0x00, 0x00000000, 0x02000010, 0x0, 0x0, 0x01000000,
+		 *        0x01, 0x01000000, 0x02000014, 0x0, 0x0, 0x00800000,
+		 *  /ebus/8042
+		 *      regs
+		 *        0x00000001, 0x00300060, 0x00000008,
+		 *        0x00000001, 0x00300060, 0x00000008,
+		 */
+		n = regs[i].which_io;
+		if (n >= 4) {
+			/* XXX This is copied from old JE-1 by Gleb. */
+			n = (regs[i].which_io - 0x10) >> 2;
+		} else {
+			;
+		}
 
 		dev->base_address[i] = dev->bus->self->base_address[n];
 		dev->base_address[i] += regs[i].phys_addr;
@@ -141,8 +183,14 @@
 		       (unsigned long)sparc_alloc_io (dev->base_address[i], 0,
 						      regs[i].reg_size,
 						      dev->prom_name, 0, 0);
+#if 0
+/*
+ * This release_region() screwes those who do sparc_alloc_io().
+ * Change drivers which do check_region(). See drivers/block/floppy.c.
+ */
 		    /* Some drivers call 'check_region', so we release it */
                     release_region(dev->base_address[i] & PAGE_MASK, PAGE_SIZE);
+#endif
 
 		    if (dev->base_address[i] == 0 ) {
 			panic("ebus: unable sparc_alloc_io for dev %s",
@@ -154,12 +202,22 @@
 	len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs));
 	if ((len == -1) || (len == 0)) {
 		dev->num_irqs = 0;
+		if ((dev->irqs[0] = dev->bus->self->irq) != 0) {
+			 dev->num_irqs = 1;
+/* P3 remove */ printk("EBUS: child %s irq %d from parent\n", dev->prom_name, dev->irqs[0]);
+		}
 	} else {
-		dev->num_irqs = len / sizeof(irqs[0]);
-
-#define IRQ_8042 7
-		if (irqs[0] == 4) dev->irqs[0] = IRQ_8042;
-		printk("FIXME: %s irq(%d)\n", dev->prom_name, irqs[0]);
+		dev->num_irqs = 1;  /* dev->num_irqs = len / sizeof(irqs[0]); */
+		if (irqs[0] == 0 || irqs[0] >= 8) {
+			/* See above for the parent. XXX */
+			printk("EBUS: %s got bad irq %d from PROM\n",
+			    dev->prom_name, irqs[0]);
+			dev->num_irqs = 0;
+			dev->irqs[0] = 0;
+		} else {
+			dev->irqs[0] = pcic_pin_to_irq(irqs[0], dev->prom_name);
+/* P3 remove */ printk("EBUS: child %s irq %d from PROM\n", dev->prom_name, dev->irqs[0]);
+		}
 	}
 
 #ifdef DEBUG_FILL_EBUS_DEV
diff -u --recursive --new-file v2.3.5/linux/arch/sparc/kernel/entry.S linux/arch/sparc/kernel/entry.S
--- v2.3.5/linux/arch/sparc/kernel/entry.S	Tue May 11 08:24:31 1999
+++ linux/arch/sparc/kernel/entry.S	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.159 1999/05/08 03:00:03 davem Exp $
+/* $Id: entry.S,v 1.160 1999/06/03 15:02:11 davem Exp $
  * arch/sparc/kernel/entry.S:  Sparc trap low-level entry points.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -1888,5 +1888,53 @@
 	LOAD_CURRENT(g6, o0)
 	retl
 	 nop
+
+#ifdef CONFIG_PCI
+#include <asm/pcic.h>
+
+	.align	4
+	.globl	linux_trap_ipi15_pcic
+linux_trap_ipi15_pcic:
+	rd	%wim, %l3
+	SAVE_ALL
+
+	/*
+	 * First deactivate NMI
+	 * or we cannot drop ET, cannot get window spill traps.
+	 * The busy loop is necessary because the PIO error
+	 * sometimes does not go away quickly and we trap again.
+	 */
+	sethi	%hi(C_LABEL(pcic_regs)), %o1
+	ld	[%o1 + %lo(C_LABEL(pcic_regs))], %o2
+
+	! Get pending status for printouts later.
+	ld	[%o2 + PCI_SYS_INT_PENDING], %o0
+
+	mov	PCI_SYS_INT_PENDING_CLEAR_ALL, %o1
+	stb	%o1, [%o2 + PCI_SYS_INT_PENDING_CLEAR]
+1:
+	ld	[%o2 + PCI_SYS_INT_PENDING], %o1
+	andcc	%o1, ((PCI_SYS_INT_PENDING_PIO|PCI_SYS_INT_PENDING_PCI)>>24), %g0
+	bne	1b
+	 nop
+
+	or	%l0, PSR_PIL, %l4
+	wr	%l4, 0x0, %psr
+	WRITE_PAUSE
+	wr	%l4, PSR_ET, %psr
+	WRITE_PAUSE
+
+	call	C_LABEL(pcic_nmi)
+	 add	%sp, REGWIN_SZ, %o1	! struct pt_regs *regs
+	RESTORE_ALL
+
+	.globl	C_LABEL(pcic_nmi_trap_patch)
+C_LABEL(pcic_nmi_trap_patch):
+	sethi	%hi(linux_trap_ipi15_pcic), %l3
+	jmpl	%l3 + %lo(linux_trap_ipi15_pcic), %g0
+	 rd	%psr, %l0
+	.word	0
+
+#endif /* CONFIG_PCI */
 
 /* End of entry.S */
diff -u --recursive --new-file v2.3.5/linux/arch/sparc/kernel/head.S linux/arch/sparc/kernel/head.S
--- v2.3.5/linux/arch/sparc/kernel/head.S	Thu Apr 22 19:24:51 1999
+++ linux/arch/sparc/kernel/head.S	Wed Jun  9 14:44:25 1999
@@ -1,11 +1,13 @@
-/* $Id: head.S,v 1.95 1999/04/13 07:40:34 anton Exp $
+/* $Id: head.S,v 1.96 1999/06/03 15:02:15 davem Exp $
  * head.S: The initial boot code for the Sparc port of Linux.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1995 Peter Zaitcev   (Zaitcev@ipmce.su)
+ * Copyright (C) 1995,1999 Pete Zaitcev   (zaitcev@metabyte.com)
  * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
  * Copyright (C) 1997 Jakub Jelinek   (jj@sunsite.mff.cuni.cz)
  * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
+ *
+ * CompactPCI platform by Eric Brower, 1999.
  */
 
 #include <linux/version.h>
@@ -116,10 +118,10 @@
 t_irq12:TRAP_ENTRY_INTERRUPT(12)            /* IRQ Zilog serial chip         */
 t_irq13:TRAP_ENTRY_INTERRUPT(13)            /* IRQ Audio Intr.               */
 t_irq14:TRAP_ENTRY_INTERRUPT(14)            /* IRQ Timer #2                  */
+	.globl	t_nmi
 #ifndef __SMP__
 t_nmi:	NMI_TRAP                            /* Level 15 (NMI)                */
 #else
-	.globl	t_nmi
 t_nmi:	TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
 #endif
 t_racc:	TRAP_ENTRY(0x20, do_reg_access)     /* General Register Access Error */
@@ -842,6 +844,8 @@
 		be	1f
 		 cmp	%l1, 'm'
 		be	1f
+		 cmp	%l1, 's'
+		be	1f
 		 cmp	%l1, 'd'
 		be	1f
 		 cmp	%l1, 'e'
@@ -853,6 +857,8 @@
 1:		set	C_LABEL(cputypval), %l1
 		ldub	[%l1 + 0x4], %l1
 		cmp	%l1, 'm'		! Test for sun4d, sun4e ?
+		be	sun4m_init
+		 cmp	%l1, 's'		! Treat sun4s as sun4m
 		be	sun4m_init
 		 cmp	%l1, 'd'		! Let us see how the beast will die
 		be	sun4d_init
diff -u --recursive --new-file v2.3.5/linux/arch/sparc/kernel/pcic.c linux/arch/sparc/kernel/pcic.c
--- v2.3.5/linux/arch/sparc/kernel/pcic.c	Tue Mar 16 21:52:05 1999
+++ linux/arch/sparc/kernel/pcic.c	Wed Jun  9 14:44:25 1999
@@ -1,10 +1,13 @@
-/* $Id: pcic.c,v 1.5 1999/03/16 00:15:20 davem Exp $
+/* $Id: pcic.c,v 1.6 1999/06/03 15:02:18 davem Exp $
  * pcic.c: Sparc/PCI controller support
  *
  * Copyright (C) 1998 V. Roganov and G. Raiko
  *
  * Code is derived from Ultra/PCI PSYCHO controller support, see that
  * for author info.
+ *
+ * Support for diverse IIep based platforms by Pete Zaitcev.
+ * CP-1200 by Eric Brower.
  */
 
 #include <linux/config.h>
@@ -16,6 +19,7 @@
 
 #include <asm/ebus.h>
 #include <asm/sbus.h> /* for sanity check... */
+#include <asm/swift.h> /* for cache flushing. */
 
 #include <asm/io.h>
 
@@ -69,9 +73,99 @@
 
 #else
 
+unsigned int pcic_pin_to_irq(unsigned int pin, char *name);
+
+/*
+ * I studied different documents and many live PROMs both from 2.30
+ * family and 3.xx versions. I came to the amazing conclusion: there is
+ * absolutely no way to route interrupts in IIep systems relying on
+ * information which PROM presents. We must hardcode interrupt routing
+ * schematics. And this actually sucks.   -- zaitcev 1999/05/12
+ *
+ * To find irq for a device we determine which routing map
+ * is in effect or, in other words, on which machine we are running.
+ * We use PROM name for this although other techniques may be used
+ * in special cases (Gleb reports a PROMless IIep based system).
+ * Once we know the map we take device configuration address and
+ * find PCIC pin number where INT line goes. Then we may either program
+ * preferred irq into the PCIC or supply the preexisting irq to the device.
+ *
+ * XXX Entries for JE-1 are completely bogus. Gleb, Vladimir, please fill them.
+ */
+struct pcic_ca2irq {
+	unsigned char busno;		/* PCI bus number */
+	unsigned char devfn;		/* Configuration address */
+	unsigned char pin;		/* PCIC external interrupt pin */
+	unsigned char irq;		/* Preferred IRQ (mappable in PCIC) */
+	unsigned int force;		/* Enforce preferred IRQ */
+};
+
+struct pcic_sn2list {
+	char *sysname;
+	struct pcic_ca2irq *intmap;
+	int mapdim;
+};
+
+/*
+ * XXX JE-1 is a little known beast.
+ * One rumor has the map this way: pin 0 - parallel, audio;
+ * pin 1 - Ethernet; pin 2 - su; pin 3 - PS/2 kbd and mouse.
+ * All other comparable systems tie serial and keyboard together,
+ * so we do not code this rumor just yet.
+ */
+static struct pcic_ca2irq pcic_i_je1[] = {
+	{ 0, 0x01, 1,  6, 1 },		/* Happy Meal */
+};
+
+/* XXX JS-E entry is incomplete - PCI Slot 2 address (pin 7)? */
+static struct pcic_ca2irq pcic_i_jse[] = {
+	{ 0, 0x00, 0, 13, 0 },		/* Ebus - serial and keyboard */
+	{ 0, 0x01, 1,  6, 0 },		/* hme */
+	{ 0, 0x08, 2,  9, 0 },		/* VGA - we hope not used :) */
+	{ 0, 0x18, 6,  8, 0 },		/* PCI INTA# in Slot 1 */
+	{ 0, 0x38, 4,  9, 0 },		/* All ISA devices. Read 8259. */
+	{ 0, 0x80, 5, 11, 0 },		/* EIDE */
+	/* {0,0x88, 0,0,0} - unknown device... PMU? Probably no interrupt. */
+	{ 0, 0xA0, 4,  9, 0 },		/* USB */
+	/*
+	 * Some pins belong to non-PCI devices, we hardcode them in drivers.
+	 * sun4m timers - irq 10, 14
+	 * PC style RTC - pin 7, irq 4 ?
+	 * Smart card, Parallel - pin 4 shared with USB, ISA
+	 * audio - pin 3, irq 5 ?
+	 */
+};
+
+/* SPARCengine-6 was the original release name of CP1200.
+ * The documentation differs between the two versions
+ */
+static struct pcic_ca2irq pcic_i_se6[] = {
+	{ 0, 0x08, 0,  2, 0 },		/* SCSI	*/
+	{ 0, 0x01, 1,  6, 0 },		/* HME	*/
+	{ 0, 0x00, 3, 13, 0 },		/* EBus	*/
+};
+
+/*
+ * Several entries in this list may point to the same routing map
+ * as several PROMs may be installed on the same physical board.
+ */
+#define SN2L_INIT(name, map)	\
+  { name, map, sizeof(map)/sizeof(struct pcic_ca2irq) }
+
+static struct pcic_sn2list pcic_known_sysnames[] = {
+	SN2L_INIT("JE-1-name", pcic_i_je1),  /* XXX Gleb, put name here, pls */
+	SN2L_INIT("SUNW,JS-E", pcic_i_jse),	/* PROLL JavaStation-E */
+	SN2L_INIT("SUNW,SPARCengine-6", pcic_i_se6), /* SPARCengine-6/CP-1200 */
+	{ NULL, NULL, 0 }
+};
+
 static struct linux_pcic PCIC;
 static struct linux_pcic *pcic = NULL;
 
+unsigned int pcic_regs;
+volatile int pcic_speculative;
+volatile int pcic_trapped;
+
 static void pci_do_gettimeofday(struct timeval *tv);
 static void pci_do_settimeofday(struct timeval *tv);
 
@@ -149,6 +243,37 @@
 	pbm->prom_node = node;
 	prom_getstring(node, "name", namebuf, sizeof(namebuf));
 	strcpy(pbm->prom_name, namebuf);
+
+	{
+		extern volatile int t_nmi[1];
+		extern int pcic_nmi_trap_patch[1];
+
+		t_nmi[0] = pcic_nmi_trap_patch[0];
+		t_nmi[1] = pcic_nmi_trap_patch[1];
+		t_nmi[2] = pcic_nmi_trap_patch[2];
+		t_nmi[3] = pcic_nmi_trap_patch[3];
+		swift_flush_dcache();
+		pcic_regs = pcic->pcic_regs;
+	}
+
+	prom_getstring(prom_root_node, "name", namebuf, sizeof(namebuf));
+	{
+		struct pcic_sn2list *p;
+
+		for (p = pcic_known_sysnames; p->sysname != NULL; p++) {
+			if (strcmp(namebuf, p->sysname) == 0)
+				break;
+		}
+		pcic->pcic_imap = p->intmap;
+		pcic->pcic_imdim = p->mapdim;
+	}
+	if (pcic->pcic_imap == NULL) {
+		/*
+		 * We do not panic here for the sake of embedded systems.
+		 */
+		printk("PCIC: System %s is unknown, cannot route interrupts\n",
+		    namebuf);
+	}
 }
 
 __initfunc(void pcibios_init(void))
@@ -166,20 +291,15 @@
 	       pcic->pcic_regs, pcic->pcic_io);
 
 	/*
-	 * FIXME:
 	 *      Switch off IOTLB translation.
-	 *      It'll be great to use IOMMU to handle HME's rings
-	 *      but we couldn't. Thus, we have to flush CPU cache
-	 *      in HME.
 	 */
 	writeb(PCI_DVMA_CONTROL_IOTLB_DISABLE, 
 	       pcic->pcic_regs+PCI_DVMA_CONTROL);
 
 	/*
-	 * FIXME:
 	 *      Increase mapped size for PCI memory space (DMA access).
 	 *      Should be done in that order (size first, address second).
-	 *      Why we couldn't set up 4GB and forget about it ?
+	 *      Why we couldn't set up 4GB and forget about it? XXX
 	 */
 	writel(0xF0000000UL, pcic->pcic_regs+PCI_SIZE_0);
 	writel(0+PCI_BASE_ADDRESS_SPACE_MEMORY, 
@@ -204,7 +324,7 @@
 		if(err != 0 && err != -1) {
 			unsigned long devfn = (regs[0].which_io >> 8) & 0xff;
 			if(devfn == pdev->devfn)
-				return node; /* Match */
+				return node;
 		}
 		node = prom_getsibling(node);
 	}
@@ -216,9 +336,9 @@
 	return kmalloc(sizeof(struct pcidev_cookie), GFP_ATOMIC);
 }
 
-
-static void pcic_map_pci_device (struct pci_dev *dev) {
-	int node, pcinode;
+static void pcic_map_pci_device (struct pci_dev *dev, int node) {
+	struct linux_prom_pci_assigned_addresses addrs[6];
+	int addrlen;
 	int i, j;
 
 	/* Is any valid address present ? */
@@ -227,74 +347,132 @@
 		if (dev->base_address[j]) i++;
 	if (!i) return; /* nothing to do */
 
+	if (node == 0 || node == -1) {
+		printk("PCIC: no prom node for device ID (%x,%x)\n",
+		    dev->device, dev->vendor);
+		return;
+	}
+
 	/*
 	 * find related address and get it's window length
 	 */
-	pcinode = prom_getchild(prom_root_node);
-	pcinode = prom_searchsiblings(pcinode, "pci");
-	if (!pcinode)
-		panic("PCIC: failed to locate 'pci' node");
-
-
-	for (node = prom_getchild(pcinode); node;
-	     node = prom_getsibling(node)) {
-		struct linux_prom_pci_assigned_addresses addrs[6];
-		int addrlen = prom_getproperty(node,"assigned-addresses",
+	addrlen = prom_getproperty(node,"assigned-addresses",
 					       (char*)addrs, sizeof(addrs));
-		if (addrlen == -1)
-			continue;
+	if (addrlen == -1) {
+		printk("PCIC: no \"assigned-addresses\" for device (%x,%x)\n",
+		    dev->device, dev->vendor);
+		return;
+	}
 
-		addrlen /= sizeof(struct linux_prom_pci_assigned_addresses);
-		for (i = 0; i < addrlen; i++ )
-		    for (j = 0; j < 6; j++) {
-			if (!dev->base_address[j] || !addrs[i].phys_lo)
-				continue;
-			if (addrs[i].phys_lo == dev->base_address[j]) {
-			    unsigned long address = dev->base_address[j];
-			    int length  = addrs[i].size_lo;
-			    char namebuf[128] = { 0, };
-			    unsigned long mapaddr, addrflags;
-	    
-			    prom_getstring(node, "name",
-					   namebuf,  sizeof(namebuf));
-
-			    /* FIXME:
-			     *      failure in allocation too large space
-			     */
-			    if (length > 0x200000) {
+	addrlen /= sizeof(struct linux_prom_pci_assigned_addresses);
+	for (i = 0; i < addrlen; i++ )
+	    for (j = 0; j < 6; j++) {
+		if (!dev->base_address[j] || !addrs[i].phys_lo)
+			continue;
+		if (addrs[i].phys_lo == dev->base_address[j]) {
+			unsigned long address = dev->base_address[j];
+			int length  = addrs[i].size_lo;
+			char namebuf[128] = { 0, };
+			unsigned long mapaddr, addrflags;
+
+			prom_getstring(node, "name", namebuf, sizeof(namebuf));
+
+			/*
+			 *      failure in allocation too large space
+			 */
+			if (length > 0x200000) {
 				length = 0x200000;
 				prom_printf("PCIC: map window for device '%s' "
 					    "reduced to 2MB !\n", namebuf);
-			    }
+			}
 
-			    /*
-			     *  Be careful with MEM/IO address flags
-			     */
-			    if ((address & PCI_BASE_ADDRESS_SPACE) ==
+			/*
+			 *  Be careful with MEM/IO address flags
+			 */
+			if ((address & PCI_BASE_ADDRESS_SPACE) ==
 				 PCI_BASE_ADDRESS_SPACE_IO) {
 				mapaddr = address & PCI_BASE_ADDRESS_IO_MASK;
-			    } else {
+			} else {
 				mapaddr = address & PCI_BASE_ADDRESS_MEM_MASK;
-			    }
-			    addrflags = address ^ mapaddr;
+			}
+			addrflags = address ^ mapaddr;
 
-			    dev->base_address[j] =
+			dev->base_address[j] =
 				(unsigned long)sparc_alloc_io(address, 0, 
 							      length,
 							      namebuf, 0, 0);
-			    if ( dev->base_address[j] == 0 )
+			if ( dev->base_address[j] == 0 )
 				panic("PCIC: failed make mapping for "
 				      "pci device '%s' with address %lx\n",
 				       namebuf, address);
 
-			    dev->base_address[j] ^= addrflags;
-			    return;
-			}
+			dev->base_address[j] ^= addrflags;
+			return;
 		}
+	    }
+
+	printk("PCIC: unable to match addresses for device (%x,%x)\n",
+	    dev->device, dev->vendor);
+}
+
+static void pcic_fill_irq(struct pci_dev *dev, int node) {
+	struct pcic_ca2irq *p;
+	int i, ivec;
+	char namebuf[64];  /* P3 remove */
+
+	if (node == -1) {
+		strcpy(namebuf, "???");
+	} else {
+		prom_getstring(node, "name", namebuf, sizeof(namebuf)); /* P3 remove */
 	}
 
-	panic("PCIC: unable to locate prom node for pci device (%x,%x) \n",
-	      dev->device, dev->vendor);
+	if ((p = pcic->pcic_imap) == 0) {
+		dev->irq = 0;
+		return;
+	}
+	for (i = 0; i < pcic->pcic_imdim; i++) {
+		if (p->busno == dev->bus->number && p->devfn == dev->devfn)
+			break;
+		p++;
+	}
+	if (i >= pcic->pcic_imdim) {
+		printk("PCIC: device %s devfn %02x:%02x not found in %d\n",
+		    namebuf, dev->bus->number, dev->devfn, pcic->pcic_imdim);
+		dev->irq = 0;
+		return;
+	}
+
+	i = p->pin;
+	if (i >= 0 && i < 4) {
+		ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_LO);
+		dev->irq = ivec >> (i << 2) & 0xF;
+	} else if (i >= 4 && i < 8) {
+		ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_HI);
+		dev->irq = ivec >> ((i-4) << 2) & 0xF;
+	} else {					/* Corrupted map */
+		printk("PCIC: BAD PIN %d\n", i); for (;;) {}
+	}
+/* P3 remove later */ printk("PCIC: device %s pin %d ivec 0x%x irq %x\n", namebuf, i, ivec, dev->irq);
+
+	/*
+	 * dev->irq=0 means PROM did not bothered to program the upper
+	 * half of PCIC. This happens on JS-E with PROM 3.11, for instance.
+	 */
+	if (dev->irq == 0 || p->force) {
+		if (p->irq == 0 || p->irq >= 15) {	/* Corrupted map */
+			printk("PCIC: BAD IRQ %d\n", p->irq); for (;;) {}
+		}
+		printk("PCIC: setting irq %x for device (%x,%x)\n",
+		    p->irq, dev->device, dev->vendor);
+		dev->irq = p->irq;
+
+		ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_HI);
+		ivec &= ~(0xF << ((p->pin - 4) << 2));
+		ivec |= p->irq << ((p->pin - 4) << 2);
+		writew(ivec, pcic->pcic_regs+PCI_INT_SELECT_HI);
+	}
+
+	return;
 }
 
 /*
@@ -317,9 +495,10 @@
 		writeb((pcic->pcic_io_phys>>24) & PCI_SIBAR_ADDRESS_MASK,
 		       pcic->pcic_regs+PCI_SIBAR);
 		writeb(PCI_ISIZE_16M, pcic->pcic_regs+PCI_ISIZE);
+
 	}
 	if(paddr < pcic->pcic_mapped_io ||
-	   paddr > pcic->pcic_mapped_io + PCI_SPACE_SIZE)
+	   paddr >= pcic->pcic_mapped_io + 0x10000)
 		return 0;
 	offset = paddr - pcic->pcic_mapped_io;
 	*addr = pcic->pcic_io_phys + offset;
@@ -334,6 +513,9 @@
   struct pci_dev *dev;
   int i, has_io, has_mem;
   unsigned short cmd;
+	struct linux_pbm_info* pbm = &pcic->pbm;
+	int node;
+	struct pcidev_cookie *pcp;
 
 	if(pcic == NULL) {
 		prom_printf("PCI: Error, PCIC not found.\n");
@@ -359,47 +541,61 @@
 		}
 		pci_read_config_word(dev, PCI_COMMAND, &cmd);
 		if (has_io && !(cmd & PCI_COMMAND_IO)) {
-			printk("PCI: Enabling I/O for device %02x:%02x\n",
+			printk("PCIC: Enabling I/O for device %02x:%02x\n",
 				dev->bus->number, dev->devfn);
 			cmd |= PCI_COMMAND_IO;
 			pci_write_config_word(dev, PCI_COMMAND, cmd);
 		}
 		if (has_mem && !(cmd & PCI_COMMAND_MEMORY)) {
-			printk("PCI: Enabling memory for device %02x:%02x\n",
+			printk("PCIC: Enabling memory for device %02x:%02x\n",
 				dev->bus->number, dev->devfn);
 			cmd |= PCI_COMMAND_MEMORY;
 			pci_write_config_word(dev, PCI_COMMAND, cmd);
 		}    
 
+		node = pdev_to_pnode(pbm, dev);
+		if(node == 0)
+			node = -1;
+
 		/* cookies */
-		{
-			struct pcidev_cookie *pcp;
-			struct linux_pbm_info* pbm = &pcic->pbm;
-			int node = pdev_to_pnode(pbm, dev);
-
-			if(node == 0)
-				node = -1;
-			pcp = pci_devcookie_alloc();
-			pcp->pbm = pbm;
-			pcp->prom_node = node;
-			dev->sysdata = pcp;
-		}
+		pcp = pci_devcookie_alloc();
+		pcp->pbm = pbm;
+		pcp->prom_node = node;
+		dev->sysdata = pcp;
 
 		/* memory mapping */
-		if (!(dev->vendor == PCI_VENDOR_ID_SUN &&
-		      dev->device == PCI_DEVICE_ID_SUN_EBUS)) {
-			pcic_map_pci_device(dev);
-		}
+		if ((dev->class>>16) != PCI_BASE_CLASS_BRIDGE)
+			pcic_map_pci_device(dev, node);
 
-		/* irq */
-#define SETIRQ(vend,devid,irqn) \
-	if (dev->vendor==vend && dev->device==devid) dev->irq = irqn;
-
-		SETIRQ(PCI_VENDOR_ID_SUN,PCI_DEVICE_ID_SUN_HAPPYMEAL,3);
+		pcic_fill_irq(dev, node);
 	}
+
 	ebus_init();
 }
 
+/*
+ * pcic_pin_to_irq() is exported to ebus.c.
+ */
+unsigned int
+pcic_pin_to_irq(unsigned int pin, char *name)
+{
+	unsigned int irq;
+	unsigned int ivec;
+
+	if (pin < 4) {
+		ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_LO);
+		irq = ivec >> (pin << 2) & 0xF;
+	} else if (pin < 8) {
+		ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_HI);
+		irq = ivec >> ((pin-4) << 2) & 0xF;
+	} else {					/* Corrupted map */
+		printk("PCIC: BAD PIN %d FOR %s\n", pin, name);
+		for (;;) {}	/* XXX Cannot panic properly in case of PROLL */
+	}
+/* P3 remove later */ printk("PCIC: dev %s pin %d ivec 0x%x irq %x\n", name, pin, ivec, irq);
+	return irq;
+}
+
 /* Makes compiler happy */
 static volatile int pcic_timer_dummy;
 
@@ -539,26 +735,38 @@
 			       unsigned char where, unsigned int *value)
 {
 	unsigned long flags;
-	if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
-	if (bus != 0 || 
-	    (device_fn != 0 && device_fn != 1 && device_fn != 0x80)) {
-		*value = 0xffffffff;
-		return PCIBIOS_SUCCESSFUL;
-	}
 
-	/* FIXME: IGA haven't got high config memory addresses !!! */
-	if (device_fn == 0x80 && where > PCI_INTERRUPT_LINE) {
-		*value = 0xffffffff;
-		return PCIBIOS_SUCCESSFUL;
-	}
+	if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
 
 	save_and_cli(flags);
+#if 0
+	pcic_speculative = 1;
+	pcic_trapped = 0;
+#endif
 	writel(CONFIG_CMD(bus,device_fn,where), pcic->pcic_config_space_addr);
+#if 0
+	nop();
+	if (pcic_trapped) {
+		restore_flags(flags);
+		*value = ~0;
+		return PCIBIOS_SUCCESSFUL;
+	}
+#endif
+	pcic_speculative = 2;
+	pcic_trapped = 0;
 	*value = readl(pcic->pcic_config_space_data + (where&4));
+	nop();
+	if (pcic_trapped) {
+		pcic_speculative = 0;
+		restore_flags(flags);
+		*value = ~0;
+		return PCIBIOS_SUCCESSFUL;
+	}
+	pcic_speculative = 0;
 	restore_flags(flags);
 	return PCIBIOS_SUCCESSFUL;
 }
-    
+
 int pcibios_write_config_byte (unsigned char bus, unsigned char devfn,
 			       unsigned char where, unsigned char value)
 {
@@ -586,8 +794,8 @@
 				unsigned char where, unsigned int value)
 {
 	unsigned long flags;
-	if ((where&3) || bus != 0 || (devfn != 0 && devfn != 1 && devfn != 0x80))
-		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
 
 	save_and_cli(flags);
 	writel(CONFIG_CMD(bus,devfn,where),pcic->pcic_config_space_addr);
@@ -599,6 +807,29 @@
 __initfunc(char *pcibios_setup(char *str))
 {
 	return str;
+}
+
+/*
+ * NMI
+ */
+void pcic_nmi(unsigned int pend, struct pt_regs *regs)
+{
+
+	pend = flip_dword(pend);
+
+	if (!pcic_speculative || (pend & PCI_SYS_INT_PENDING_PIO) == 0) {
+		/*
+		 * XXX On CP-1200 PCI #SERR may happen, we do not know
+		 * what to do about it yet.
+		 */
+		printk("Aiee, NMI pend 0x%x pc 0x%x spec %d, hanging\n",
+		    pend, (int)regs->pc, pcic_speculative);
+		for (;;) { }
+	}
+	pcic_speculative = 0;
+	pcic_trapped = 1;
+	regs->pc = regs->npc;
+	regs->npc += 4;
 }
 
 /*
diff -u --recursive --new-file v2.3.5/linux/arch/sparc/kernel/ptrace.c linux/arch/sparc/kernel/ptrace.c
--- v2.3.5/linux/arch/sparc/kernel/ptrace.c	Mon May 31 22:28:04 1999
+++ linux/arch/sparc/kernel/ptrace.c	Mon Jun  7 11:15:33 1999
@@ -134,26 +134,6 @@
 	flush_tlb_page(vma, addr);
 }
 
-static struct vm_area_struct * find_extend_vma(struct task_struct * tsk,
-					       unsigned long addr)
-{
-	struct vm_area_struct * vma;
-
-	addr &= PAGE_MASK;
-	vma = find_vma(tsk->mm,addr);
-	if (!vma)
-		return NULL;
-	if (vma->vm_start <= addr)
-		return vma;
-	if (!(vma->vm_flags & VM_GROWSDOWN))
-		return NULL;
-	if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
-		return NULL;
-	vma->vm_offset -= vma->vm_start - addr;
-	vma->vm_start = addr;
-	return vma;
-}
-
 /*
  * This routine checks the page boundaries, and that the offset is
  * within the task area. It then calls get_long() to read a long.
diff -u --recursive --new-file v2.3.5/linux/arch/sparc/kernel/setup.c linux/arch/sparc/kernel/setup.c
--- v2.3.5/linux/arch/sparc/kernel/setup.c	Mon May 31 22:28:04 1999
+++ linux/arch/sparc/kernel/setup.c	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.106 1999/05/28 16:03:18 anton Exp $
+/*  $Id: setup.c,v 1.107 1999/06/03 15:02:20 davem Exp $
  *  linux/arch/sparc/kernel/setup.c
  *
  *  Copyright (C) 1995  David S. Miller (davem@caip.rutgers.edu)
@@ -313,6 +313,7 @@
 	if(!strcmp(&cputypval,"sun4 ")) { sparc_cpu_model=sun4; }
 	if(!strcmp(&cputypval,"sun4c")) { sparc_cpu_model=sun4c; }
 	if(!strcmp(&cputypval,"sun4m")) { sparc_cpu_model=sun4m; }
+	if(!strcmp(&cputypval,"sun4s")) { sparc_cpu_model=sun4m; }  /* CP-1200 with PROM 2.30 -E */
 	if(!strcmp(&cputypval,"sun4d")) { sparc_cpu_model=sun4d; }
 	if(!strcmp(&cputypval,"sun4e")) { sparc_cpu_model=sun4e; }
 	if(!strcmp(&cputypval,"sun4u")) { sparc_cpu_model=sun4u; }
diff -u --recursive --new-file v2.3.5/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c
--- v2.3.5/linux/arch/sparc/kernel/sys_sunos.c	Mon May 31 22:28:04 1999
+++ linux/arch/sparc/kernel/sys_sunos.c	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.97 1999/05/24 19:40:39 davem Exp $
+/* $Id: sys_sunos.c,v 1.98 1999/06/09 08:23:39 davem Exp $
  * sys_sunos.c: SunOS specific syscall compatibility support.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -150,7 +150,6 @@
 	unsigned long newbrk, oldbrk;
 
 	down(&current->mm->mmap_sem);
-	lock_kernel();
 	if(ARCH_SUN4C_SUN4) {
 		if(brk >= 0x20000000 && brk < 0xe0000000) {
 			goto out;
@@ -210,12 +209,9 @@
 	 * Ok, we have probably got enough memory - let it rip.
 	 */
 	current->mm->brk = brk;
-	do_mmap(NULL, oldbrk, newbrk-oldbrk,
-		PROT_READ|PROT_WRITE|PROT_EXEC,
-		MAP_FIXED|MAP_PRIVATE, 0);
+	do_brk(oldbrk, newbrk-oldbrk)
 	retval = 0;
 out:
-	unlock_kernel();
 	up(&current->mm->mmap_sem);
 	return retval;
 }
diff -u --recursive --new-file v2.3.5/linux/arch/sparc64/Makefile linux/arch/sparc64/Makefile
--- v2.3.5/linux/arch/sparc64/Makefile	Wed Mar 10 16:53:36 1999
+++ linux/arch/sparc64/Makefile	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.35 1999/01/02 16:45:50 davem Exp $
+# $Id: Makefile,v 1.37 1999/06/04 13:29:10 jj Exp $
 # sparc64/Makefile
 #
 # Makefile for the architecture dependent flags and dependencies on the
@@ -15,7 +15,7 @@
 CC		:= sparc64-linux-gcc -D__KERNEL__ -I$(TOPDIR)/include
 
 CC_HAS_ARGS := $(shell if echo "$(CC)" | grep '\(__KERNEL__\| 	\)' > /dev/null; then echo y; else echo n; fi)
-IS_EGCS := $(shell if $(CC) --version 2>&1 | grep 'egcs' > /dev/null; then echo y; else echo n; fi)
+IS_EGCS := $(shell if $(CC) -c -m64 -mcmodel=medlow -o _tmp.o arch/sparc64/math-emu/fnegq.c >/dev/null 2>&1; then echo y; else echo n; fi; rm -f _tmp.o)
 NEW_GAS := $(shell if $(LD) --version 2>&1 | grep 'elf64_sparc' > /dev/null; then echo y; else echo n; fi)
 
 ifneq ($(CC_HAS_ARGS),y)
diff -u --recursive --new-file v2.3.5/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- v2.3.5/linux/arch/sparc64/defconfig	Mon May 31 22:28:04 1999
+++ linux/arch/sparc64/defconfig	Wed Jun  9 14:44:25 1999
@@ -69,6 +69,8 @@
 # CONFIG_SUN_BPP is not set
 # CONFIG_SUN_VIDEOPIX is not set
 CONFIG_SUN_AURORA=m
+CONFIG_APM_RTC_IS_GMT=y
+# CONFIG_RTC is not set
 
 #
 # Linux/SPARC audio subsystem (EXPERIMENTAL)
diff -u --recursive --new-file v2.3.5/linux/arch/sparc64/kernel/binfmt_aout32.c linux/arch/sparc64/kernel/binfmt_aout32.c
--- v2.3.5/linux/arch/sparc64/kernel/binfmt_aout32.c	Mon Mar 22 10:06:36 1999
+++ linux/arch/sparc64/kernel/binfmt_aout32.c	Wed Jun  9 14:44:25 1999
@@ -48,9 +48,7 @@
 	end = PAGE_ALIGN(end);
 	if (end <= start)
 		return;
-	do_mmap(NULL, start, end - start,
-		PROT_READ | PROT_WRITE | PROT_EXEC,
-		MAP_FIXED | MAP_PRIVATE, 0);
+	do_brk(start, end - start);
 }
 
 /*
@@ -284,24 +282,18 @@
  	current->flags &= ~PF_FORKNOEXEC;
 	if (N_MAGIC(ex) == NMAGIC) {
 		/* Fuck me plenty... */
-		error = do_mmap(NULL, N_TXTADDR(ex), ex.a_text,
-				PROT_READ|PROT_WRITE|PROT_EXEC,
-				MAP_FIXED|MAP_PRIVATE, 0);
+		error = do_brk(N_TXTADDR(ex), ex.a_text);
 		read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex),
 			  ex.a_text, 0);
-		error = do_mmap(NULL, N_DATADDR(ex), ex.a_data,
-				PROT_READ|PROT_WRITE|PROT_EXEC,
-				MAP_FIXED|MAP_PRIVATE, 0);
+		error = do_brk(N_DATADDR(ex), ex.a_data);
 		read_exec(bprm->dentry, fd_offset + ex.a_text, (char *) N_DATADDR(ex),
 			  ex.a_data, 0);
 		goto beyond_if;
 	}
 
 	if (N_MAGIC(ex) == OMAGIC) {
-		do_mmap(NULL, N_TXTADDR(ex) & PAGE_MASK,
-			ex.a_text+ex.a_data + PAGE_SIZE - 1,
-			PROT_READ|PROT_WRITE|PROT_EXEC,
-			MAP_FIXED|MAP_PRIVATE, 0);
+		do_brk(N_TXTADDR(ex) & PAGE_MASK,
+			ex.a_text+ex.a_data + PAGE_SIZE - 1);
 		read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex),
 			  ex.a_text+ex.a_data, 0);
 	} else {
@@ -316,9 +308,7 @@
 
 		if (!file->f_op || !file->f_op->mmap) {
 			sys_close(fd);
-			do_mmap(NULL, 0, ex.a_text+ex.a_data,
-				PROT_READ|PROT_WRITE|PROT_EXEC,
-				MAP_FIXED|MAP_PRIVATE, 0);
+			do_brk(0, ex.a_text+ex.a_data);
 			read_exec(bprm->dentry, fd_offset,
 				  (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0);
 			goto beyond_if;
@@ -359,11 +349,16 @@
 
 	set_brk(current->mm->start_brk, current->mm->brk);
 
-	p = setup_arg_pages(p, bprm);
+	retval = setup_arg_pages(bprm);
+	if (retval < 0) { 
+		/* Someone check-me: is this error path enough? */ 
+		send_sig(SIGKILL, current, 0); 
+		return retval;
+	}
 
-	p = (unsigned long) create_aout32_tables((char *)p, bprm);
-	current->mm->start_stack = p;
-	start_thread32(regs, ex.a_entry, p);
+	current->mm->start_stack =
+		(unsigned long) create_aout32_tables((char *)bprm->p, bprm);
+	start_thread32(regs, ex.a_entry, current->mm->start_stack);
 	if (current->flags & PF_PTRACED)
 		send_sig(SIGTRAP, current, 0);
 	return 0;
@@ -442,9 +437,7 @@
 	len = PAGE_ALIGN(ex.a_text + ex.a_data);
 	bss = ex.a_text + ex.a_data + ex.a_bss;
 	if (bss > len) {
-		error = do_mmap(NULL, start_addr + len, bss - len,
-				PROT_READ | PROT_WRITE | PROT_EXEC,
-				MAP_PRIVATE | MAP_FIXED, 0);
+		error = do_brk(start_addr + len, bss - len);
 		retval = error;
 		if (error != start_addr + len)
 			goto out_putf;
diff -u --recursive --new-file v2.3.5/linux/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c
--- v2.3.5/linux/arch/sparc64/kernel/ioctl32.c	Tue May 11 08:24:31 1999
+++ linux/arch/sparc64/kernel/ioctl32.c	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.62 1999/05/01 09:17:44 davem Exp $
+/* $Id: ioctl32.c,v 1.63 1999/06/09 04:56:14 davem Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997  Jakub Jelinek  (jj@sunsite.mff.cuni.cz)
@@ -37,6 +37,7 @@
 #include <linux/fb.h>
 #include <linux/ext2_fs.h>
 #include <linux/videodev.h>
+#include <linux/netdevice.h>
 
 #include <scsi/scsi.h>
 /* Ugly hack. */
@@ -417,6 +418,23 @@
         __kernel_caddr_t32  ifcbuf;
 };
 
+static int dev_ifname32(unsigned int fd, unsigned long arg)
+{
+	struct device *dev;
+	struct ifreq32 ifr32;
+	int err;
+
+	if (copy_from_user(&ifr32, (struct ifreq32 *)arg, sizeof(struct ifreq32)))
+		return -EFAULT;
+
+	dev = dev_get_by_index(ifr32.ifr_ifindex);
+	if (!dev)
+		return -ENODEV;
+
+	err = copy_to_user((struct ifreq32 *)arg, &ifr32, sizeof(struct ifreq32));
+	return (err ? -EFAULT : 0);
+}
+
 static inline int dev_ifconf(unsigned int fd, unsigned long arg)
 {
 	struct ifconf32 ifc32;
@@ -1687,6 +1705,10 @@
 		goto out;
 	}
 	switch (cmd) {
+	case SIOCGIFNAME:
+		error = dev_ifname32(fd, arg);
+		goto out;
+
 	case SIOCGIFCONF:
 		error = dev_ifconf(fd, arg);
 		goto out;
diff -u --recursive --new-file v2.3.5/linux/arch/sparc64/kernel/ptrace.c linux/arch/sparc64/kernel/ptrace.c
--- v2.3.5/linux/arch/sparc64/kernel/ptrace.c	Mon May 31 22:28:04 1999
+++ linux/arch/sparc64/kernel/ptrace.c	Mon Jun  7 11:15:33 1999
@@ -204,26 +204,6 @@
 	flush_tlb_page(vma, addr);
 }
 
-static struct vm_area_struct * find_extend_vma(struct task_struct * tsk,
-					       unsigned long addr)
-{
-	struct vm_area_struct * vma;
-
-	addr &= PAGE_MASK;
-	vma = find_vma(tsk->mm,addr);
-	if (!vma)
-		return NULL;
-	if (vma->vm_start <= addr)
-		return vma;
-	if (!(vma->vm_flags & VM_GROWSDOWN))
-		return NULL;
-	if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
-		return NULL;
-	vma->vm_offset -= vma->vm_start - addr;
-	vma->vm_start = addr;
-	return vma;
-}
-
 /*
  * This routine checks the page boundaries, and that the offset is
  * within the task area. It then calls get_long() to read a long.
diff -u --recursive --new-file v2.3.5/linux/arch/sparc64/kernel/signal.c linux/arch/sparc64/kernel/signal.c
--- v2.3.5/linux/arch/sparc64/kernel/signal.c	Tue Oct 27 09:52:20 1998
+++ linux/arch/sparc64/kernel/signal.c	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-/*  $Id: signal.c,v 1.38 1998/10/16 03:19:04 davem Exp $
+/*  $Id: signal.c,v 1.40 1999/06/02 19:19:52 jj Exp $
  *  arch/sparc64/kernel/signal.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
@@ -491,7 +491,7 @@
 /* Checks if the fp is valid */
 static int invalid_frame_pointer(void *fp, int fplen)
 {
-	if ((((unsigned long) fp) & 7) || ((unsigned long)fp) > 0x80000000000ULL - fplen)
+	if (((unsigned long) fp) & 7)
 		return 1;
 	return 0;
 }
@@ -554,8 +554,10 @@
 		goto sigill;
 
 	if (current->tss.w_saved != 0) {
+#ifdef DEBUG_SIGNALS
 		printk ("%s[%d]: Invalid user stack frame for "
 			"signal delivery.\n", current->comm, current->pid);
+#endif
 		goto sigill;
 	}
 
@@ -590,35 +592,7 @@
 	regs->tnpc = (regs->tpc + 4);
 
 	/* 4. return to kernel instructions */
-	if (ka->ka_restorer)
-		regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
-	else {
-		/* Flush instruction space. */
-		unsigned long address = ((unsigned long)&(sf->insns[0]));
-		pgd_t *pgdp = pgd_offset(current->mm, address);
-		pmd_t *pmdp = pmd_offset(pgdp, address);
-		pte_t *ptep = pte_offset(pmdp, address);
-
-		regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
-		
-		/* mov __NR_sigreturn, %g1 */
-		err |= __put_user(0x821020d8, &sf->insns[0]);
-
-		/* t 0x6d */
-		err |= __put_user(0x91d0206d, &sf->insns[1]);
-		if (err)
-			goto sigsegv;
-
-		if(pte_present(*ptep)) {
-			unsigned long page = pte_page(*ptep);
-
-			__asm__ __volatile__("
-			membar	#StoreStore
-			flush	%0 + %1"
-			: : "r" (page), "r" (address & (PAGE_SIZE - 1))
-			: "memory");
-		}
-	}
+	regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
 	return;
 
 sigill:
@@ -650,8 +624,10 @@
 		goto sigill;
 
 	if (current->tss.w_saved != 0) {
+#ifdef DEBUG_SIGNALS
 		printk ("%s[%d]: Invalid user stack frame for "
 			"signal delivery.\n", current->comm, current->pid);
+#endif
 		goto sigill;
 	}
 
@@ -690,35 +666,7 @@
 	regs->tnpc = (regs->tpc + 4);
 
 	/* 4. return to kernel instructions */
-	if (ka->ka_restorer)
-		regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
-	else {
-		/* Flush instruction space. */
-		unsigned long address = ((unsigned long)&(sf->insns[0]));
-		pgd_t *pgdp = pgd_offset(current->mm, address);
-		pmd_t *pmdp = pmd_offset(pgdp, address);
-		pte_t *ptep = pte_offset(pmdp, address);
-
-		regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2);
-		
-		/* mov __NR_rt_sigreturn, %g1 */
-		err |= __put_user(0x82102065, &sf->insns[0]);
-
-		/* t 0x6d */
-		err |= __put_user(0x91d0206d, &sf->insns[1]);
-		if (err)
-			goto sigsegv;
-
-		if(pte_present(*ptep)) {
-			unsigned long page = pte_page(*ptep);
-
-			__asm__ __volatile__("
-			membar	#StoreStore
-			flush	%0 + %1"
-			: : "r" (page), "r" (address & (PAGE_SIZE - 1))
-			: "memory");
-		}
-	}
+	regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer;
 	return;
 
 sigill:
diff -u --recursive --new-file v2.3.5/linux/arch/sparc64/kernel/sys_sparc.c linux/arch/sparc64/kernel/sys_sparc.c
--- v2.3.5/linux/arch/sparc64/kernel/sys_sparc.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/kernel/sys_sparc.c	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.26 1999/01/07 19:07:01 jj Exp $
+/* $Id: sys_sparc.c,v 1.27 1999/06/02 12:06:34 jj Exp $
  * linux/arch/sparc64/kernel/sys_sparc.c
  *
  * This file contains various random system calls that
@@ -325,39 +325,6 @@
 		return -EINVAL;
 	regs->tstate = (regs->tstate & ~TSTATE_MM) | (model << 14);
 	return 0;
-}
-
-asmlinkage int
-sys_sigaction(int sig, const struct old_sigaction *act,
-	      struct old_sigaction *oact)
-{
-	struct k_sigaction new_ka, old_ka;
-	int ret;
-
-	if (act) {
-		old_sigset_t mask;
-		if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
-		    __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
-		    __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
-			return -EFAULT;
-		__get_user(new_ka.sa.sa_flags, &act->sa_flags);
-		__get_user(mask, &act->sa_mask);
-		siginitset(&new_ka.sa.sa_mask, mask);
-		new_ka.ka_restorer = NULL;
-	}
-
-	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
-
-	if (!ret && oact) {
-		if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
-		    __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
-		    __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
-			return -EFAULT;
-		__put_user(old_ka.sa.sa_flags, &oact->sa_flags);
-		__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
-	}
-
-	return ret;
 }
 
 asmlinkage int
diff -u --recursive --new-file v2.3.5/linux/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c
--- v2.3.5/linux/arch/sparc64/kernel/sys_sparc32.c	Mon May 17 09:55:21 1999
+++ linux/arch/sparc64/kernel/sys_sparc32.c	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.108 1999/05/16 10:50:32 davem Exp $
+/* $Id: sys_sparc32.c,v 1.109 1999/06/03 07:11:31 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -2776,42 +2776,46 @@
  * memory to free pages in kernel mem. These are in a format ready
  * to be put directly into the top of new user memory.
  */
-static unsigned long
-copy_strings32(int argc,u32 * argv,unsigned long *page,
-	       unsigned long p)
+static int copy_strings32(int argc, u32 * argv, struct linux_binprm *bprm)
 {
-	u32 str;
-
-	if (!p) return 0;	/* bullet-proofing */
 	while (argc-- > 0) {
+		u32 str;
 		int len;
 		unsigned long pos;
 
-		get_user(str, argv+argc);
-		if (!str) panic("VFS: argc is wrong");
-		len = strlen_user((char *)A(str));	/* includes the '\0' */
-		if (p < len)	/* this shouldn't happen - 128kB */
-			return 0;
-		p -= len; pos = p;
+		if (get_user(str, argv + argc) ||
+		    !str ||
+		    !(len = strlen_user((char *)A(str))))
+			return -EFAULT;
+		if (bprm->p < len)
+			return -E2BIG;
+
+		bprm->p -= len;
+
+		pos = bprm->p;
 		while (len) {
 			char *pag;
 			int offset, bytes_to_copy;
 
 			offset = pos % PAGE_SIZE;
-			if (!(pag = (char *) page[pos/PAGE_SIZE]) &&
-			    !(pag = (char *) page[pos/PAGE_SIZE] =
+			if (!(pag = (char *) bprm->page[pos/PAGE_SIZE]) &&
+			    !(pag = (char *) bprm->page[pos/PAGE_SIZE] =
 			      (unsigned long *) get_free_page(GFP_USER)))
-				return 0;
+				return -ENOMEM;
+
 			bytes_to_copy = PAGE_SIZE - offset;
 			if (bytes_to_copy > len)
 				bytes_to_copy = len;
-			copy_from_user(pag + offset, (char *)A(str), bytes_to_copy);
+
+			if (copy_from_user(pag + offset, (char *)A(str), bytes_to_copy))
+				return -EFAULT;
+
 			pos += bytes_to_copy;
 			str += bytes_to_copy;
 			len -= bytes_to_copy;
 		}
 	}
-	return p;
+	return 0;
 }
 
 /*
@@ -2850,29 +2854,36 @@
 	}
 
 	retval = prepare_binprm(&bprm);
+	if (retval < 0)
+		goto out;
 	
-	if(retval>=0) {
-		bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p, 2);
-		bprm.exec = bprm.p;
-		bprm.p = copy_strings32(bprm.envc,envp,bprm.page,bprm.p);
-		bprm.p = copy_strings32(bprm.argc,argv,bprm.page,bprm.p);
-		if (!bprm.p)
-			retval = -E2BIG;
-	}
+	retval = copy_strings_kernel(1, &bprm.filename, &bprm);
+	if (retval < 0)
+		goto out;
+
+	bprm.exec = bprm.p;
+	retval = copy_strings32(bprm.envc, envp, &bprm);
+	if (retval < 0)
+		goto out;
 
-	if(retval>=0)
-		retval = search_binary_handler(&bprm,regs);
-	if(retval>=0)
+	retval = copy_strings32(bprm.argc, argv, &bprm);
+	if (retval < 0)
+		goto out;
+
+	retval = search_binary_handler(&bprm, regs);
+	if (retval >= 0)
 		/* execve success */
 		return retval;
 
+out:
 	/* Something went wrong, return the inode and free the argument pages*/
-	if(bprm.dentry)
+	if (bprm.dentry)
 		dput(bprm.dentry);
 
 	for (i=0 ; i<MAX_ARG_PAGES ; i++)
 		free_page(bprm.page[i]);
-	return(retval);
+
+	return retval;
 }
 
 /*
diff -u --recursive --new-file v2.3.5/linux/arch/sparc64/kernel/sys_sunos32.c linux/arch/sparc64/kernel/sys_sunos32.c
--- v2.3.5/linux/arch/sparc64/kernel/sys_sunos32.c	Mon May 31 22:28:04 1999
+++ linux/arch/sparc64/kernel/sys_sunos32.c	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos32.c,v 1.25 1999/05/24 19:40:44 davem Exp $
+/* $Id: sys_sunos32.c,v 1.26 1999/06/09 08:23:54 davem Exp $
  * sys_sunos32.c: SunOS binary compatability layer on sparc64.
  *
  * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -134,7 +134,6 @@
 	unsigned long newbrk, oldbrk, brk = (unsigned long) baddr;
 
 	down(&current->mm->mmap_sem);
-	lock_kernel();
 	if (brk < current->mm->end_code)
 		goto out;
 	newbrk = PAGE_ALIGN(brk);
@@ -175,12 +174,9 @@
 		goto out;
 	/* Ok, we have probably got enough memory - let it rip. */
 	current->mm->brk = brk;
-	do_mmap(NULL, oldbrk, newbrk-oldbrk,
-		PROT_READ|PROT_WRITE|PROT_EXEC,
-		MAP_FIXED|MAP_PRIVATE, 0);
+	do_brk(oldbrk, newbrk-oldbrk);
 	retval = 0;
 out:
-	unlock_kernel();
 	up(&current->mm->mmap_sem);
 	return retval;
 }
diff -u --recursive --new-file v2.3.5/linux/arch/sparc64/kernel/systbls.S linux/arch/sparc64/kernel/systbls.S
--- v2.3.5/linux/arch/sparc64/kernel/systbls.S	Thu Apr 22 19:24:51 1999
+++ linux/arch/sparc64/kernel/systbls.S	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.53 1999/04/07 17:14:11 davem Exp $
+/* $Id: systbls.S,v 1.54 1999/06/02 12:06:31 jj Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -115,7 +115,7 @@
 /*180*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_query_module
 	.word sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_newuname
 /*190*/	.word sys_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-	.word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys_sigaction, sys_sgetmask
+	.word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys_nis_syscall, sys_sgetmask
 /*200*/	.word sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, sys_nis_syscall
 	.word sys_nis_syscall, sys_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall
 /*210*/	.word sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo
diff -u --recursive --new-file v2.3.5/linux/arch/sparc64/kernel/traps.c linux/arch/sparc64/kernel/traps.c
--- v2.3.5/linux/arch/sparc64/kernel/traps.c	Mon May 31 22:28:04 1999
+++ linux/arch/sparc64/kernel/traps.c	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.59 1999/05/18 16:57:10 jj Exp $
+/* $Id: traps.c,v 1.60 1999/06/02 19:19:55 jj Exp $
  * arch/sparc64/kernel/traps.c
  *
  * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
@@ -282,11 +282,16 @@
 				   unsigned long sfsr, unsigned long sfar)
 {
 	lock_kernel();
+	if (regs->tstate & TSTATE_PRIV) {
 #if 1
-	printk("instruction_access_exception: Shit SFSR[%016lx] SFAR[%016lx], going.\n",
-	       sfsr, sfar);
+		printk("instruction_access_exception: Shit SFSR[%016lx] SFAR[%016lx], going.\n",
+		       sfsr, sfar);
 #endif
-	die_if_kernel("Iax", regs);
+		die_if_kernel("Iax", regs);
+	}
+	current->tss.sig_desc = SUBSIG_ILLINST;
+	current->tss.sig_address = regs->tpc;
+	force_sig(SIGILL, current);
 	unlock_kernel();
 }
 
diff -u --recursive --new-file v2.3.5/linux/arch/sparc64/math-emu/sfp-util.h linux/arch/sparc64/math-emu/sfp-util.h
--- v2.3.5/linux/arch/sparc64/math-emu/sfp-util.h	Mon May 31 22:28:05 1999
+++ linux/arch/sparc64/math-emu/sfp-util.h	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-/* $Id: sfp-util.h,v 1.1 1999/05/28 13:43:07 jj Exp $
+/* $Id: sfp-util.h,v 1.2 1999/06/07 18:24:15 jj Exp $
  * arch/sparc64/math-emu/sfp-util.h
  *
  * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz)
@@ -55,7 +55,7 @@
 		    srlx %7,32,%5
 		    mulx %3,%5,%3
 		    mulx %2,%5,%5
-		    sethi 0x80000000,%2
+		    sethi %%hi(0x80000000),%2
 		    addcc %4,%3,%4
 		    srlx %4,32,%4
 		    add %2,%2,%2
diff -u --recursive --new-file v2.3.5/linux/drivers/Makefile linux/drivers/Makefile
--- v2.3.5/linux/drivers/Makefile	Mon May 10 10:18:34 1999
+++ linux/drivers/Makefile	Wed Jun  2 14:40:22 1999
@@ -53,6 +53,15 @@
   endif
 endif 
 
+ifeq ($(CONFIG_I2O),y)
+SUB_DIRS += i2o
+MOD_SUB_DIRS += i2o
+else
+  ifeq ($(CONFIG_I2O),m)
+  MOD_SUB_DIRS += i2o
+  endif
+endif 
+
 # If CONFIG_SCSI is set, the core of SCSI support will be added to the kernel,
 # but some of the low-level things may also be modules.
 ifeq ($(CONFIG_SCSI),y)
diff -u --recursive --new-file v2.3.5/linux/drivers/block/cy82c693.c linux/drivers/block/cy82c693.c
--- v2.3.5/linux/drivers/block/cy82c693.c	Mon May 31 22:28:05 1999
+++ linux/drivers/block/cy82c693.c	Wed Jun  2 22:21:51 1999
@@ -423,7 +423,8 @@
 __initfunc(void ide_init_cy82c693(ide_hwif_t *hwif))
 {
 	hwif->chipset = ide_cy82c693;
-	hwif->dmaproc = &cy82c693_dmaproc;
+	if (hwif->dma_base)
+		hwif->dmaproc = &cy82c693_dmaproc;
 	hwif->tuneproc = &cy82c693_tune_drive;
 
 	init_cy82c693_chip(hwif->pci_dev);
diff -u --recursive --new-file v2.3.5/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v2.3.5/linux/drivers/block/floppy.c	Mon May 17 09:55:21 1999
+++ linux/drivers/block/floppy.c	Wed Jun  9 16:42:58 1999
@@ -1927,8 +1927,6 @@
 
 static void floppy_ready(void)
 {
-	unsigned long flags;
-	
 	CHECK_RESET;
 	if (start_motor(floppy_ready)) return;
 	if (fdc_dtr()) return;
@@ -1948,7 +1946,7 @@
 	if ((raw_cmd->flags & FD_RAW_READ) || 
 	    (raw_cmd->flags & FD_RAW_WRITE))
 	{
-		flags=claim_dma_lock();
+		unsigned long flags = claim_dma_lock();
 		fd_chose_dma_mode(raw_cmd->kernel_data,
 				  raw_cmd->length);
 		release_dma_lock(flags);
diff -u --recursive --new-file v2.3.5/linux/drivers/block/ide-pmac.c linux/drivers/block/ide-pmac.c
--- v2.3.5/linux/drivers/block/ide-pmac.c	Fri May 14 18:55:14 1999
+++ linux/drivers/block/ide-pmac.c	Mon Jun  7 12:12:22 1999
@@ -65,7 +65,6 @@
 				ide_ioreg_t ctrl_port,
 				int *irq)
 {
-	ide_ioreg_t reg = ide_ioreg_t data_port;
 	int i, r;
 
 	if (data_port == 0)
@@ -76,20 +75,15 @@
 	r = check_media_bay_by_base(data_port, MB_CD);
 	if (r == -EINVAL)
 		return;
-		
-	for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
-		hw->io_ports[i] = reg * 0x10;
-		reg += 1;
-	}
-	if (ctrl_port) {
-		hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
-	} else {
-		hw->io_ports[IDE_CONTROL_OFFSET] = hw->io_ports[IDE_DATA_OFFSET] + 0x160;
-	}
+	
+	for ( i = 0; i < 8 ; ++i )
+		hw->io_ports[i] = data_port + i * 0x10;
+	hw->io_ports[8] = data_port + 0x160;
+
 	if (irq != NULL) {
 		*irq = 0;
 		for (i = 0; i < MAX_HWIFS; ++i) {
-			if (base == pmac_ide_regbase[i]) {
+			if (data_port == pmac_ide_regbase[i]) {
 				*irq = pmac_ide_irq[i];
 				break;
 			}
@@ -156,7 +150,7 @@
 			       np->full_name);
 			continue;
 		}
-		
+
 		base = (unsigned long) ioremap(np->addrs[0].address, 0x200);
 		
 		/* XXX This is bogus. Should be fixed in the registry by checking
diff -u --recursive --new-file v2.3.5/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.3.5/linux/drivers/block/ll_rw_blk.c	Fri May 14 18:55:14 1999
+++ linux/drivers/block/ll_rw_blk.c	Wed Jun  2 22:21:51 1999
@@ -471,6 +471,8 @@
 	     case IDE3_MAJOR:
 	     case IDE4_MAJOR:
 	     case IDE5_MAJOR:
+	     case IDE6_MAJOR:
+	     case IDE7_MAJOR:
 	     case ACSI_MAJOR:
 	     case MFM_ACORN_MAJOR:
 		/*
@@ -497,6 +499,7 @@
 	     case SCSI_DISK6_MAJOR:
 	     case SCSI_DISK7_MAJOR:
 	     case SCSI_CDROM_MAJOR:
+	     case I2O_MAJOR:
 
 		do {
 			if (req->sem)
diff -u --recursive --new-file v2.3.5/linux/drivers/block/ns87415.c linux/drivers/block/ns87415.c
--- v2.3.5/linux/drivers/block/ns87415.c	Mon May 31 22:28:05 1999
+++ linux/drivers/block/ns87415.c	Wed Jun  2 22:21:51 1999
@@ -166,13 +166,15 @@
 #endif
 	}
 
-	outb(0x60, hwif->dma_base + 2);
+	if (hwif->dma_base)
+		outb(0x60, hwif->dma_base + 2);
 
 	if (!using_inta)
 		hwif->irq = hwif->channel ? 15 : 14;	/* legacy mode */
 	else if (!hwif->irq && hwif->mate && hwif->mate->irq)
 		hwif->irq = hwif->mate->irq;	/* share IRQ with mate */
 
-	hwif->dmaproc = &ns87415_dmaproc;
+	if (hwif->dma_base)
+		hwif->dmaproc = &ns87415_dmaproc;
 	hwif->selectproc = &ns87415_selectproc;
 }
diff -u --recursive --new-file v2.3.5/linux/drivers/block/piix.c linux/drivers/block/piix.c
--- v2.3.5/linux/drivers/block/piix.c	Fri May 14 18:55:14 1999
+++ linux/drivers/block/piix.c	Wed Jun  2 22:21:51 1999
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/piix.c	Version 0.22	March 29, 1999
+ * linux/drivers/block/piix.c	Version 0.23	May 29, 1999
  *
  *  Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer
  *  Copyright (C) 1998-1999 Andre Hedrick, Author and Maintainer
@@ -13,10 +13,10 @@
  *                 41
  *                 43
  *
- * | PIO 0       | c0 | 80 | 0 |
- * | PIO 2 | SW2 | d0 | 90 | 4 |
- * | PIO 3 | MW1 | e1 | a1 | 9 |
- * | PIO 4 | MW2 | e3 | a3 | b |
+ * | PIO 0       | c0 | 80 | 0 | 	piix_tune_drive(drive, 0);
+ * | PIO 2 | SW2 | d0 | 90 | 4 | 	piix_tune_drive(drive, 2);
+ * | PIO 3 | MW1 | e1 | a1 | 9 | 	piix_tune_drive(drive, 3);
+ * | PIO 4 | MW2 | e3 | a3 | b | 	piix_tune_drive(drive, 4);
  * 
  * sitre = word40 & 0x4000; primary
  * sitre = word42 & 0x4000; secondary
@@ -58,10 +58,40 @@
 
 #include "ide_modes.h"
 
-#define PIIX_DMA_PROC			0
-#define PIIX_DEBUG_SET_XFER		0
 #define PIIX_DEBUG_DRIVE_INFO		0
 
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/*
+ *
+ */
+static byte piix_dma_2_pio (byte xfer_rate) {
+	switch(xfer_rate) {
+		case XFER_UDMA_4:
+		case XFER_UDMA_3:
+		case XFER_UDMA_2:
+		case XFER_UDMA_1:
+		case XFER_UDMA_0:
+		case XFER_MW_DMA_2:
+		case XFER_PIO_4:
+			return 4;
+		case XFER_MW_DMA_1:
+		case XFER_PIO_3:
+			return 3;
+		case XFER_SW_DMA_2:
+		case XFER_PIO_2:
+			return 2;
+		case XFER_MW_DMA_0:
+		case XFER_SW_DMA_1:
+		case XFER_SW_DMA_0:
+		case XFER_PIO_1:
+		case XFER_PIO_0:
+		case XFER_PIO_SLOW:
+		default:
+			return 0;
+	}
+}
+
 /*
  *  Based on settings done by AMI BIOS
  *  (might be usefull if drive is not registered in CMOS for any reason).
@@ -70,19 +100,22 @@
 {
 	unsigned long flags;
 	u16 master_data;
-	byte slave_data, speed;
-	int err;
-	int is_slave = (&HWIF(drive)->drives[1] == drive);
-	int master_port = HWIF(drive)->index ? 0x42 : 0x40;
-	int slave_port = 0x44;
-			   /* ISP  RTC */
-	byte timings[][2] = { { 0, 0 },
-			      { 0, 0 },
-			      { 1, 0 },
-			      { 2, 1 },
-			      { 2, 3 }, };
-			      
+	byte slave_data;
+	int is_slave		= (&HWIF(drive)->drives[1] == drive);
+	int master_port		= HWIF(drive)->index ? 0x42 : 0x40;
+	int slave_port		= 0x44;
+				 /* ISP  RTC */
+	byte timings[][2]	= { { 0, 0 },
+				    { 0, 0 },
+				    { 1, 0 },
+				    { 2, 1 },
+				    { 2, 3 }, };
+
+#if 1
+	pio = ide_get_best_pio_mode(drive, pio, 5, NULL);
+#else
 	pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+#endif
 	pci_read_config_word(HWIF(drive)->pci_dev, master_port, &master_data);
 	if (is_slave) {
 		master_data = master_data | 0x4000;
@@ -107,28 +140,15 @@
 	if (is_slave)
 		pci_write_config_byte(HWIF(drive)->pci_dev, slave_port, slave_data);
 	restore_flags(flags);
-
-	switch(pio) {
-		case 4: speed = XFER_PIO_4;break;
-		case 3: speed = XFER_PIO_3;break;
-		case 2: speed = XFER_PIO_2;break;
-		case 1: speed = XFER_PIO_1;break;
-		default:
-			speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
-			break;
-	}
-
-	err = ide_wait_cmd(drive, WIN_SETFEATURES, speed, SETFEATURES_XFER, 0, NULL);
 }
 
-extern char *ide_xfer_verbose (byte xfer_rate);
-
 static int piix_config_drive_for_dma(ide_drive_t *drive, int ultra)
 {
 	struct hd_driveid *id = drive->id;
 	ide_hwif_t *hwif = HWIF(drive);
 	struct pci_dev *dev = hwif->pci_dev;
 
+	unsigned long		flags;
 	int			sitre;
 	short			reg4042, reg44, reg48, reg4a;
 	byte			speed;
@@ -144,23 +164,22 @@
 	pci_read_config_word(dev, 0x48, &reg48);
 	pci_read_config_word(dev, 0x4a, &reg4a);
 
-#if PIIX_DEBUG_SET_XFER
-	printk("PIIX%s: DMA enable ",
-		(dev->device == PCI_DEVICE_ID_INTEL_82371FB_0) ? "a" :
-		(dev->device == PCI_DEVICE_ID_INTEL_82371FB_1) ? "b" :
-		(dev->device == PCI_DEVICE_ID_INTEL_82371SB_1) ? "3" :
-		(dev->device == PCI_DEVICE_ID_INTEL_82371AB)   ? "4" : " UNKNOWN" );
-#endif /* PIIX_DEBUG_SET_XFER */
+	save_flags(flags);
+	cli();
 
 	if (id->dma_ultra && (ultra)) {
 		if (!(reg48 & u_flag)) {
 			pci_write_config_word(dev, 0x48, reg48|u_flag);
 		}
 	} else {
-		pci_write_config_word(dev, 0x48, reg48 & ~u_flag);
+		if (reg48 & u_flag) {
+			pci_write_config_word(dev, 0x48, reg48 & ~u_flag);
+		}
 	}
 
 	if ((id->dma_ultra & 0x0004) && (ultra)) {
+		drive->id->dma_mword &= ~0x0F00;
+		drive->id->dma_1word &= ~0x0F00;
 		if (!((id->dma_ultra >> 8) & 4)) {
 			drive->id->dma_ultra &= ~0x0F00;
 			drive->id->dma_ultra |= 0x0404;
@@ -171,6 +190,8 @@
 		}
 		speed = XFER_UDMA_2;
 	} else if ((id->dma_ultra & 0x0002) && (ultra)) {
+		drive->id->dma_mword &= ~0x0F00;
+		drive->id->dma_1word &= ~0x0F00;
 		if (!((id->dma_ultra >> 8) & 2)) {
 			drive->id->dma_ultra &= ~0x0F00;
 			drive->id->dma_ultra |= 0x0202;
@@ -182,6 +203,8 @@
 		}
 		speed = XFER_UDMA_1;
 	} else if ((id->dma_ultra & 0x0001) && (ultra)) {
+		drive->id->dma_mword &= ~0x0F00;
+		drive->id->dma_1word &= ~0x0F00;
 		if (!((id->dma_ultra >> 8) & 1)) {
 			drive->id->dma_ultra &= ~0x0F00;
 			drive->id->dma_ultra |= 0x0101;
@@ -193,33 +216,46 @@
 		}
 		speed = XFER_UDMA_0;
 	} else if (id->dma_mword & 0x0004) {
-		drive->id->dma_ultra &= ~0x0F0F;
-		pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+		if (reg4a & a_speed)
+			pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+		drive->id->dma_ultra &= ~0x0F00;
+		drive->id->dma_1word &= ~0x0F00;
 		if (!((id->dma_mword >> 8) & 4)) {
 			drive->id->dma_mword &= ~0x0F00;
 			drive->id->dma_mword |= 0x0404;
 		}
 		speed = XFER_MW_DMA_2;
 	} else if (id->dma_mword & 0x0002) {
-		drive->id->dma_ultra &= ~0x0F0F;
-		pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+		if (reg4a & a_speed)
+			pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+		drive->id->dma_ultra &= ~0x0F00;
+		drive->id->dma_1word &= ~0x0F00;
 		if (!((id->dma_mword >> 8) & 2)) {
 			drive->id->dma_mword &= ~0x0F00;
 			drive->id->dma_mword |= 0x0202;
 		}
 		speed = XFER_MW_DMA_1;
 	} else if (id->dma_1word & 0x0004) {
-		drive->id->dma_ultra &= ~0x0F0F;
-		pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+		if (reg4a & a_speed)
+			pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+		drive->id->dma_ultra &= ~0x0F00;
+		drive->id->dma_mword &= ~0x0F00;
 		if (!((id->dma_1word >> 8) & 4)) {
 			drive->id->dma_1word &= ~0x0F00;
 			drive->id->dma_1word |= 0x0404;
 		}
 		speed = XFER_SW_DMA_2;
         } else {
-		return ide_dma_off_quietly;
+#if 0
+		speed = XFER_PIO_0;
+#else
+		speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL);
+#endif
 	}
 
+	restore_flags(flags);
+	piix_tune_drive(drive, piix_dma_2_pio(speed));
+
 	(void) ide_wait_cmd(drive, WIN_SETFEATURES, speed, SETFEATURES_XFER, 0, NULL);
 
 #if PIIX_DEBUG_DRIVE_INFO
@@ -241,7 +277,7 @@
 	int ultra = (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_INTEL_82371AB) ? 1 : 0;
 	switch (func) {
 		case ide_dma_check:
-			return piix_config_drive_for_dma(drive, ultra);
+			 return ide_dmaproc((ide_dma_action_t) piix_config_drive_for_dma(drive, ultra), drive);
 		default :
 			break;
 	}
@@ -252,7 +288,7 @@
 void ide_init_piix (ide_hwif_t *hwif)
 {
 	hwif->tuneproc = &piix_tune_drive;
-#if PIIX_DMA_PROC
-	hwif->dmaproc = &piix_dmaproc;
-#endif /* PIIX_DMA_PROC */
+	if (hwif->dma_base) {
+		hwif->dmaproc = &piix_dmaproc;
+	}
 }
diff -u --recursive --new-file v2.3.5/linux/drivers/block/rd.c linux/drivers/block/rd.c
--- v2.3.5/linux/drivers/block/rd.c	Mon May 17 09:55:21 1999
+++ linux/drivers/block/rd.c	Fri Jun  4 01:03:28 1999
@@ -627,7 +627,7 @@
 #ifdef CONFIG_BLK_DEV_INITRD
 __initfunc(void initrd_load(void))
 {
-	rd_load_image(MKDEV(MAJOR_NR, INITRD_MINOR),0,0);
+	rd_load_image(MKDEV(MAJOR_NR, INITRD_MINOR),rd_image_start,0);
 }
 #endif
 
diff -u --recursive --new-file v2.3.5/linux/drivers/char/adbmouse.c linux/drivers/char/adbmouse.c
--- v2.3.5/linux/drivers/char/adbmouse.c	Thu Apr 29 12:53:48 1999
+++ linux/drivers/char/adbmouse.c	Mon Jun  7 12:12:22 1999
@@ -248,7 +248,7 @@
 {
     mouse.active = 0;
     mouse.ready = 0;
-    mouse.wait = NULL;
+    init_waitqueue_head(&mouse.wait);
 
 #ifdef __powerpc__
     if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) )
diff -u --recursive --new-file v2.3.5/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.3.5/linux/drivers/char/bttv.c	Mon May 31 22:28:05 1999
+++ linux/drivers/char/bttv.c	Mon Jun  7 16:17:59 1999
@@ -1,3 +1,4 @@
+
 /* 
     bttv - Bt848 frame grabber driver
 
@@ -545,6 +546,8 @@
         { 3, 4, 0, 2, 0x01e000, { 2, 0, 1, 1}, {0x01c000, 0, 0x018000, 0x014000, 0x002000, 0 }},
         /* "Leadtek WinView 601", */
         { 3, 1, 0, 2, 0x8300f8, { 2, 3, 1, 1,0}, {0x4fa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007}},
+        /* AVEC Intercapture */
+        { 3, 1, 9, 2, 0, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0}},
 };
 #define TVCARDS (sizeof(tvcards)/sizeof(tvcard))
 
@@ -2900,6 +2903,18 @@
         I2CWrite(bus, I2C_TEA6300, TEA6300_SW, 0x01, 1); /* mute off input A */
 }
 
+static void init_tea6320(struct i2c_bus *bus)
+{
+  I2CWrite(bus, I2C_TEA6300, TEA6320_V, 0x28, 1); /* master volume */
+  I2CWrite(bus, I2C_TEA6300, TEA6320_FFL, 0x28, 1); /* volume left 0dB  */
+  I2CWrite(bus, I2C_TEA6300, TEA6320_FFR, 0x28, 1); /* volume right 0dB */
+  I2CWrite(bus, I2C_TEA6300, TEA6320_FRL, 0x28, 1); /* volume rear left 0dB  */
+  I2CWrite(bus, I2C_TEA6300, TEA6320_FRR, 0x28, 1); /* volume rear right 0dB */
+  I2CWrite(bus, I2C_TEA6300, TEA6320_BA, 0x11, 1); /* bass 0dB         */
+  I2CWrite(bus, I2C_TEA6300, TEA6320_TR, 0x11, 1); /* treble 0dB       */
+  I2CWrite(bus, I2C_TEA6300, TEA6320_S, TEA6320_S_GMU, 1); /* mute off input A */
+}
+
 static void init_tda8425(struct i2c_bus *bus) 
 {
         I2CWrite(bus, I2C_TDA8425, TDA8425_VL, 0xFC, 1); /* volume left 0dB  */
@@ -3027,9 +3042,16 @@
         
         if (I2CRead(&(btv->i2c), I2C_TEA6300) >=0)
         {
+          if(btv->type==BTTV_AVEC_INTERCAP)
+            {
+                printk(KERN_INFO "bttv%d: fader chip: TEA6320\n",btv->nr);
+                btv->audio_chip = TEA6320;
+                init_tea6320(&(btv->i2c));
+            } else {
 		printk(KERN_INFO "bttv%d: fader chip: TEA6300\n",btv->nr);
 		btv->audio_chip = TEA6300;
 		init_tea6300(&(btv->i2c));
+            }
         } else
 		printk(KERN_INFO "bttv%d: NO fader chip: TEA6300\n",btv->nr);
 
@@ -3073,6 +3095,9 @@
 	        case BTTV_WINVIEW_601:
 			strcpy(btv->video_dev.name,"BT848(Leadtek WinView 601)");
  			break;	   
+                case BTTV_AVEC_INTERCAP:
+                        strcpy(btv->video_dev.name,"(AVEC Intercapture)");
+                        break;
 	}
 	printk("%s\n",btv->video_dev.name);
 	audio(btv, AUDIO_MUTE);
diff -u --recursive --new-file v2.3.5/linux/drivers/char/bttv.h linux/drivers/char/bttv.h
--- v2.3.5/linux/drivers/char/bttv.h	Mon May 31 22:28:05 1999
+++ linux/drivers/char/bttv.h	Mon Jun  7 16:17:59 1999
@@ -211,6 +211,7 @@
 #define BTTV_ZOLTRIX       0x0f
 #define BTTV_PIXVIEWPLAYTV 0x10
 #define BTTV_WINVIEW_601   0x11
+#define BTTV_AVEC_INTERCAP 0x12
 
 #define AUDIO_TUNER        0x00
 #define AUDIO_RADIO        0x01
@@ -225,6 +226,7 @@
 #define TDA8425            0x02
 #define TDA9840            0x03
 #define TEA6300            0x04
+#define TEA6320            0x05
 
 #define I2C_TSA5522        0xc2
 #define I2C_TDA9840	   0x84
@@ -233,7 +235,7 @@
 #define I2C_HAUPEE         0xa0
 #define I2C_STBEE          0xae
 #define I2C_VHX 	   0xc0
-#define I2C_TEA6300        0x80
+#define I2C_TEA6300        0x80 /* same as TEA6320 */
 
 #define TDA9840_SW	   0x00
 #define TDA9840_LVADJ	   0x02
@@ -260,6 +262,22 @@
 #define TEA6300_TR         0x03		/* treble control */
 #define TEA6300_FA         0x04		/* fader control */
 #define TEA6300_SW         0x05		/* mute and source switch */
+
+
+#define TEA6320_V          0x00
+#define TEA6320_FFR        0x01  /* volume front right */
+#define TEA6320_FFL        0x02  /* volume front left */
+#define TEA6320_FRR        0x03  /* volume rear right */
+#define TEA6320_FRL        0x04  /* volume rear left */
+#define TEA6320_BA         0x05  /* bass */
+#define TEA6320_TR         0x06  /* treble */
+#define TEA6320_S          0x07  /* switch register */
+                                 /* values for those registers: */
+#define TEA6320_S_SA       0x01  /* stereo A input */
+#define TEA6320_S_SB       0x02  /* stereo B */
+#define TEA6320_S_SC       0x04  /* stereo C */
+#define TEA6320_S_GMU      0x80  /* general mute */
+
 
 #define PT2254_L_CHANEL 0x10
 #define PT2254_R_CHANEL 0x08
diff -u --recursive --new-file v2.3.5/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.3.5/linux/drivers/char/misc.c	Fri Mar 26 13:57:41 1999
+++ linux/drivers/char/misc.c	Wed Jun  9 14:44:25 1999
@@ -76,6 +76,7 @@
 extern void dtlk_init(void);
 extern void pcwatchdog_init(void);
 extern int rtc_init(void);
+extern int rtc_sun_init(void);		/* Combines MK48T02 and MK48T08 */
 extern int rtc_DP8570A_init(void);
 extern int rtc_MK48T08_init(void);
 extern int dsp56k_init(void);
@@ -247,7 +248,10 @@
 #ifdef CONFIG_BVME6000
 	rtc_DP8570A_init();
 #endif
-#if defined(CONFIG_RTC) || defined(CONFIG_SUN_MOSTEK_RTC)
+#if defined(CONFIG_SUN_MOSTEK_RTC)
+	rtc_sun_init();
+#endif
+#if defined(CONFIG_RTC)
 	rtc_init();
 #endif
 #ifdef CONFIG_ATARI_DSP56K
diff -u --recursive --new-file v2.3.5/linux/drivers/char/radio-cadet.c linux/drivers/char/radio-cadet.c
--- v2.3.5/linux/drivers/char/radio-cadet.c	Fri May 14 18:55:17 1999
+++ linux/drivers/char/radio-cadet.c	Mon Jun  7 16:17:59 1999
@@ -1,7 +1,7 @@
 /* cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card 
  *
  * by Fred Gleason <fredg@wava.com>
- * Version 0.3.1
+ * Version 0.3.2
  *
  * (Loosely) based on code for the Aztech radio card by
  *
@@ -557,7 +557,7 @@
 		return -EINVAL;
 		
 	request_region(io,2,"cadet");
-	printk(KERN_INFO "ADS Cadet Radio Card at %x\n",io);
+	printk(KERN_INFO "ADS Cadet Radio Card at 0x%x\n",io);
 	return 0;
 }
 
@@ -570,12 +570,11 @@
 
 	for(i=0;i<8;i++) {
 	        io=iovals[i];
-	        if(check_region(io,2)) {
-	                return -1;
-		}  
-		cadet_setfreq(1410);
-		if(cadet_getfreq()==1410) {
-		        return io;
+	        if(check_region(io,2)>=0) {
+		        cadet_setfreq(1410);
+			if(cadet_getfreq()==1410) {
+			        return io;
+			}
 		}
 	}
 	return -1;
diff -u --recursive --new-file v2.3.5/linux/drivers/char/rtc.c linux/drivers/char/rtc.c
--- v2.3.5/linux/drivers/char/rtc.c	Fri May 14 18:55:17 1999
+++ linux/drivers/char/rtc.c	Wed Jun  9 14:44:25 1999
@@ -32,10 +32,11 @@
  *	1.08	Miquel van Smoorenburg: disallow certain things on the
  *		DEC Alpha as the CMOS clock is also used for other things.
  *	1.09	Nikita Schmidt: epoch support and some Alpha cleanup.
+ *	1.09a	Pete Zaitcev: Sun SPARC
  *
  */
 
-#define RTC_VERSION		"1.09"
+#define RTC_VERSION		"1.09a"
 
 #define RTC_IRQ 	8	/* Can't see this changing soon.	*/
 #define RTC_IO_EXTENT	0x10	/* Only really two ports, but...	*/
@@ -62,6 +63,12 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 
+#ifdef __sparc__
+#include <asm/ebus.h>
+
+static unsigned long rtc_port;
+#endif
+
 /*
  *	We sponge a minor off of the misc major. No need slurping
  *	up another valuable major dev number for this. If you add
@@ -83,12 +90,12 @@
 
 static unsigned int rtc_poll(struct file *file, poll_table *wait);
 
-void get_rtc_time (struct rtc_time *rtc_tm);
-void get_rtc_alm_time (struct rtc_time *alm_tm);
-void rtc_dropped_irq(unsigned long data);
+static void get_rtc_time (struct rtc_time *rtc_tm);
+static void get_rtc_alm_time (struct rtc_time *alm_tm);
+static void rtc_dropped_irq(unsigned long data);
 
-void set_rtc_irq_bit(unsigned char bit);
-void mask_rtc_irq_bit(unsigned char bit);
+static void set_rtc_irq_bit(unsigned char bit);
+static void mask_rtc_irq_bit(unsigned char bit);
 
 static inline unsigned char rtc_is_updating(void);
 
@@ -525,7 +532,42 @@
 	unsigned long uip_watchdog;
 	char *guess = NULL;
 #endif
+#ifdef __sparc__
+	struct linux_ebus *ebus;
+	struct linux_ebus_device *edev;
+	int rtc_irq;
+#endif
+
 	printk(KERN_INFO "Real Time Clock Driver v%s\n", RTC_VERSION);
+#ifdef __sparc__
+	for_each_ebus(ebus) {
+		for_each_ebusdev(edev, ebus) {
+			if(strcmp(edev->prom_name, "rtc") == 0) {
+				goto found;
+			}
+		}
+	}
+	printk("rtc_init: no PC rtc found\n");
+	return -EIO;
+
+found:
+	rtc_port = edev->base_address[0];
+	rtc_irq = edev->irqs[0];
+	/*
+	 * XXX Interrupt pin #7 in Espresso is shared between RTC and
+	 * PCI Slot 2 INTA# (and some INTx# in Slot 1). SA_INTERRUPT here
+	 * is asking for trouble with add-on boards. Change to SA_SHIRQ.
+	 */
+	if(request_irq(rtc_irq, rtc_interrupt, SA_INTERRUPT, "rtc", (void *)&rtc_port)) {
+		/*
+		 * Standard way for sparc to print irq's is to use
+		 * __irq_itoa(). I think for EBus it's ok to use %d.
+		 */
+		printk("rtc: cannot register IRQ %d\n", rtc_irq);
+		return -EIO;
+	}
+	misc_register(&rtc_dev);
+#else
 	if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL))
 	{
 		/* Yeah right, seeing as irq 8 doesn't even hit the bus. */
@@ -535,6 +577,7 @@
 	misc_register(&rtc_dev);
 	/* Check region? Naaah! Just snarf it up. */
 	request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");
+#endif /* __sparc__ vs. others */
 #ifdef __alpha__
 	rtc_freq = HZ;
 	
@@ -588,7 +631,7 @@
  *	for something that requires a steady > 1KHz signal anyways.)
  */
 
-void rtc_dropped_irq(unsigned long data)
+static void rtc_dropped_irq(unsigned long data)
 {
 	unsigned long flags;
 
@@ -696,7 +739,7 @@
 	return uip;
 }
 
-void get_rtc_time(struct rtc_time *rtc_tm)
+static void get_rtc_time(struct rtc_time *rtc_tm)
 {
 
 	unsigned long flags, uip_watchdog = jiffies;
@@ -753,7 +796,7 @@
 	rtc_tm->tm_mon--;
 }
 
-void get_rtc_alm_time(struct rtc_time *alm_tm)
+static void get_rtc_alm_time(struct rtc_time *alm_tm)
 {
 	unsigned long flags;
 	unsigned char ctrl;
@@ -803,7 +846,7 @@
 	rtc_irq_data = 0;
 }
 
-void set_rtc_irq_bit(unsigned char bit)
+static void set_rtc_irq_bit(unsigned char bit)
 {
 	unsigned char val;
 	unsigned long flags;
diff -u --recursive --new-file v2.3.5/linux/drivers/char/tuner.c linux/drivers/char/tuner.c
--- v2.3.5/linux/drivers/char/tuner.c	Sun Jan 17 18:28:06 1999
+++ linux/drivers/char/tuner.c	Mon Jun  7 16:17:59 1999
@@ -84,7 +84,9 @@
 	      //  16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,0xc2,623},
 	        16*170.00,16*450.00,0x02,0x04,0x01,0x8e,0xc2,623},
 	{"Temic 4036 FY5 NTSC", TEMIC, NTSC,
-		16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc2,732},
+	        16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc2,732},
+        {"Alps HSBH1", TEMIC, NTSC,
+                16*137.25,16*385.25,0x01,0x02,0x08,0x8e,0xc2,732},
 };
 
 /* ---------------------------------------------------------------------- */
diff -u --recursive --new-file v2.3.5/linux/drivers/char/tuner.h linux/drivers/char/tuner.h
--- v2.3.5/linux/drivers/char/tuner.h	Sun Nov  8 14:36:46 1998
+++ linux/drivers/char/tuner.h	Mon Jun  7 16:17:59 1999
@@ -31,6 +31,7 @@
 #define TUNER_TEMIC_NTSC    6
 #define TUNER_TEMIC_PAL_I   7
 #define TUNER_TEMIC_4036FY5_NTSC	8
+#define TUNER_ALPS_TSBH1_NTSC 9
 
 #define NOTUNER 0
 #define PAL     1
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/Config.in linux/drivers/i2o/Config.in
--- v2.3.5/linux/drivers/i2o/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/Config.in	Wed Jun  2 14:47:42 1999
@@ -0,0 +1,12 @@
+mainmenu_option next_comment
+comment 'I2O device support'
+
+tristate 'I2O support' CONFIG_I2O
+
+dep_tristate 'I2O PCI support' CONFIG_I2O_PCI $CONFIG_I2O
+dep_tristate 'I2O Block OSM' CONFIG_I2O_BLOCK $CONFIG_I2O
+dep_tristate 'I2O LAN OSM' CONFIG_I2O_LAN $CONFIG_I2O
+dep_tristate 'I2O SCSI OSM' CONFIG_I2O_SCSI $CONFIG_I2O
+dep_tristate 'I2O /proc support' CONFIG_I2O_PROC $CONFIG_I2O
+
+endmenu
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/Makefile linux/drivers/i2o/Makefile
--- v2.3.5/linux/drivers/i2o/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/Makefile	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,75 @@
+#
+# Makefile for the kernel I2O OSM.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now inherited from the
+# parent makefile.
+#
+
+#
+# Note : at this point, these files are compiled on all systems.
+# In the future, some of these should be built conditionally.
+#
+
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+
+L_TARGET := i2o.a
+L_OBJS   := 
+M_OBJS   :=
+
+ifeq ($(CONFIG_I2O_PCI),y)
+L_OBJS += i2o_pci.o
+else
+  ifeq ($(CONFIG_I2O_PCI),m)
+  M_OBJS += i2o_pci.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2O),y)
+LX_OBJS += i2o_core.o i2o_config.o
+else
+  ifeq ($(CONFIG_I2O),m)
+  MX_OBJS += i2o_core.o i2o_config.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2O_BLOCK),y)
+LX_OBJS += i2o_block.o
+else
+  ifeq ($(CONFIG_I2O_BLOCK),m)
+  MX_OBJS += i2o_block.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2O_LAN),y)
+LX_OBJS += i2o_lan.o
+else
+  ifeq ($(CONFIG_I2O_LAN),m)
+  MX_OBJS += i2o_lan.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2O_SCSI),y)
+LX_OBJS += i2o_scsi.o
+else
+  ifeq ($(CONFIG_I2O_SCSI),m)
+  MX_OBJS += i2o_scsi.o
+  endif
+endif
+
+ifeq ($(CONFIG_I2O_PROC),y)
+LX_OBJS += i2o_proc.o
+else
+  ifeq ($(CONFIG_I2O_PROC),m)
+  MX_OBJS += i2o_proc.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/README linux/drivers/i2o/README
--- v2.3.5/linux/drivers/i2o/README	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/README	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,78 @@
+
+	Linux I2O Support	(c) Copyright 1999 Red Hat Software
+					and others.
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version
+	2 of the License, or (at your option) any later version.
+
+AUTHORS (so far)
+
+Alan Cox, 	Building Number Three Ltd.
+	Core code, SCSI and Block OSMs
+
+Steve Ralston,	LSI Logic Corp.
+	Debugging SCSI and Block OSM
+
+Deepak Saxena,	Intel Corp.
+	/proc interface, bug fixes
+	Ioctl interfaces for control
+
+Philip Rumpf
+	Fixed assorted dumb SMP locking bugs
+
+Juha Sievanen,  University Of Helsinki Finland
+	LAN OSM
+	Bug fixes
+	Core code extensions
+
+CREDITS
+
+	This work was made possible by 
+
+Red Hat Software
+	Funding for the Building #3 part of the project
+
+Symbios Logic (Now LSI)
+	Host adapters, hints, known to work platforms when I hit
+	compatibility problems
+
+BoxHill Corporation
+	Loan of initial FibreChannel disk array used for development work.
+
+STATUS:
+
+o	The core setup works within limits.
+o	The scsi layer seems to almost work. I'm still chasing down the hang
+	bug.
+o	The block OSM is fairly minimal but does seem to work.
+
+
+TO DO:
+
+General:
+o	Support multiple IOP's and tell them about each other
+o	Provide hidden address space if asked
+o	Long term message flow control
+o	PCI IOP's without interrupts are not supported yet
+o	Push FAIL handling into the core
+o	DDM control interfaces for module load etc
+
+Block:
+o	Real error handler
+o	Multiple major numbers
+o	Read ahead and cache handling stuff. Talk to Ingo and people
+o	Power management
+o	Finish Media changers
+
+SCSI:
+o	Find the right way to associate drives/luns/busses
+
+Net:
+o	Port the existing RCPCI work to the frame work or write a new
+	driver. This one is with the Finns
+
+Tape:
+o	Anyone seen anything implementing this ?
+
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/README.ioctl linux/drivers/i2o/README.ioctl
--- v2.3.5/linux/drivers/i2o/README.ioctl	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/README.ioctl	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,398 @@
+
+Linux I2O User Space Interface
+rev 0.3 - 04/20/99
+
+=============================================================================
+Originally written by Deepak Saxena(deepak.saxena@intel.com)
+Currently maintained by Deepak Saxena(deepak.saxena@intel.com)
+=============================================================================
+
+I. Introduction
+
+The Linux I2O susbsytem provides a set of ioctl() commands than can be
+utilized by user space applications to communicate with IOPs and devices
+on individual IOPs. This document defines the specific ioctl() commands
+that are available to the user and provides examples of their uses.
+
+This document assumes the reader is familiar with or has access to the 
+I2O specification as no I2O message parameters are outlined.  For information 
+on the specification, see http://www.i2osig.org
+
+This document and the I2O user space interface are currently maintained
+by Deepak Saxena.  Please send all comments, errata, and bug fixes to
+deepak.saxena@intel.com
+
+II. IOP Access
+
+Access to the I2O subsystem is provided through the device file named 
+/dev/i2octl.  This file is a character file with major number 10 and minor
+number 166.  It can be created through the following command:
+
+   mknod /dev/i2octl c 10 166
+
+III. Determining the IOP Count
+
+   SYNOPSIS 
+
+   ioctl(fd, I2OGETIOPS,  int *count);
+
+   u8 count[MAX_I2O_CONTROLLERS];
+
+   DESCRIPTION
+
+   This function returns the system's active IOP table.  count should
+   point to a buffer containing MAX_I2O_CONTROLLERS entries.  Upon 
+   returning, each entry will contain a non-zero value if the given
+   IOP unit is active, and NULL if it is inactive or non-existent.
+
+   RETURN VALUE.
+
+   Returns 0 if no errors occur, and -1 otherwise.  If an error occurs,
+   errno is set appropriately:
+
+      EIO      Unkown error
+
+IV. ExecHrtGet Message
+
+   SYNOPSIS 
+ 
+   ioctl(fd, I2OHRTGET, struct i2o_cmd_hrt *hrt);
+
+      struct i2o_cmd_hrtlct
+      {
+         u32   iop;      /* IOP unit number */
+         void  *resbuf;  /* Buffer for result */
+         u32   *reslen;  /* Buffer length in bytes */
+      };
+
+   DESCRIPTION
+
+   This function posts an ExecHrtHet message to the IOP specified by
+   hrt->iop and returns the data in the buffer pointed to by hrt->buf
+   The size of the data written is placed into the memory pointed to
+   by hrt->len.
+
+   RETURNS
+
+   This function returns 0 if no errors occur. If an error occurs, -1 
+   is returned and errno is set appropriately:
+
+      ETIMEDOUT   Timeout waiting for reply message
+      ENOMEM      Kernel memory allocation error
+      ENOBUFS     Buffer not large enough.  If this occurs, the required
+                  buffer length is written into *(hrt->reslen)
+      EFAULT      Invalid user space pointer was passed
+      ENXIO       Invalid IOP number
+      EIO         Unkown error
+  
+V. ExecLctNotify Message
+   
+   SYNOPSIS 
+ 
+   ioctl(fd, I2OLCTGET, struct i2o_cmd_lct *lct);
+
+      struct i2o_cmd_hrtlct
+      {
+         u32   iop;      /* IOP unit number */
+         void  *resbuf;  /* Buffer for result */
+         u32   *reslen;  /* Buffer length in bytes */
+      };
+
+   DESCRIPTION
+
+   This function posts an ExecLctGet message to the IOP specified by 
+   lct->iop and returns the data in the buffer pointed to by lct->buf
+   The size of the data written is placed into the memory pointed to
+   by lct->reslen.
+
+   RETURNS
+
+   This function returns 0 if no errors occur. If an error occurs, -1 
+   is returned and errno is set appropriately:
+
+      ETIMEDOUT   Timeout waiting for reply message
+      ENOMEM      Kernel memory allocation error
+      ENOBUFS     Buffer not large enough.  If this occurs, the required
+                  buffer length is written into *(lct->reslen)
+      EFAULT      Invalid user space pointer was passed
+      ENXIO       Invalid IOP number
+      EIO         Unkown error
+
+VI. UtilParamsSet Message
+   
+   SYNOPSIS 
+ 
+   ioctl(fd, I2OPARMSET, struct i2o_parm_setget *ops);
+
+      struct i2o_cmd_psetget
+      {
+         u32   iop;      /* IOP unit number */
+         u32   tid;      /* Target device TID */
+         void  *opbuf;   /* Operation List buffer */
+         u32   oplen;    /* Operation List buffer length in bytes */
+         void  *resbuf;  /* Result List buffer */
+         u32   *reslen;  /* Result List buffer length in bytes */
+      };
+
+   DESCRIPTION
+
+   This function posts a UtilParamsSet message to the device identified
+   by ops->iop and ops->tid.  The operation list for the message is 
+   sent through the ops->oplen buffer, and the result list is written
+   into the buffer pointed to by ops->oplen.  The number of bytes 
+   written is placed into *(ops->reslen). 
+
+   RETURNS
+
+   The return value is the size in bytes of the data written into
+   ops->resbuf if no errors occur.  If an error occurs, -1 is returned 
+   and errno is set appropriatly:
+
+      ETIMEDOUT   Timeout waiting for reply message
+      ENOMEM      Kernel memory allocation error
+      ENOBUFS     Buffer not large enough.  If this occurs, the required
+                  buffer length is written into *(ops->reslen)
+      EFAULT      Invalid user space pointer was passed
+      ENXIO       Invalid IOP number
+      EIO         Unkown error
+
+   A return value of 0 does not mean that the value was actually
+   changed properly on the IOP.  The user should check the result
+   list to determine the specific status of the transaction.
+
+VII. UtilParamsGet Message
+   
+   SYNOPSIS 
+ 
+   ioctl(fd, I2OPARMGET, struct i2o_parm_setget *ops);
+
+      struct i2o_parm_setget
+      {
+         u32   iop;      /* IOP unit number */
+         u32   tid;      /* Target device TID */
+         void  *opbuf;   /* Operation List buffer */
+         u32   oplen;    /* Operation List buffer length in bytes */
+         void  *resbuf;  /* Result List buffer */
+         u32   *reslen;  /* Result List buffer length in bytes */
+      };
+
+   DESCRIPTION
+
+   This function posts a UtilParamsGet message to the device identified
+   by ops->iop and ops->tid.  The operation list for the message is 
+   sent through the ops->oplen buffer, and the result list is written
+   into the buffer pointed to by ops->oplen.  The actual size of data
+   written is placed into *(ops->reslen).
+
+   RETURNS
+
+      ETIMEDOUT   Timeout waiting for reply message
+      ENOMEM      Kernel memory allocation error
+      ENOBUFS     Buffer not large enough.  If this occurs, the required
+                  buffer length is written into *(ops->reslen)
+      EFAULT      Invalid user space pointer was passed
+      ENXIO       Invalid IOP number
+      EIO         Unkown error
+
+   A return value of 0 does not mean that the value was actually
+   properly retreived.  The user should check the result list 
+   to determine the specific status of the transaction.
+
+VIII. ExecSwDownload Message
+   
+   SYNOPSIS 
+ 
+   ioctl(fd, I2OSWDL, struct i2o_sw_xfer *sw);
+
+      struct i2o_sw_xfer
+      {
+         u32   iop;       /* IOP unit number */
+         u8    dl_flags;  /* DownLoadFlags field */
+         u8    sw_type;   /* Software type */
+         u32   sw_id;     /* Software ID */
+         void  *buf;      /* Pointer to software buffer */
+         u32   *swlen;    /* Length of software data */        
+         u32   *maxfrag;  /* Number of fragments */
+         u32   *curfrag;  /* Current fragment number */
+      };
+
+   DESCRIPTION
+
+   This function downloads the software pointed to by  sw->buf to the
+   iop identified by sw->iop. The DownloadFlags, SwID, and SwType fields
+   of the ExecSwDownload message are filed in with the values of 
+   sw->dl_flags, sw->sw_id, and sw->sw_type.
+
+   Once the ioctl() is called and software transfer begins, the 
+   user can read the value *(sw->maxfrag) and *(sw->curfrag) to
+   determine the status of the software transfer. As the IOP
+   is very slow when it comes to SW transfers, this can be
+   used by a separate thread to report status to the user. The
+   user _should not_ write to this memory location until the ioctl()
+   has returned.
+
+   RETURNS
+
+   This function returns 0 no errors occur. If an error occurs, -1 
+   is returned and errno is set appropriatly:
+
+      ETIMEDOUT   Timeout waiting for reply message
+      ENOMEM      Kernel memory allocation error
+      ENOBUFS     Buffer not large enough.  If this occurs, the required
+                  buffer length is written into *(ops->reslen)
+      EFAULT      Invalid user space pointer was passed
+      ENXIO       Invalid IOP number
+      EIO         Unkown error
+
+IX. ExecSwUpload Message
+   
+   SYNOPSIS 
+ 
+   ioctl(fd, I2OSWUL, struct i2o_sw_xfer *sw);
+
+      struct i2o_sw_xfer
+      {
+         u32   iop;      /* IOP unit number */
+         u8    flags;    /* Unused */
+         u8    sw_type;  /* Software type */
+         u32   sw_id;    /* Software ID */
+         void  *buf;     /* Pointer to software buffer */
+         u32   *swlen;   /* Length in bytes of software */        
+         u32   *maxfrag; /* Number of fragments */
+         u32   *curfrag; /* Current fragment number */
+      };
+
+   DESCRIPTION
+
+   This function uploads software from the IOP identified by sw->iop
+   and places it in the buffer pointed to by sw->buf.  The SwID, SwType
+   and SwSize fields of the ExecSwDownload message are filed in 
+   with the values of sw->sw_id, sw->sw_type, sw->swlen, and.  The 
+   actual size of the module is written into *(sw->buflen).
+
+   Once the ioctl() is called and software transfer begins, the 
+   user can read the value *(sw->maxfrag) and *(sw->curfrag) to
+   determine the status of the software transfer.  As the IOP
+   is very slow when it comes to SW transfers, this can be
+   used by a separate thread to report status to the user. The
+   user _should not_ write to this memory location until the ioctl()
+   has returned.
+
+   RETURNS
+
+   This function returns 0 if no errors occur.  If an error occurs, -1
+   is returned and errno is set appropriatly:
+
+      ETIMEDOUT   Timeout waiting for reply message
+      ENOMEM      Kernel memory allocation error
+      ENOBUFS     Buffer not large enough.  If this occurs, the required
+                  buffer length is written into *(ops->reslen)
+      EFAULT      Invalid user space pointer was passed
+      ENXIO       Invalid IOP number
+      EIO         Unkown error
+         
+X. ExecSwRemove Message
+   
+   SYNOPSIS 
+ 
+   ioctl(fd, I2OSWDEL, struct i2o_sw_xfer *sw);
+
+      struct i2o_sw_xfer
+      {
+         u32   iop;      /* IOP unit number */
+         u8    flags;    /* Unused */
+         u8    sw_type;  /* Software type */
+         u32   sw_id;    /* Software ID */
+         void  *buf;     /* Unused */
+         u32   *swlen;   /* Length in bytes of software data */        
+         u32   *maxfrag; /* Unused */
+         u32   *curfrag; /* Unused */
+      };
+
+   DESCRIPTION
+
+   This function uploads software from the IOP identified by sw->iop
+   and places it in the buffer pointed to by sw->buf.  The SwID, SwType
+   and SwSize fields of the ExecSwDownload message are filed in 
+   with the values of sw->dl_flags, sw->sw_id, and sw->sw_type.  The 
+   actual size of the module is written into *(sw->buflen).
+
+   RETURNS
+
+   This function returns 0 if no errors occur.  If an error occurs, -1
+   is returned and errno is set appropriatly:
+
+      ETIMEDOUT   Timeout waiting for reply message
+      ENOMEM      Kernel memory allocation error
+      ENOBUFS     Buffer not large enough.  If this occurs, the required
+                  buffer length is written into *(ops->reslen)
+      EFAULT      Invalid user space pointer was passed
+      ENXIO       Invalid IOP number
+      EIO         Unkown error
+
+X. UtilConfigDialog Message
+   
+   SYNOPSIS 
+ 
+   ioctl(fd, I2OHTML, struct i2o_html *htquery);
+
+      struct i2o_html
+      {
+         u32   iop;      /* IOP unit number */
+         u32   tid;      /* Target device ID */
+         u32   page;     /* HTML page */
+         void  *resbuf;  /* Buffer for reply HTML page */
+         u32   *reslen;  /* Length in bytes of reply buffer */
+         void  *qbuf;    /* Pointer to HTTP query string */
+         u32   qlen;     /* Length in bytes of query string buffer */        
+      };
+
+   DESCRIPTION
+
+   This function posts an UtilConfigDialog message to the device identified
+   by htquery->iop and htquery->tid.  The requested HTML page number is 
+   provided by the htquery->page field, and the resultant data is stored 
+   in the buffer pointed to by htquery->resbuf.  If there is an HTTP query 
+   string that is to be sent to the device, it should be sent in the buffer
+   pointed to by htquery->qbuf.  If there is no query string, this field
+   should be set to NULL. The actual size of the reply received is written
+   into *(htquery->reslen)
+  
+   RETURNS
+
+   This function returns 0 if no error occur. If an error occurs, -1
+   is returned and J errno is set appropriatly:
+
+      ETIMEDOUT   Timeout waiting for reply message
+      ENOMEM      Kernel memory allocation error
+      ENOBUFS     Buffer not large enough.  If this occurs, the required
+                  buffer length is written into *(ops->reslen)
+      EFAULT      Invalid user space pointer was passed
+      ENXIO       Invalid IOP number
+      EIO         Unkown error
+
+XI. Events
+
+    In the process of determining this.  Current idea is to have use
+    the select() interface to allow user apps to periodically poll
+    the /dev/i2octl device for events.  When select() notifies the user
+    that an event is available, the user would call read() to retrieve
+    a list of all the events that are pending for the specific device.
+
+=============================================================================
+Revision History
+=============================================================================
+
+Rev 0.1 - 04/01/99
+- Initial revision
+
+Rev 0.2 - 04/06/99
+- Changed return values to match UNIX ioctl() standard.  Only return values
+  are 0 and -1.  All errors are reported through errno.
+- Added summary of proposed possible event interfaces
+
+Rev 0.3 - 04/20/99
+- Changed all ioctls() to use pointers to user data instead of actual data
+- Updated error values to match the code
+
+
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/README.lan linux/drivers/i2o/README.lan
--- v2.3.5/linux/drivers/i2o/README.lan	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/README.lan	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,38 @@
+
+	Linux I2O LAN OSM
+	(c) University of Helsinki, Department of Computer Science
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version
+	2 of the License, or (at your option) any later version.
+
+AUTHORS
+Auvo Häkkinen, Auvo.Hakkinen@cs.Helsinki.FI
+Juha Sievänen, Juha.Sievanen@cs.Helsinki.FI
+
+CREDITS
+
+	This work was made possible by 
+
+European Committee
+	Funding for the project
+
+SysKonnect
+	Loaning of FDDI cards
+
+ASUSTeK
+	I2O motherboard
+
+STATUS:
+o	The FDDI part of LAN OSM is working to some extent.
+o	Only packet per bucket is now supported.
+
+TO DO:
+
+LAN:
+o	Add support for bactches
+o	Find why big packets flow from I2O box out, but don't want to come in
+o	Find the bug in i2o_set_multicast_list(), which kills interrupt
+	handler in i2o_wait_reply()
+o	Add support for Ethernet, Token Ring, AnyLAN, Fibre Channel
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/i2o_block.c linux/drivers/i2o/i2o_block.c
--- v2.3.5/linux/drivers/i2o/i2o_block.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/i2o_block.c	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,1071 @@
+/*
+ *	I2O block device driver. 
+ *
+ *	(C) Copyright 1999   Red Hat Software
+ *	
+ *	Written by Alan Cox, Building Number Three Ltd
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ * 	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *
+ *	This is an initial test release. Most of the good code was taken
+ *	from the nbd driver by Pavel Machek, who in turn took some of it
+ *	from loop.c. Isn't free software great for reusability 8)
+ *
+ *	Fixes:
+ *		Steve Ralston:	Multiple device handling error fixes,
+ *				Added a queue depth.
+ */
+
+#include <linux/major.h>
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/ioctl.h>
+#include <linux/i2o.h>
+#include <linux/blkdev.h>
+#include <linux/malloc.h>
+#include <linux/hdreg.h>
+
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+
+#define MAJOR_NR I2O_MAJOR
+
+#include <linux/blk.h>
+
+#define MAX_I2OB	16
+
+#define MAX_I2OB_DEPTH	4
+
+/*
+ *	Some of these can be made smaller later
+ */
+
+static int i2ob_blksizes[MAX_I2OB<<4];
+static int i2ob_hardsizes[MAX_I2OB<<4];
+static int i2ob_sizes[MAX_I2OB<<4];
+static int i2ob_media_change_flag[MAX_I2OB];
+static u32 i2ob_max_sectors[MAX_I2OB<<4];
+
+static int i2ob_context;
+
+#ifdef __SMP__
+static spinlock_t i2ob_lock = SPIN_LOCK_UNLOCKED;
+#endif
+
+struct i2ob_device
+{
+	struct i2o_controller *controller;
+	struct i2o_device *i2odev;
+	int tid;
+	int flags;
+	int refcnt;
+	struct request *head, *tail;
+	int done_flag;
+};
+
+/*
+ *	Each I2O disk is one of these.
+ */
+
+static struct i2ob_device i2ob_dev[MAX_I2OB<<4];
+static int i2ob_devices = 0;
+static struct hd_struct i2ob[MAX_I2OB<<4];
+static struct gendisk i2ob_gendisk;	/* Declared later */
+
+static atomic_t queue_depth;		/* For flow control later on */
+
+#define DEBUG( s )
+/* #define DEBUG( s ) printk( s ) 
+ */
+
+static int i2ob_install_device(struct i2o_controller *, struct i2o_device *, int);
+static void i2ob_end_request(struct request *);
+static void do_i2ob_request(void);
+
+/*
+ *	Get a message
+ */
+
+static u32 i2ob_get(struct i2ob_device *dev)
+{ 
+	struct i2o_controller *c=dev->controller;
+	return I2O_POST_READ32(c);
+}
+ 
+/*
+ *	Turn a Linux block request into an I2O block read/write.
+ */
+
+static int i2ob_send(u32 m, struct i2ob_device *dev, struct request *req, u32 base, int unit)
+{
+	struct i2o_controller *c = dev->controller;
+	int tid = dev->tid;
+	u32 *msg;
+	u32 *mptr;
+	u64 offset;
+	struct buffer_head *bh = req->bh;
+	static int old_qd = 2;
+	int count = req->nr_sectors<<9;
+	
+	/*
+	 *	Build a message
+	 */
+	
+	msg = bus_to_virt(c->mem_offset + m);
+	
+	msg[2] = i2ob_context|(unit<<8);
+	msg[3] = (u32)req;	/* 64bit issue again here */
+	msg[5] = req->nr_sectors << 9;
+	
+	/* This can be optimised later - just want to be sure its right for
+	   starters */
+	offset = ((u64)(req->sector+base)) << 9;
+	msg[6] = offset & 0xFFFFFFFF;
+	msg[7] = (offset>>32);
+	mptr=msg+8;
+	
+	if(req->cmd == READ)
+	{
+		msg[1] = I2O_CMD_BLOCK_READ<<24|HOST_TID<<12|tid;
+		/* We don't yet do cache/readahead and other magic */
+		msg[4] = 1<<16;	
+		while(bh!=NULL)
+		{
+			*mptr++ = 0x10000000|(bh->b_size);
+			*mptr++ = virt_to_bus(bh->b_data);
+			count -= bh->b_size;
+			bh = bh->b_reqnext;
+		}
+	}
+	else if(req->cmd == WRITE)
+	{
+		msg[1] = I2O_CMD_BLOCK_WRITE<<24|HOST_TID<<12|tid;
+		msg[4] = 1<<16;
+		while(bh!=NULL)
+		{
+			*mptr++ = 0x14000000|(bh->b_size);
+			count -= bh->b_size;
+			*mptr++ = virt_to_bus(bh->b_data);
+			bh = bh->b_reqnext;
+		}
+	}
+	mptr[-2]|= 0xC0000000;
+	msg[0] = I2O_MESSAGE_SIZE(mptr-msg) | SGL_OFFSET_8;
+	
+	if(req->current_nr_sectors > 8)
+		printk("Gathered sectors %ld.\n", 
+			req->current_nr_sectors);
+			
+	if(count != 0)
+	{
+		printk("Request count botched by %d.\n", count);
+		msg[5] -= count;
+	}
+
+//	printk("Send for %p\n", req);
+
+	i2o_post_message(c,m);
+	atomic_inc(&queue_depth);
+	if(atomic_read(&queue_depth)>old_qd)
+	{
+		old_qd=atomic_read(&queue_depth);
+		printk("Depth now %d.\n", old_qd);
+	}
+	return 0;
+}
+
+/*
+ *	Remove a request from the _locked_ request list. We update both the
+ *	list chain and if this is the last item the tail pointer.
+ */
+ 
+static void i2ob_unhook_request(struct i2ob_device *dev, struct request *req)
+{
+	struct request **p = &dev->head;
+	struct request *nt = NULL;
+	static int crap = 0;
+	
+	while(*p!=NULL)
+	{
+		if(*p==req)
+		{
+			if(dev->tail==req)
+				dev->tail = nt;
+			*p=req->next;
+			return;
+		}
+		nt=*p;
+		p=&(nt->next);
+	}
+	if(!crap++)
+		printk("i2o_block: request queue corrupt!\n");
+}
+
+/*
+ *	Request completion handler
+ */
+ 
+static void i2ob_end_request(struct request *req)
+{
+	/*
+	 * Loop until all of the buffers that are linked
+	 * to this request have been marked updated and
+	 * unlocked.
+	 */
+	while (end_that_request_first( req, !req->errors, "i2o block" ));
+
+	/*
+	 * It is now ok to complete the request.
+	 */
+	end_that_request_last( req );
+}
+
+
+/*
+ *	OSM reply handler. This gets all the message replies
+ */
+
+static void i2o_block_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *msg)
+{
+	struct request *req;
+	u8 st;
+	u32 *m = (u32 *)msg;
+	u8 unit = (m[2]>>8)&0xF0;	/* low 4 bits are partition */
+	
+	if(m[0] & (1<<13))
+	{
+		printk("IOP fail.\n");
+		printk("From %d To %d Cmd %d.\n",
+			(m[1]>>12)&0xFFF,
+			m[1]&0xFFF,
+			m[1]>>24);
+		printk("Failure Code %d.\n", m[4]>>24);
+		if(m[4]&(1<<16))
+			printk("Format error.\n");
+		if(m[4]&(1<<17))
+			printk("Path error.\n");
+		if(m[4]&(1<<18))
+			printk("Path State.\n");
+		if(m[4]&(1<<18))
+			printk("Congestion.\n");
+		
+		m=(u32 *)bus_to_virt(m[7]);
+		printk("Failing message is %p.\n", m);
+		
+		/* We need to up the request failure count here and maybe
+		   abort it */
+		req=(struct request *)m[3];
+		/* Now flush the message by making it a NOP */
+		m[0]&=0x00FFFFFF;
+		m[0]|=(I2O_CMD_UTIL_NOP)<<24;
+		i2o_post_message(c,virt_to_bus(m));
+		
+	}
+	else
+	{
+		if(m[2]&0x80000000)
+		{
+			int * ptr = (int *)m[3];
+			if(m[4]>>24)
+				*ptr = -1;
+			else
+				*ptr = 1;
+			return;
+		}
+		/*
+		 *	Lets see what is cooking. We stuffed the
+		 *	request in the context.
+		 */
+		 
+		req=(struct request *)m[3];
+		st=m[4]>>24;
+	
+		if(st!=0)
+		{
+			printk(KERN_ERR "i2ob: error %08X\n", m[4]);
+			/*
+			 *	Now error out the request block
+			 */
+			req->errors++;	
+		}
+	}
+	/*
+	 *	Dequeue the request.
+	 */
+	
+	spin_lock(&io_request_lock);
+	spin_lock(&i2ob_lock);
+	i2ob_unhook_request(&i2ob_dev[unit], req);
+	i2ob_end_request(req);
+	
+	/*
+	 *	We may be able to do more I/O
+	 */
+	 
+	atomic_dec(&queue_depth);
+	do_i2ob_request();
+	spin_unlock(&i2ob_lock);
+	spin_unlock(&io_request_lock);
+}
+
+static struct i2o_handler i2o_block_handler =
+{
+	i2o_block_reply,
+	"I2O Block OSM",
+	0
+};
+
+
+/*
+ *	Flush all pending requests as errors. Must call with the queue
+ *	locked.
+ */
+ 
+#if 0
+static void i2ob_clear_queue(struct i2ob_device *dev)
+{
+	struct request *req;
+
+	while (1) {
+		req = dev->tail;
+		if (!req)
+			return;
+		req->errors++;
+		i2ob_end_request(req);
+
+		if (dev->tail == dev->head)
+			dev->head = NULL;
+		dev->tail = dev->tail->next;
+	}
+}
+#endif
+
+/*
+ *	The I2O block driver is listed as one of those that pulls the
+ *	front entry off the queue before processing it. This is important
+ *	to remember here. If we drop the io lock then CURRENT will change
+ *	on us. We must unlink CURRENT in this routine before we return, if
+ *	we use it.
+ */
+
+static void do_i2ob_request(void)
+{
+	struct request *req;
+	int unit;
+	struct i2ob_device *dev;
+	u32 m;
+
+	while (CURRENT) {
+		/*
+		 *	On an IRQ completion if there is an inactive
+		 *	request on the queue head it means it isnt yet
+		 *	ready to dispatch.
+		 */
+		if(CURRENT->rq_status == RQ_INACTIVE)
+			return;
+			
+		/*
+		 *	Queue depths probably belong with some kind of
+		 *	generic IOP commit control. Certainly its not right
+		 *	its global!
+		 */
+		if(atomic_read(&queue_depth)>=MAX_I2OB_DEPTH)
+			break;
+
+		req = CURRENT;
+		unit = MINOR(req->rq_dev);
+		dev = &i2ob_dev[(unit&0xF0)];
+		/* Get a message */
+		m = i2ob_get(dev);
+		/* No messages -> punt 
+		   FIXME: if we have no messages, and there are no messages 
+		   we deadlock now. Need a timer/callback ?? */
+		if(m==0xFFFFFFFF)
+		{
+			printk("i2ob: no messages!\n");
+			break;
+		}
+		req->errors = 0;
+		CURRENT = CURRENT->next;
+		req->next = NULL;
+
+		if (dev->head == NULL) {
+			dev->head = req;
+			dev->tail = req;
+		} else {
+			dev->tail->next = req;
+			dev->tail = req;
+		}
+		i2ob_send(m, dev, req, i2ob[unit].start_sect, (unit&0xF0));
+	}
+}
+
+static void i2ob_request(void)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&i2ob_lock, flags);
+	do_i2ob_request();
+	spin_unlock_irqrestore(&i2ob_lock, flags);
+}	
+
+/*
+ *	SCSI-CAM for ioctl geometry mapping
+ *	Duplicated with SCSI - this should be moved into somewhere common
+ *	perhaps genhd ?
+ */
+ 
+static void i2o_block_biosparam(
+	unsigned long capacity,
+	unsigned short *cyls,
+	unsigned char *hds,
+	unsigned char *secs) 
+{ 
+	unsigned long heads, sectors, cylinders, temp; 
+
+	cylinders = 1024L;			/* Set number of cylinders to max */ 
+	sectors = 62L;      			/* Maximize sectors per track */ 
+
+	temp = cylinders * sectors;		/* Compute divisor for heads */ 
+	heads = capacity / temp;		/* Compute value for number of heads */
+	if (capacity % temp) {			/* If no remainder, done! */ 
+    		heads++;                	/* Else, increment number of heads */ 
+    		temp = cylinders * heads;	/* Compute divisor for sectors */ 
+    		sectors = capacity / temp;	/* Compute value for sectors per
+						       track */ 
+	    	if (capacity % temp) {		/* If no remainder, done! */ 
+			sectors++;                  /* Else, increment number of sectors */ 
+	      		temp = heads * sectors;	/* Compute divisor for cylinders */
+	      		cylinders = capacity / temp;/* Compute number of cylinders */ 
+		} 
+	} 
+	/* if something went wrong, then apparently we have to return
+	   a geometry with more than 1024 cylinders */
+	if (cylinders == 0 || heads > 255 || sectors > 63 || cylinders >1023) 
+	{
+		unsigned long temp_cyl;
+		
+		heads = 64;
+		sectors = 32;
+		temp_cyl = capacity / (heads * sectors);
+		if (temp_cyl > 1024) 
+		{
+			heads = 255;
+			sectors = 63;
+		}
+		cylinders = capacity / (heads * sectors);
+	}
+	*cyls = (unsigned int) cylinders;	/* Stuff return values */ 
+	*secs = (unsigned int) sectors; 
+	*hds  = (unsigned int) heads; 
+} 
+
+/*
+ *	Rescan the partition tables
+ */
+ 
+static int do_i2ob_revalidate(kdev_t dev, int maxu)
+{
+	int minor=MINOR(dev);
+	int i;
+	
+	minor&=0xF0;
+	
+	i2ob_dev[minor].refcnt++;
+	if(i2ob_dev[minor].refcnt>maxu+1)
+	{
+		i2ob_dev[minor].refcnt--;
+		return -EBUSY;
+	}
+	
+	for( i = 15; i>=0 ; i--)
+	{
+		int m = minor+i;
+		kdev_t d = MKDEV(MAJOR_NR, m);
+		struct super_block *sb = get_super(d);
+		
+		sync_dev(d);
+		if(sb)
+			invalidate_inodes(sb);
+		invalidate_buffers(d);
+		i2ob_gendisk.part[m].start_sect = 0;
+		i2ob_gendisk.part[m].nr_sects = 0;
+	}
+
+	/*
+	 *	Do a physical check and then reconfigure
+	 */
+	 
+	i2ob_install_device(i2ob_dev[minor].controller, i2ob_dev[minor].i2odev,
+		minor);
+	i2ob_dev[minor].refcnt--;
+	return 0;
+}
+
+/*
+ *	Issue device specific ioctl calls.
+ */
+
+static int i2ob_ioctl(struct inode *inode, struct file *file,
+		     unsigned int cmd, unsigned long arg)
+{
+	struct i2ob_device *dev;
+	int minor;
+
+	/* Anyone capable of this syscall can do *real bad* things */
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!inode)
+		return -EINVAL;
+	minor = MINOR(inode->i_rdev);
+	if (minor >= (MAX_I2OB<<4))
+		return -ENODEV;
+
+	dev = &i2ob_dev[minor];
+	switch (cmd) {
+		case BLKRASET:
+			if(!capable(CAP_SYS_ADMIN))  return -EACCES;
+			if(arg > 0xff) return -EINVAL;
+			read_ahead[MAJOR(inode->i_rdev)] = arg;
+			return 0;
+
+		case BLKRAGET:
+			if (!arg)  return -EINVAL;
+			return put_user(read_ahead[MAJOR(inode->i_rdev)],
+					(long *) arg); 
+		case BLKGETSIZE:
+			return put_user(i2ob[minor].nr_sects, (long *) arg);
+
+		case BLKFLSBUF:
+			if(!capable(CAP_SYS_ADMIN))
+				return -EACCES;
+
+			fsync_dev(inode->i_rdev);
+			invalidate_buffers(inode->i_rdev);
+			return 0;
+			
+		case HDIO_GETGEO:
+		{
+			struct hd_geometry g;
+			int u=minor&0xF0;
+			i2o_block_biosparam(i2ob_sizes[u]<<1, 
+				&g.cylinders, &g.heads, &g.sectors);
+			g.start = i2ob[minor].start_sect;
+			return copy_to_user((void *)arg,&g, sizeof(g))?-EFAULT:0;
+		}
+	
+		case BLKRRPART:
+			if(!capable(CAP_SYS_ADMIN))
+				return -EACCES;
+			return do_i2ob_revalidate(inode->i_rdev,1);
+			
+		default:
+			return blk_ioctl(inode->i_rdev, cmd, arg);
+	}
+}
+
+/*
+ *	Issue UTIL_CLAIM messages
+ */
+ 
+static int i2ob_claim_device(struct i2ob_device *dev, int onoff)
+{
+	return i2o_issue_claim(dev->controller, dev->tid, i2ob_context, onoff, &dev->done_flag);
+}
+
+/*
+ *	Close the block device down
+ */
+ 
+static int i2ob_release(struct inode *inode, struct file *file)
+{
+	struct i2ob_device *dev;
+	int minor;
+
+	minor = MINOR(inode->i_rdev);
+	if (minor >= (MAX_I2OB<<4))
+		return -ENODEV;
+	sync_dev(inode->i_rdev);
+	dev = &i2ob_dev[(minor&0xF0)];
+	if (dev->refcnt <= 0)
+		printk(KERN_ALERT "i2ob_release: refcount(%d) <= 0\n", dev->refcnt);
+	dev->refcnt--;
+	if(dev->refcnt==0)
+	{
+		/*
+		 *	Flush the onboard cache on unmount
+		 */
+		u32 msg[5];
+		int *query_done = &dev->done_flag;
+		msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+		msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid;
+		msg[2] = i2ob_context|0x80000000;
+		msg[3] = (u32)query_done;
+		msg[4] = 60<<16;
+		i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+		/*
+		 *	Unlock the media
+		 */
+		msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+		msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid;
+		msg[2] = i2ob_context|0x80000000;
+		msg[3] = (u32)query_done;
+		msg[4] = -1;
+		i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+	
+		/*
+                 * Now unclaim the device.
+                 */
+                if (i2ob_claim_device(dev, 0)<0)
+                        printk(KERN_ERR "i2ob_release: controller rejected unclaim.\n");
+
+	}
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ *	Open the block device.
+ */
+ 
+static int i2ob_open(struct inode *inode, struct file *file)
+{
+	int minor;
+	struct i2ob_device *dev;
+	
+	if (!inode)
+		return -EINVAL;
+	minor = MINOR(inode->i_rdev);
+	if (minor >= MAX_I2OB<<4)
+		return -ENODEV;
+	dev=&i2ob_dev[(minor&0xF0)];
+
+	if(dev->refcnt++==0)
+	{ 
+		u32 msg[6];
+		int *query_done;
+		
+		
+		if(i2ob_claim_device(dev, 1)<0)
+		{
+			dev->refcnt--;
+			return -EBUSY;
+		}
+		
+		query_done = &dev->done_flag;
+		/*
+		 *	Mount the media if needed. Note that we don't use
+		 *	the lock bit. Since we have to issue a lock if it
+		 *	refuses a mount (quite possible) then we might as
+		 *	well just send two messages out.
+		 */
+		msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;		
+		msg[1] = I2O_CMD_BLOCK_MMOUNT<<24|HOST_TID<<12|dev->tid;
+		msg[2] = i2ob_context|0x80000000;
+		msg[3] = (u32)query_done;
+		msg[4] = -1;
+		msg[5] = 0;
+		i2o_post_wait(dev->controller, dev->tid, msg, 24, query_done,2);
+		/*
+		 *	Lock the media
+		 */
+		msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+		msg[1] = I2O_CMD_BLOCK_MLOCK<<24|HOST_TID<<12|dev->tid;
+		msg[2] = i2ob_context|0x80000000;
+		msg[3] = (u32)query_done;
+		msg[4] = -1;
+		i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+	}		
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ *	Issue a device query
+ */
+ 
+static int i2ob_query_device(struct i2ob_device *dev, int table, 
+	int field, void *buf, int buflen)
+{
+	return i2o_query_scalar(dev->controller, dev->tid, i2ob_context,
+		table, field, buf, buflen, &dev->done_flag);
+}
+
+
+/*
+ *	Install the I2O block device we found.
+ */
+ 
+static int i2ob_install_device(struct i2o_controller *c, struct i2o_device *d, int unit)
+{
+	u64 size;
+	u32 blocksize;
+	u32 limit;
+	u8 type;
+	u32 flags, status;
+	struct i2ob_device *dev=&i2ob_dev[unit];
+	int i;
+
+	/*
+	 *	Ask for the current media data. If that isn't supported
+	 *	then we ask for the device capacity data
+	 */
+	 
+	if(i2ob_query_device(dev, 0x0004, 1, &blocksize, 4) != 0
+	  || i2ob_query_device(dev, 0x0004, 0, &size, 8) !=0 )
+	{
+		i2ob_query_device(dev, 0x0000, 3, &blocksize, 4);
+		i2ob_query_device(dev, 0x0000, 4, &size, 8);
+	}
+	
+	i2ob_query_device(dev, 0x0000, 5, &flags, 4);
+	i2ob_query_device(dev, 0x0000, 6, &status, 4);
+	i2ob_sizes[unit] = (int)(size>>10);
+	i2ob_hardsizes[unit] = blocksize;
+	i2ob_gendisk.part[unit].nr_sects = i2ob_sizes[unit];
+
+	/* Setting this higher than 1024 breaks the symbios for some reason */
+		
+	limit=4096;	/* 8 deep scatter gather */
+
+	printk("Byte limit is %d.\n", limit);
+	
+	for(i=unit;i<=unit+15;i++)
+		i2ob_max_sectors[i]=(limit>>9);
+	
+	i2ob[unit].nr_sects = (int)(size>>9);
+
+	i2ob_query_device(dev, 0x0000, 0, &type, 1);
+	
+	sprintf(d->dev_name, "%s%c", i2ob_gendisk.major_name, 'a' + (unit>>4));
+
+	printk("%s: ", d->dev_name);
+	if(status&(1<<10))
+		printk("RAID ");
+	switch(type)
+	{
+		case 0: printk("Disk Storage");break;
+		case 4: printk("WORM");break;
+		case 5: printk("CD-ROM");break;
+		case 7:	printk("Optical device");break;
+		default:
+			printk("Type %d", type);
+	}
+	if(((flags & (1<<3)) && !(status & (1<<3))) ||
+	   ((flags & (1<<4)) && !(status & (1<<4))))
+	{
+		printk(" Not loaded.\n");
+		return 0;
+	}
+	printk(" %dMb, %d byte sectors",
+		(int)(size>>20), blocksize);
+	if(status&(1<<0))
+	{
+		u32 cachesize;
+		i2ob_query_device(dev, 0x0003, 0, &cachesize, 4);
+		cachesize>>=10;
+		if(cachesize>4095)
+			printk(", %dMb cache", cachesize>>10);
+		else
+			printk(", %dKb cache", cachesize);
+	}
+	printk(".\n");
+	printk("%s: Maximum sectors/read set to %d.\n", 
+		d->dev_name, i2ob_max_sectors[unit]);
+	resetup_one_dev(&i2ob_gendisk, unit>>4);
+	return 0;
+}
+
+static void i2ob_probe(void)
+{
+	int i;
+	int unit = 0;
+	int warned = 0;
+		
+	for(i=0; i< MAX_I2O_CONTROLLERS; i++)
+	{
+		struct i2o_controller *c=i2o_find_controller(i);
+		struct i2o_device *d;
+	
+		if(c==NULL)
+			continue;
+
+		for(d=c->devices;d!=NULL;d=d->next)
+		{
+			if(d->class!=I2O_CLASS_RANDOM_BLOCK_STORAGE)
+				continue;
+
+			if(unit<MAX_I2OB<<4)
+			{
+ 				/*
+                                 * Get the device and fill in the
+                                 * Tid and controller.
+                                 */
+                                struct i2ob_device *dev=&i2ob_dev[unit];
+       				dev->i2odev = d; 
+				dev->controller = c;
+                                dev->tid = d->id;
+ 
+                                /*
+                                 * Insure the device can be claimed
+                                 * before installing it.
+                                 */
+                                if(i2ob_claim_device(dev, 1)==0)
+                                {
+                                        printk(KERN_INFO "Claimed Dev %x Tid %d Unit %d\n",dev,dev->tid,unit);
+                                        i2ob_install_device(c,d,unit);
+                                        unit+=16;
+ 
+                                        /*
+                                         * Now that the device has been
+                                         * installed, unclaim it so that
+                                         * it can be claimed by either
+                                         * the block or scsi driver.
+                                         */
+                                        if (i2ob_claim_device(dev, 0)<0)
+                                                printk(KERN_INFO "Could not unclaim Dev %x Tid %d\n",dev,dev->tid);
+ 
+                                }
+                                else
+                                        printk(KERN_INFO "TID %d not claimed\n",dev->tid);
+			}
+			else
+			{
+				if(!warned++)
+					printk("i2o_block: too many controllers, registering only %d.\n", unit>>4);
+			}
+		}
+	}
+	i2ob_devices = unit;
+}
+
+/*
+ *	Have we seen a media change ?
+ */
+ 
+static int i2ob_media_change(kdev_t dev)
+{
+	int i=MINOR(dev);
+	i>>=4;
+	if(i2ob_media_change_flag[i])
+	{
+		i2ob_media_change_flag[i]=0;
+		return 1;
+	}
+	return 0;
+}
+
+static int i2ob_revalidate(kdev_t dev)
+{
+	return do_i2ob_revalidate(dev, 0);
+}
+
+static int i2ob_reboot_event(struct notifier_block *n, unsigned long code, void *p)
+{
+	int i;
+	
+	if(code != SYS_RESTART && code != SYS_HALT && code != SYS_POWER_OFF)
+		return NOTIFY_DONE;
+	for(i=0;i<MAX_I2OB;i++)
+	{
+		struct i2ob_device *dev=&i2ob_dev[(i<<4)];
+		
+		if(dev->refcnt!=0)
+		{
+			/*
+			 *	Flush the onboard cache on power down
+			 *	also unlock the media
+			 */
+			u32 msg[5];
+			int *query_done = &dev->done_flag;
+			msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+			msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid;
+			msg[2] = i2ob_context|0x80000000;
+			msg[3] = (u32)query_done;
+			msg[4] = 60<<16;
+			i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+			/*
+			 *	Unlock the media
+			 */
+			msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+			msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid;
+			msg[2] = i2ob_context|0x80000000;
+			msg[3] = (u32)query_done;
+			msg[4] = -1;
+			i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+		}
+	}	
+	return NOTIFY_DONE;
+}
+
+struct notifier_block i2ob_reboot_notifier =
+{
+	i2ob_reboot_event,
+	NULL,
+	0
+};
+
+static struct file_operations i2ob_fops =
+{
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	i2ob_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	i2ob_open,		/* open */
+	NULL,			/* flush */
+	i2ob_release,		/* release */
+	NULL,			/* fsync */
+	NULL,			/* fasync */
+	i2ob_media_change,	/* Media Change */
+	i2ob_revalidate,	/* Revalidate */
+	NULL			/* File locks */
+};
+
+/*
+ *	Partitioning
+ */
+ 
+static void i2ob_geninit(struct gendisk *gd)
+{
+}
+	
+static struct gendisk i2ob_gendisk = 
+{
+	MAJOR_NR,
+	"i2ohd",
+	4,
+	1<<4,
+	MAX_I2OB,
+	i2ob_geninit,
+	i2ob,
+	i2ob_sizes,
+	0,
+	NULL,
+	NULL
+};
+
+/*
+ * And here should be modules and kernel interface 
+ *  (Just smiley confuses emacs :-)
+ */
+
+#ifdef MODULE
+#define i2ob_init init_module
+#endif
+
+int i2ob_init(void)
+{
+	int i;
+
+	printk("I2O block device OSM v0.06. (C) 1999 Red Hat Software.\n");
+	
+	/*
+	 *	Register the block device interfaces
+	 */
+
+	if (register_blkdev(MAJOR_NR, "i2o_block", &i2ob_fops)) {
+		printk("Unable to get major number %d for i2o_block\n",
+		       MAJOR_NR);
+		return -EIO;
+	}
+#ifdef MODULE
+	printk("i2o_block: registered device at major %d\n", MAJOR_NR);
+#endif
+
+	/*
+	 *	Now fill in the boiler plate
+	 */
+	 
+	blksize_size[MAJOR_NR] = i2ob_blksizes;
+	hardsect_size[MAJOR_NR] = i2ob_hardsizes;
+	blk_size[MAJOR_NR] = i2ob_sizes;
+	max_sectors[MAJOR_NR] = i2ob_max_sectors;
+	
+	blk_dev[MAJOR_NR].request_fn = i2ob_request;
+	for (i = 0; i < MAX_I2OB << 4; i++) {
+		i2ob_dev[i].refcnt = 0;
+		i2ob_dev[i].flags = 0;
+		i2ob_dev[i].controller = NULL;
+		i2ob_dev[i].i2odev = NULL;
+		i2ob_dev[i].tid = 0;
+		i2ob_dev[i].head = NULL;
+		i2ob_dev[i].tail = NULL;
+		i2ob_blksizes[i] = 1024;
+		i2ob_max_sectors[i] = 2;
+	}
+	
+	/*
+	 *	Register the OSM handler as we will need this to probe for
+	 *	drives, geometry and other goodies.
+	 */
+
+	if(i2o_install_handler(&i2o_block_handler)<0)
+	{
+		unregister_blkdev(MAJOR_NR, "i2o_block");
+		printk(KERN_ERR "i2o_block: unable to register OSM.\n");
+		return -EINVAL;
+	}
+	i2ob_context = i2o_block_handler.context;	 
+
+	/*
+	 *	Finally see what is actually plugged in to our controllers
+	 */
+
+	i2ob_probe();
+	
+	register_reboot_notifier(&i2ob_reboot_notifier);
+	return 0;
+}
+
+#ifdef MODULE
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Red Hat Software");
+MODULE_DESCRIPTION("I2O Block Device OSM");
+
+void cleanup_module(void)
+{
+	struct gendisk **gdp;
+	
+	unregister_reboot_notifier(&i2ob_reboot_notifier);
+	
+	/*
+	 *	Flush the OSM
+	 */
+
+	i2o_remove_handler(&i2o_block_handler);
+		 
+	/*
+	 *	Return the block device
+	 */
+	if (unregister_blkdev(MAJOR_NR, "i2o_block") != 0)
+		printk("i2o_block: cleanup_module failed\n");
+	else
+		printk("i2o_block: module cleaned up.\n");
+
+	/*
+	 *	Why isnt register/unregister gendisk in the kernel ???
+	 */
+
+	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+		if (*gdp == &i2ob_gendisk)
+			break;
+
+}
+#endif
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/i2o_config.c linux/drivers/i2o/i2o_config.c
--- v2.3.5/linux/drivers/i2o/i2o_config.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/i2o_config.c	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,613 @@
+/*
+ *	I2O Configuration Interface Driver
+ *
+ *	(C) Copyright 1999   Red Hat Software
+ *	
+ *	Written by Alan Cox, Building Number Three Ltd
+ *
+ *      Modified 04/20/199 by Deepak Saxena
+ *         - Added basic ioctl() support
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ * 	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/i2o.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/spinlock.h>
+
+#include "i2o_proc.h"
+
+static int i2o_cfg_token = 0;
+static int i2o_cfg_context = -1;
+static void *page_buf;
+static void *i2o_buffer;
+static int i2o_ready;
+static int i2o_pagelen;
+static int i2o_error;
+static int cfg_inuse;
+static int i2o_eof;
+static spinlock_t i2o_config_lock = SPIN_LOCK_UNLOCKED;
+struct wait_queue *i2o_wait_queue;
+
+static int ioctl_getiops(unsigned long);
+static int ioctl_gethrt(unsigned long);
+static int ioctl_getlct(unsigned long);
+static int ioctl_parms(unsigned long, unsigned int);
+static int ioctl_html(unsigned long);
+static int ioctl_swdl(unsigned long);
+static int ioctl_swul(unsigned long);
+static int ioctl_swdel(unsigned long);
+
+/*
+ *	This is the callback for any message we have posted. The message itself
+ *	will be returned to the message pool when we return from the IRQ
+ *
+ *	This runs in irq context so be short and sweet.
+ */
+static void i2o_cfg_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *m)
+{
+	i2o_cfg_token = I2O_POST_WAIT_OK;
+
+	return;
+}
+
+/*
+ *	Each of these describes an i2o message handler. They are
+ *	multiplexed by the i2o_core code
+ */
+ 
+struct i2o_handler cfg_handler=
+{
+	i2o_cfg_reply,
+	"Configuration",
+	0
+};
+
+static long long cfg_llseek(struct file *file, long long offset, int origin)
+{
+	return -ESPIPE;
+}
+
+/* i2ocontroller/i2odevice/page/?data */
+
+static ssize_t cfg_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+	printk(KERN_INFO "i2o_config write not yet supported\n");
+
+	return 0;
+}
+
+/* To be written for event management support */
+static ssize_t cfg_read(struct file *file, char *buf, size_t count, loff_t *ptr)
+{
+	return 0;
+}
+
+static int cfg_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+	unsigned long arg)
+{
+	int ret;
+
+	/* Only 1 token, so lock... */
+	spin_lock(&i2o_config_lock);
+
+	switch(cmd)
+	{	
+		case I2OGETIOPS:
+			ret = ioctl_getiops(arg);
+			break;
+
+		case I2OHRTGET:
+			ret = ioctl_gethrt(arg);
+			break;
+
+		case I2OLCTGET:
+			ret = ioctl_getlct(arg);
+			break;
+
+		case I2OPARMSET:
+			ret = ioctl_parms(arg, I2OPARMSET);
+			break;
+
+		case I2OPARMGET:
+			ret = ioctl_parms(arg, I2OPARMGET);
+			break;
+
+		case I2OSWDL:
+			ret = ioctl_swdl(arg);
+			break;
+
+		case I2OSWUL:
+			ret = ioctl_swul(arg);
+			break;
+
+		case I2OSWDEL:
+			ret = ioctl_swdel(arg);
+			break;
+
+		case I2OHTML:
+			ret = ioctl_html(arg);
+			break;
+
+		default:
+			ret = -EINVAL;
+	}
+
+	spin_unlock(&i2o_config_lock);
+	return ret;
+}
+
+int ioctl_getiops(unsigned long arg)
+{
+	u8 *user_iop_table = (u8*)arg;
+	struct i2o_controller *c = NULL;
+	int i;
+	u8 foo[MAX_I2O_CONTROLLERS];
+
+	if(!access_ok(VERIFY_WRITE, user_iop_table,  MAX_I2O_CONTROLLERS))
+		return -EFAULT;
+
+	for(i = 0; i < MAX_I2O_CONTROLLERS; i++)
+	{
+		c = i2o_find_controller(i);
+		if(c)
+		{
+			printk(KERN_INFO "ioctl: iop%d found\n", i);
+			foo[i] = 1;
+			i2o_unlock_controller(c);
+		}
+		else
+		{
+			printk(KERN_INFO "ioctl: iop%d not found\n", i);
+			foo[i] = 0;
+		}
+	}
+
+	__copy_to_user(user_iop_table, foo, MAX_I2O_CONTROLLERS);
+	return 0;
+}
+
+int ioctl_gethrt(unsigned long arg)
+{
+	struct i2o_controller *c;
+	struct i2o_cmd_hrtlct *cmd = (struct i2o_cmd_hrtlct*)arg;
+	struct i2o_cmd_hrtlct kcmd;
+	pi2o_hrt hrt;
+	u32 msg[6];
+	u32 *workspace;
+	int len;
+	int token;
+	u32 reslen;
+	int ret = 0;
+
+	if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct)))
+		return -EFAULT;
+
+	if(get_user(reslen, kcmd.reslen) < 0)
+		return -EFAULT;
+
+	if(kcmd.resbuf == NULL)
+		return -EFAULT;
+
+	c = i2o_find_controller(kcmd.iop);
+	if(!c)
+		return -ENXIO;
+
+	workspace = kmalloc(8192, GFP_KERNEL);
+	hrt = (pi2o_hrt)workspace;
+	if(workspace==NULL)
+		return -ENOMEM;
+
+	memset(workspace, 0, 8192);
+
+	msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4;
+	msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID;
+	msg[2]= (u32)cfg_handler.context;
+	msg[3]= 0;
+	msg[4]= (0xD0000000 | 8192);
+	msg[5]= virt_to_phys(workspace);
+
+	token = i2o_post_wait(c, ADAPTER_TID, msg, 6*4, &i2o_cfg_token,2);
+	if(token == I2O_POST_WAIT_TIMEOUT)
+	{
+		kfree(workspace);
+		i2o_unlock_controller(c);
+		return -ETIMEDOUT;
+	}
+	i2o_unlock_controller(c);
+
+	len = 8 + ((hrt->entry_len * hrt->num_entries) << 2);
+	/* We did a get user...so assuming mem is ok...is this bad? */
+	put_user(len, kcmd.reslen);
+	if(len > reslen)
+		ret = -ENOBUFS;	
+	if(copy_to_user(kcmd.resbuf, (void*)hrt, len))
+		ret = -EINVAL;
+
+	kfree(workspace);
+	return ret;
+}
+
+int ioctl_getlct(unsigned long arg)
+{
+	struct i2o_controller *c;
+	struct i2o_cmd_hrtlct *cmd = (struct i2o_cmd_hrtlct*)arg;
+	struct i2o_cmd_hrtlct kcmd;
+	pi2o_lct lct;
+	u32 msg[9];
+	u32 *workspace;
+	int len;
+	int token;
+	int ret = 0;
+	u32 reslen;
+
+	if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct)))
+		return -EFAULT;
+
+	if(get_user(reslen, kcmd.reslen) < 0)
+		return -EFAULT;
+
+	if(kcmd.resbuf == NULL)
+		return -EFAULT;
+
+	c = i2o_find_controller(kcmd.iop);
+	if(!c)
+		return -ENXIO;
+
+	workspace = kmalloc(8192, GFP_KERNEL);
+	lct = (pi2o_lct)workspace;
+	if(workspace==NULL)
+		return -ENOMEM;
+
+	memset(workspace, 0, 8192);
+
+	msg[0]= EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6;
+	msg[1]= I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID;
+	msg[2]= (u32)cfg_handler.context;
+	msg[3]= 0;
+	msg[4]= 0xFFFFFFFF;
+	msg[5]= 0;
+	msg[6]= (0xD0000000 | 8192);
+	msg[7]= virt_to_phys(workspace);
+
+	token = i2o_post_wait(c, ADAPTER_TID, msg, 8*4, &i2o_cfg_token,2);
+	if(token == I2O_POST_WAIT_TIMEOUT)
+	{
+		kfree(workspace);
+		i2o_unlock_controller(c);
+		return -ETIMEDOUT;
+	}
+	i2o_unlock_controller(c);
+
+	len = (unsigned int)lct->table_size << 2;
+	put_user(len, kcmd.reslen);
+	if(len > reslen)
+		ret = -ENOBUFS;	
+	else if(copy_to_user(kcmd.resbuf, (void*)lct, len))
+		ret = -EINVAL;
+
+	kfree(workspace);
+	return ret;
+}
+
+static int ioctl_parms(unsigned long arg, unsigned int type)
+{
+	int ret = 0;
+	struct i2o_controller *c;
+	struct i2o_cmd_psetget *cmd = (struct i2o_cmd_psetget*)arg;
+	struct i2o_cmd_psetget kcmd;
+	u32 msg[9];
+	u32 reslen;
+	int token;
+	u8 *ops;
+	u8 *res;
+	u16 *res16;	
+	u32 *res32;
+	u16 count;
+	int len;
+	int i,j;
+
+	u32 i2o_cmd = (type == I2OPARMGET ? 
+				I2O_CMD_UTIL_PARAMS_GET :
+				I2O_CMD_UTIL_PARAMS_SET);
+
+	if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_psetget)))
+		return -EFAULT;
+
+	if(get_user(reslen, kcmd.reslen))
+		return -EFAULT;
+
+	c = i2o_find_controller(kcmd.iop);
+	if(!c)
+		return -ENXIO;
+
+	ops = (u8*)kmalloc(kcmd.oplen, GFP_KERNEL);
+	if(!ops)
+		return -ENOMEM;
+
+	if(copy_from_user(ops, kcmd.opbuf, kcmd.oplen))
+	{
+		kfree(ops);
+		return -EFAULT;
+	}
+
+	/*
+	 * It's possible to have a _very_ large table
+	 * and that the user asks for all of it at once...
+	 */
+	res = (u8*)kmalloc(65536, GFP_KERNEL);
+	if(!res)
+	{
+		kfree(ops);
+		return -ENOMEM;
+	}
+
+	res16 = (u16*)res;
+
+	msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
+	msg[1]=i2o_cmd<<24|HOST_TID<<12|cmd->tid;
+	msg[2]=(u32)cfg_handler.context;
+	msg[3]=0;
+	msg[4]=0;
+	msg[5]=0x54000000|kcmd.oplen;
+	msg[6]=virt_to_bus(ops);
+	msg[7]=0xD0000000|(65536);
+	msg[8]=virt_to_bus(res);
+
+	/*
+	 * Parm set sometimes takes a little while for some reason
+	 */
+	token = i2o_post_wait(c, kcmd.tid, msg, 9*4, &i2o_cfg_token,10);
+	if(token == I2O_POST_WAIT_TIMEOUT)
+	{
+		kfree(ops);
+		kfree(res);
+		return -ETIMEDOUT;
+	}
+
+	kfree(ops);
+
+	/* 
+  	 * Determine required size...there's got to be a quicker way? 
+	 * Dump data to syslog for debugging failures
+	 */
+	count = res16[0];
+	printk(KERN_INFO "%0#6x\n%0#6x\n", res16[0], res16[1]);
+	len = 4;
+	res16 += 2;
+	for(i = 0; i < count; i++ )
+	{
+		len += res16[0] << 2;	/* BlockSize field in ResultBlock */
+		res32 = (u32*)res16;
+		for(j = 0; j < res16[0]; j++)
+			printk(KERN_INFO "%0#10x\n", res32[j]);
+		res16 += res16[0] << 1;	/* Shift to next block */
+	}
+
+	put_user(len, kcmd.reslen);
+	if(len > reslen)
+		ret = -ENOBUFS;
+	else if(copy_to_user(cmd->resbuf, res, len))
+		ret = -EFAULT;
+
+	kfree(res);
+
+	return ret;
+}
+
+int ioctl_html(unsigned long arg)
+{
+	struct i2o_html *cmd = (struct i2o_html*)arg;
+	struct i2o_html kcmd;
+	struct i2o_controller *c;
+	u8 *res = NULL;
+	void *query = NULL;
+	int ret = 0;
+	int token;
+	u32 len;
+	u32 reslen;
+	u32 msg[MSG_FRAME_SIZE/4];
+
+	if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_html)))
+	{
+		printk(KERN_INFO "i2o_config: can't copy html cmd\n");
+		return -EFAULT;
+	}
+
+	if(get_user(reslen, kcmd.reslen) < 0)
+	{
+		printk(KERN_INFO "i2o_config: can't copy html reslen\n");
+		return -EFAULT;
+	}
+
+	if(!kcmd.resbuf)		
+	{
+		printk(KERN_INFO "i2o_config: NULL html buffer\n");
+		return -EFAULT;
+	}
+
+	c = i2o_find_controller(kcmd.iop);
+	if(!c)
+		return -ENXIO;
+
+	if(kcmd.qlen) /* Check for post data */
+	{
+		query = kmalloc(kcmd.qlen, GFP_KERNEL);
+		if(!query)
+			return -ENOMEM;
+		if(copy_from_user(query, kcmd.qbuf, kcmd.qlen))
+		{
+			printk(KERN_INFO "i2o_config: could not get query\n");
+			kfree(query);
+			return -EFAULT;
+		}
+	}
+
+	res = kmalloc(4096, GFP_KERNEL);
+	if(!res)
+		return -ENOMEM;
+
+	msg[1] = (I2O_CMD_UTIL_CONFIG_DIALOG << 24)|HOST_TID<<12|kcmd.tid;
+	msg[2] = i2o_cfg_context;
+	msg[3] = 0;
+	msg[4] = kcmd.page;
+	msg[5] = 0xD0000000|4096;
+	msg[6] = virt_to_bus(res);
+	if(!kcmd.qlen) /* Check for post data */
+		msg[0] = SEVEN_WORD_MSG_SIZE|SGL_OFFSET_5;
+	else
+	{
+		msg[0] = NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
+		msg[5] = 0x50000000|4096;
+		msg[7] = 0xD4000000|(kcmd.qlen);
+		msg[8] = virt_to_phys(query);
+	}
+
+	token = i2o_post_wait(c, cmd->tid, msg, 9*4, &i2o_cfg_token, 10);
+	if(token == I2O_POST_WAIT_TIMEOUT)
+	{
+		kfree(res);
+		if(kcmd.qlen) kfree(query);
+
+		return -ETIMEDOUT;
+	}
+
+	len = strnlen(res, 8192);
+	put_user(len, kcmd.reslen);
+	if(len > reslen)
+		ret = -ENOMEM;
+	if(copy_to_user(kcmd.resbuf, res, len))
+		ret = -EFAULT;
+
+	kfree(res);
+	if(kcmd.qlen) 
+		kfree(query);
+
+	return ret;
+}
+
+/* To be written */
+int ioctl_swdl(unsigned long arg)
+{
+	return -ENOSYS;
+}
+
+/* To be written */
+int ioctl_swul(unsigned long arg)
+{
+	return -EINVAL;
+}
+
+/* To be written */
+int ioctl_swdel(unsigned long arg)
+{
+	return 0;
+}
+
+static int cfg_open(struct inode *inode, struct file *file)
+{
+	/* 
+         * Should support multiple management users
+         */
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int cfg_release(struct inode *inode, struct file *file)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+
+static struct file_operations config_fops =
+{
+	cfg_llseek,
+	cfg_read,
+	cfg_write,
+	NULL,
+	NULL /*cfg_poll*/,
+	cfg_ioctl,
+	NULL,		/* No mmap */
+	cfg_open,
+	NULL,		/* No flush */
+	cfg_release
+};
+
+static struct miscdevice i2o_miscdev = {
+	I2O_MINOR,
+	"i2octl",
+	&config_fops
+};	
+
+#ifdef MODULE
+int init_module(void)
+#else
+int i2o_config_init(void)
+#endif
+{
+	printk(KERN_INFO "i2o configuration manager v 0.02\n");
+	
+	if((page_buf = kmalloc(4096, GFP_KERNEL))==NULL)
+	{
+		printk(KERN_ERR "i2o_config: no memory for page buffer.\n");
+		return -ENOBUFS;
+	}
+	if(misc_register(&i2o_miscdev)==-1)
+	{
+		printk(KERN_ERR "i2o_config: can't register device.\n");
+		kfree(page_buf);
+		return -EBUSY;
+	}
+	/*
+	 *	Install our handler
+	 */
+	if(i2o_install_handler(&cfg_handler)<0)
+	{
+		kfree(page_buf);
+		printk(KERN_ERR "i2o_config: handler register failed.\n");
+		misc_deregister(&i2o_miscdev);
+		return -EBUSY;
+	}
+	/*
+	 *	The low 16bits of the transaction context must match this
+	 *	for everything we post. Otherwise someone else gets our mail
+	 */
+	i2o_cfg_context = cfg_handler.context;
+	return 0;
+}
+
+#ifdef MODULE
+
+void cleanup_module(void)
+{
+	misc_deregister(&i2o_miscdev);
+	
+	if(page_buf)
+		kfree(page_buf);
+	if(i2o_cfg_context != -1)
+		i2o_remove_handler(&cfg_handler);
+	if(i2o_buffer)
+		kfree(i2o_buffer);
+}
+ 
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Red Hat Software");
+MODULE_DESCRIPTION("I2O Configuration");
+
+#endif
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/i2o_core.c linux/drivers/i2o/i2o_core.c
--- v2.3.5/linux/drivers/i2o/i2o_core.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/i2o_core.c	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,2053 @@
+/*
+ *	Core I2O structure managment
+ *
+ *	(C) Copyright 1999   Red Hat Software
+ *	
+ *	Written by Alan Cox, Building Number Three Ltd
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ * 	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *
+ *	A lot of the I2O message side code from this is taken from the
+ *	Red Creek RCPCI45 adapter driver by Red Creek Communications
+ *
+ *	Some fixes and cleanup by Philipp Rumpf
+ *
+ *	Additional fixes by Juha Sievänen <Juha.Sievanen@cs.Helsinki.FI>
+ *	
+ */
+ 
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/i2o.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+
+#include <asm/io.h>
+#include <asm/spinlock.h>
+
+#include "i2o_lan.h"
+
+/*
+ *	Size of the I2O module table
+ */
+ 
+
+static struct i2o_handler *i2o_handlers[MAX_I2O_MODULES];
+static struct i2o_controller *i2o_controllers[MAX_I2O_CONTROLLERS];
+int i2o_num_controllers = 0;
+
+
+extern int i2o_online_controller(struct i2o_controller *c);
+
+/*
+ *	I2O configuration spinlock. This isnt a big deal for contention
+ *	so we have one only
+ */
+ 
+#ifdef __SMP__
+static spinlock_t i2o_configuration_lock = SPIN_LOCK_UNLOCKED;
+#endif
+
+/*
+ *	Install an I2O handler - these handle the asynchronous messaging
+ *	from the card once it has initialised.
+ */
+ 
+int i2o_install_handler(struct i2o_handler *h)
+{
+	int i;
+	spin_lock(&i2o_configuration_lock);
+	for(i=0;i<MAX_I2O_MODULES;i++)
+	{
+		if(i2o_handlers[i]==NULL)
+		{
+			h->context = i;
+			i2o_handlers[i]=h;
+			spin_unlock(&i2o_configuration_lock);
+			return 0;
+		}
+	}
+	spin_unlock(&i2o_configuration_lock);
+	return -ENOSPC;
+}
+
+int i2o_remove_handler(struct i2o_handler *h)
+{
+	i2o_handlers[h->context]=NULL;
+	return 0;
+}
+	
+
+/*
+ *	Each I2O controller has a chain of devices on it - these match
+ *	the useful parts of the LCT of the board.
+ */
+ 
+int i2o_install_device(struct i2o_controller *c, struct i2o_device *d)
+{
+	spin_lock(&i2o_configuration_lock);
+	d->controller=c;
+	d->owner=NULL;
+	d->next=c->devices;
+	c->devices=d;
+	*d->dev_name = 0;
+	spin_unlock(&i2o_configuration_lock);
+	return 0;
+}
+
+/* we need this version to call out of i2o_delete_controller */
+
+int __i2o_delete_device(struct i2o_device *d)
+{
+	struct i2o_device **p;
+
+	p=&(d->controller->devices);
+
+	/*
+	 *	Hey we have a driver!
+	 */
+	 
+	if(d->owner)
+		return -EBUSY;
+
+	/*
+	 *	Seek, locate
+	 */
+	 			
+	while(*p!=NULL)
+	{
+		if(*p==d)
+		{
+			/*
+			 *	Destroy
+			 */
+			*p=d->next;
+			kfree(d);
+			return 0;
+		}
+		p=&((*p)->next);
+	}
+	printk(KERN_ERR "i2o_delete_device: passed invalid device.\n");
+	return -EINVAL;
+}
+
+int i2o_delete_device(struct i2o_device *d)
+{
+	int ret;
+
+	spin_lock(&i2o_configuration_lock);
+
+	ret = __i2o_delete_device(d);
+
+	spin_unlock(&i2o_configuration_lock);
+
+	return ret;
+}
+
+/*
+ *	Add and remove controllers from the I2O controller list
+ */
+ 
+int i2o_install_controller(struct i2o_controller *c)
+{
+	int i;
+	spin_lock(&i2o_configuration_lock);
+	for(i=0;i<MAX_I2O_CONTROLLERS;i++)
+	{
+		if(i2o_controllers[i]==NULL)
+		{
+			i2o_controllers[i]=c;
+			c->next=i2o_controller_chain;
+			i2o_controller_chain=c;
+			c->unit = i;
+			sprintf(c->name, "i2o/iop%d", i);
+			i2o_num_controllers++;
+			spin_unlock(&i2o_configuration_lock);
+			return 0;
+		}
+	}
+	printk(KERN_ERR "No free i2o controller slots.\n");
+	spin_unlock(&i2o_configuration_lock);
+	return -EBUSY;
+}
+
+int i2o_delete_controller(struct i2o_controller *c)
+{
+	struct i2o_controller **p;
+	
+	spin_lock(&i2o_configuration_lock);
+	if(atomic_read(&c->users))
+	{
+		spin_unlock(&i2o_configuration_lock);
+		return -EBUSY;
+	}
+	while(c->devices)
+	{
+		if(__i2o_delete_device(c->devices)<0)
+		{
+			/* Shouldnt happen */
+			spin_unlock(&i2o_configuration_lock);
+			return -EBUSY;
+		}
+	}
+	c->destructor(c);
+	
+	p=&i2o_controller_chain;
+	
+	while(*p)
+	{
+		if(*p==c)
+		{
+			/* Prepare for restart */
+//			i2o_clear_controller(c);
+
+			*p=c->next;
+			spin_unlock(&i2o_configuration_lock);
+			if(c->page_frame);
+				kfree(c->page_frame);
+			i2o_controllers[c->unit]=NULL;
+			kfree(c);
+			i2o_num_controllers--;
+			return 0;
+		}
+		p=&((*p)->next);
+	}
+	spin_unlock(&i2o_configuration_lock);
+	printk(KERN_ERR "i2o_delete_controller: bad pointer!\n");
+	return -ENOENT;
+}
+
+void i2o_unlock_controller(struct i2o_controller *c)
+{
+	atomic_dec(&c->users);
+}
+
+struct i2o_controller *i2o_find_controller(int n)
+{
+	struct i2o_controller *c;
+	
+	if(n<0 || n>=MAX_I2O_CONTROLLERS)
+		return NULL;
+	
+	spin_lock(&i2o_configuration_lock);
+	c=i2o_controllers[n];
+	if(c!=NULL)
+		atomic_inc(&c->users);
+	spin_unlock(&i2o_configuration_lock);
+	return c;
+}
+	
+
+/*
+ *	Track if a device is being used by a driver
+ */
+ 
+int i2o_claim_device(struct i2o_device *d, struct i2o_driver *r)
+{
+	spin_lock(&i2o_configuration_lock);
+	if(d->owner)
+	{
+		spin_unlock(&i2o_configuration_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&d->controller->users);
+	d->owner=r;
+	spin_unlock(&i2o_configuration_lock);
+	return 0;
+}
+
+int i2o_release_device(struct i2o_device *d)
+{
+	spin_lock(&i2o_configuration_lock);
+	if(d->owner==NULL)
+	{
+		spin_unlock(&i2o_configuration_lock);
+		return -EINVAL;
+	}
+	atomic_dec(&d->controller->users);
+	d->owner=NULL;
+	spin_unlock(&i2o_configuration_lock);
+	return 0;
+}
+
+/*
+ *	This is called by the bus specific driver layer when an interrupt
+ *	or poll of this card interface is desired.
+ */
+ 
+void i2o_run_queue(struct i2o_controller *c)
+{
+	struct i2o_message *m;
+	u32 mv;
+	
+	while((mv=I2O_REPLY_READ32(c))!=0xFFFFFFFF)
+	{
+		struct i2o_handler *i;
+		m=(struct i2o_message *)bus_to_virt(mv);
+		/*
+		 *	Temporary Debugging
+		 */
+		if(((m->function_addr>>24)&0xFF)==0x15)
+			printk("UTFR!\n");
+//		printk("dispatching.\n");
+		i=i2o_handlers[m->initiator_context&(MAX_I2O_MODULES-1)];
+		if(i)
+			i->reply(i,c,m);
+		else
+			printk("Spurious reply\n");
+	 	i2o_flush_reply(c,mv);
+		mb();
+	}
+}
+
+
+/*
+ *	Do i2o class name lookup
+ */
+const char *i2o_get_class_name(int class)
+{
+	int idx = 16;
+	static char *i2o_class_name[] = {
+		"Executive",
+		"Device Driver Module",
+		"Block Device",
+		"Tape Device",
+		"LAN Inteface",
+		"WAN Interface",
+		"Fibre Channel Port",
+		"Fibre Channel Device",
+		"SCSI Device",
+		"ATE Port",
+		"ATE Device",
+		"Floppy Controller",
+		"Floppy Device",
+		"Secondary Bus Port",
+		"Peer Transport Agent",
+		"Peer Transport",
+		"Unknown"
+	};
+	
+	switch(class&0xFFF)
+	{
+		case I2O_CLASS_EXECUTIVE:
+			idx = 0; break;
+		case I2O_CLASS_DDM:
+			idx = 1; break;
+		case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+			idx = 2; break;
+		case I2O_CLASS_SEQUENTIAL_STORAGE:
+			idx = 3; break;
+		case I2O_CLASS_LAN:
+			idx = 4; break;
+		case I2O_CLASS_WAN:
+			idx = 5; break;
+		case I2O_CLASS_FIBRE_CHANNEL_PORT:
+			idx = 6; break;
+		case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL:
+			idx = 7; break;
+		case I2O_CLASS_SCSI_PERIPHERAL:
+			idx = 8; break;
+		case I2O_CLASS_ATE_PORT:
+			idx = 9; break;
+		case I2O_CLASS_ATE_PERIPHERAL:
+			idx = 10; break;
+		case I2O_CLASS_FLOPPY_CONTROLLER:
+			idx = 11; break;
+		case I2O_CLASS_FLOPPY_DEVICE:
+			idx = 12; break;
+		case I2O_CLASS_BUS_ADAPTER_PORT:
+			idx = 13; break;
+		case I2O_CLASS_PEER_TRANSPORT_AGENT:
+			idx = 14; break;
+		case I2O_CLASS_PEER_TRANSPORT:
+			idx = 15; break;
+	}
+
+	return i2o_class_name[idx];
+}
+
+
+/*
+ *	Wait up to 5 seconds for a message slot to be available.
+ */
+ 
+u32 i2o_wait_message(struct i2o_controller *c, char *why)
+{
+	long time=jiffies;
+	u32 m;
+	while((m=I2O_POST_READ32(c))==0xFFFFFFFF)
+	{
+		if((jiffies-time)>=5*HZ)
+		{
+			printk(KERN_ERR "%s: Timeout waiting for message to send %s.\n", 
+				c->name, why);
+			return 0xFFFFFFFF;
+		}
+		schedule();
+		barrier();
+	}
+	return m;
+}
+
+
+/*
+ *	Wait up to 5 seconds for a reply to be available.
+ */
+ 
+u32 i2o_wait_reply(struct i2o_controller *c, char *why, int timeout)
+{
+	u32 m;
+	long time=jiffies;
+	
+	while((m=I2O_REPLY_READ32(c))==0xFFFFFFFF)
+	{
+		if(jiffies-time >= timeout*HZ )
+		{
+			printk(KERN_ERR "%s: timeout waiting for %s reply.\n",
+				c->name, why);
+			return 0xFFFFFFFF;
+		}
+		schedule();
+	}
+	return m;
+}
+	
+
+
+/* Quiesce and clear IOP  */
+int i2o_quiesce_controller(struct i2o_controller *c)
+{
+	u32 m;
+	u32 *msg;
+
+	/* now we stop receiving messages to this IOP */
+	m=i2o_wait_message(c, "Quiesce IOP");
+	if(m==0xFFFFFFFF)
+		return -ETIMEDOUT;
+
+	msg=(u32 *)(c->mem_offset+m);
+
+	msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+	msg[1]=I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID;
+	msg[2]=0;
+	msg[3]=0;
+
+	printk(KERN_DEBUG "Sending SysQuiesce to %s\n", c->name);
+	i2o_post_message(c,m);
+
+	m=i2o_wait_reply(c, "System Quiesce", 20);
+
+	if (m==0xFFFFFFFF)
+		return -ETIMEDOUT;
+	/* Someday we should check return status... */
+
+	return 0;
+}
+
+int i2o_clear_controller(struct i2o_controller *c)
+{
+	u32 m;
+	u32 *msg;
+
+	m=i2o_wait_message(c, "IOP Clear");
+	if (m==0xFFFFFFFF)
+		return -ETIMEDOUT;
+
+	msg=(u32 *)(c->mem_offset+m);
+
+	msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+	msg[1]=I2O_CMD_ADAPTER_CLEAR<<24|HOST_TID<<12|ADAPTER_TID;
+	msg[2]=0;
+	msg[3]=0;
+
+	printk(KERN_DEBUG "Sending IOPClear to %s\n", c->name);
+	i2o_post_message(c, m);
+
+	m=i2o_wait_reply(c, "IOP Clear timeout", 5);
+
+	if(m==0xFFFFFFFF)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+
+/*
+ *	i2o table walking. We just provide a single element retrieve. You can
+ *	all sorts of fancy lookups in I2O but we have no performance critical
+ *	lookups so why write all the code for it.
+ */
+ 
+#if 0
+static int i2o_query_table_polled(struct i2o_controller *c, int tid, void *buf, int buflen, 
+	int group, int field, u32 *key, int keylen)
+{
+	u32 m;
+	u32 *msg;
+	u16 op[64];
+	u32 *p;
+	int i;
+	u32 *rbuf;
+
+	op[0]=1;			/* One Operation */
+	op[1]=0;			/* PAD */
+	op[2]=2;			/* LIST_GET */
+	op[3]=group;			/* group number */
+	op[4]=1;			/* 1 field */
+	op[5]=field;			/* Field number */
+	op[6]=1;			/* Key count */
+	memcpy(op+7, key, keylen);	/* Key */
+	
+	m=i2o_wait_message(c, "I2O query table.");
+	if(m==0xFFFFFFFF)
+	{	
+		return -ETIMEDOUT;
+	}
+	
+	msg=(u32 *)(c->mem_offset+m);
+	
+	rbuf=kmalloc(buflen+32, GFP_KERNEL);
+	if(rbuf==NULL)
+	{
+		printk(KERN_ERR "No free memory for table read.\n");
+		return -ENOMEM;
+	}
+	msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
+	msg[1]=I2O_CMD_UTIL_PARAMS_GET<<24|HOST_TID<<12|tid;
+	msg[2]=0;			/* Context */
+	msg[3]=0;
+	msg[4]=0;
+	msg[5]=0x54000000|(14);
+	msg[6]=virt_to_bus(op);
+	msg[7]=0xD0000000|(32+buflen);
+	msg[8]=virt_to_bus(rbuf);
+
+	i2o_post_message(c,m);
+	barrier();
+	
+	/*
+	 *	Now wait for a reply
+	 */
+	 
+	
+	m=i2o_wait_reply(c, "Table read timeout", 5);
+	
+	if(m==0xFFFFFFFF)
+	{
+		kfree(rbuf);
+		return -ETIMEDOUT;
+	}
+	
+	msg = (u32 *)bus_to_virt(m);
+
+	if(msg[4]>>24)
+	{
+		i2o_report_status(KERN_WARNING, "i2o_core",
+				  (msg[1]>>24)&0xFF, (msg[4]>>24)&0xFF,
+				  msg[4]&0xFFFF);
+	}
+	
+	p=rbuf;
+
+	/* Ok 'p' is the reply block - lets see what happened */
+	/* p0->p2 are the header */
+	
+	/* FIXME: endians - turn p3 to little endian */
+	
+	i=(p[0]&0xFFFF)<<2;		/* Message size */
+	if(i<buflen)
+		buflen=i;
+	
+	/* Do we have an error block ? */
+	if(p[0]&0xFF000000)
+	{
+		printk(KERN_ERR "%s: error in field read.\n",
+			c->name);
+		kfree(rbuf);
+		return -EBADR;
+	}
+		
+	/* p[1] holds the more flag and row count - we dont care */
+	
+	/* Ok it worked p[2]-> hold the data */
+	memcpy(buf,  p+2, buflen);
+	
+	kfree(rbuf);
+	
+	/* Finally return the message */
+	I2O_REPLY_WRITE32(c,m);
+	return buflen;
+}
+#endif
+
+static int i2o_query_scalar_polled(struct i2o_controller *c, int tid, void *buf, int buflen, 
+	int group, int field)
+{
+	u32 m;
+	u32 *msg;
+	u16 op[8];
+	u32 *p;
+	int i;
+	u32 *rbuf;
+
+	op[0]=1;			/* One Operation */
+	op[1]=0;			/* PAD */
+	op[2]=1;			/* FIELD_GET */
+	op[3]=group;			/* group number */
+	op[4]=1;			/* 1 field */
+	op[5]=field;			/* Field number */
+
+	m=i2o_wait_message(c, "I2O query scalar.");
+	if(m==0xFFFFFFFF)
+	{	
+		return -ETIMEDOUT;
+	}
+	
+	msg=(u32 *)(c->mem_offset+m);
+	
+	rbuf=kmalloc(buflen+32, GFP_KERNEL);
+	if(rbuf==NULL)
+	{
+		printk(KERN_ERR "No free memory for scalar read.\n");
+		return -ENOMEM;
+	}
+
+	msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
+	msg[1]=I2O_CMD_UTIL_PARAMS_GET<<24|HOST_TID<<12|tid;
+	msg[2]=0;			/* Context */
+	msg[3]=0;
+	msg[4]=0;
+	msg[5]=0x54000000|12;
+	msg[6]=virt_to_bus(op);
+	msg[7]=0xD0000000|(32+buflen);
+	msg[8]=virt_to_bus(rbuf);
+
+	i2o_post_message(c,m);
+	barrier();
+	
+	/*
+	 *	Now wait for a reply
+	 */
+	 
+	
+	m=i2o_wait_reply(c, "Scalar read timeout", 5);
+	
+	if(m==0xFFFFFFFF)
+	{
+		kfree(rbuf);
+		return -ETIMEDOUT;
+	}
+	 
+	msg = (u32 *)bus_to_virt(m);
+	if(msg[4]>>24)
+	{
+		i2o_report_status(KERN_WARNING, "i2o_core",
+				  (msg[1]>>24)&0xFF, (msg[4]>>24)&0xFF,
+				  msg[4]&0xFFFF);
+	}
+	
+	p=rbuf;
+
+	/* Ok 'p' is the reply block - lets see what happened */
+	/* p0->p2 are the header */
+	
+	/* FIXME: endians - turn p3 to little endian */
+	
+	if((p[0]&0xFFFF)!=1)	
+		printk(KERN_WARNING "Suspicious field read return 0x%08X\n", p[0]);
+		
+	i=(p[1]&0xFFFF)<<2;		/* Message size */
+	if(i<buflen)
+		buflen=i;
+	
+	/* Do we have an error block ? */
+	if(p[1]&0xFF000000)
+	{
+		printk(KERN_ERR "%s: error in field read.\n",
+			c->name);
+		kfree(rbuf);
+		return -EBADR;
+	}
+		
+	/* p[1] holds the more flag and row count - we dont care */
+	
+	/* Ok it worked p[2]-> hold the data */
+	memcpy(buf,  p+2, buflen);
+	
+	kfree(rbuf);
+	
+	/* Finally return the message */
+	I2O_REPLY_WRITE32(c,m);
+	return buflen;
+}
+
+/*
+ *	 Dump the information block associated with a given unit (TID)
+ */
+ 
+void i2o_report_controller_unit(struct i2o_controller *c, int unit)
+{
+	char buf[64];
+	
+	if(i2o_query_scalar_polled(c, unit, buf, 16, 0xF100, 3)>=0)
+	{
+		buf[16]=0;
+		printk(KERN_INFO "     Vendor: %s\n", buf);
+	}
+	if(i2o_query_scalar_polled(c, unit, buf, 16, 0xF100, 4)>=0)
+	{
+		buf[16]=0;
+		printk(KERN_INFO "     Device: %s\n", buf);
+	}
+#if 0
+	if(i2o_query_scalar_polled(c, unit, buf, 16, 0xF100, 5)>=0)
+	{
+		buf[16]=0;
+		printk(KERN_INFO "Description: %s\n", buf);
+	}
+#endif	
+	if(i2o_query_scalar_polled(c, unit, buf, 8, 0xF100, 6)>=0)
+	{
+		buf[8]=0;
+		printk(KERN_INFO "        Rev: %s\n", buf);
+	}
+}
+
+
+/*
+ *	Parse the hardware resource table. Right now we print it out
+ *	and don't do a lot with it. We should collate these and then
+ *	interact with the Linux resource allocation block.
+ *
+ *	Lets prove we can read it first eh ?
+ *
+ *	This is full of endianisms!
+ */
+ 
+static int i2o_parse_hrt(struct i2o_controller *c, u8 *p)
+{
+	u32 *rows=(u32 *)p;
+	u8 *d;
+	int count;
+	int length;
+	int i;
+	int state;
+	
+	if(p[3]!=0)
+	{
+		printk(KERN_ERR "i2o: HRT table for controller is too new a version.\n");
+		return -1;
+	}
+		
+	count=p[0]|(p[1]<<8);
+	length = p[2];
+	
+	printk(KERN_INFO "HRT has %d entries of %d bytes each.\n",
+		count, length<<2);
+
+	rows+=2;
+	
+	for(i=0;i<count;i++)
+	{
+		printk(KERN_INFO "Adapter %08X: ", rows[0]);
+		p=(u8 *)(rows+1);
+		d=(u8 *)(rows+2);
+		state=p[1]<<8|p[0];
+		
+		printk("TID %04X:[", state&0xFFF);
+		state>>=12;
+		if(state&(1<<0))
+			printk("H");		/* Hidden */
+		if(state&(1<<2))
+		{
+			printk("P");		/* Present */
+			if(state&(1<<1))
+				printk("C");	/* Controlled */
+		}
+		if(state>9)
+			printk("*");		/* Hard */
+		
+		printk("]:");
+		
+		switch(p[3]&0xFFFF)
+		{
+			case 0:
+				/* Adapter private bus - easy */
+				printk("Local bus %d: I/O at 0x%04X Mem 0x%08X", 
+					p[2], d[1]<<8|d[0], *(u32 *)(d+4));
+				break;
+			case 1:
+				/* ISA bus */
+				printk("ISA %d: CSN %d I/O at 0x%04X Mem 0x%08X",
+					p[2], d[2], d[1]<<8|d[0], *(u32 *)(d+4));
+				break;
+					
+			case 2: /* EISA bus */
+				printk("EISA %d: Slot %d I/O at 0x%04X Mem 0x%08X",
+					p[2], d[3], d[1]<<8|d[0], *(u32 *)(d+4));
+				break;
+
+			case 3: /* MCA bus */
+				printk("MCA %d: Slot %d I/O at 0x%04X Mem 0x%08X",
+					p[2], d[3], d[1]<<8|d[0], *(u32 *)(d+4));
+				break;
+
+			case 4: /* PCI bus */
+				printk("PCI %d: Bus %d Device %d Function %d",
+					p[2], d[2], d[1], d[0]);
+				break;
+
+			case 0x80: /* Other */
+			default:
+				printk("Unsupported bus type.");
+				break;
+		}
+		printk("\n");
+		rows+=length;
+	}
+	return 0;
+}
+	
+/*
+ *	The logical configuration table tells us what we can talk to
+ *	on the board. Most of the stuff isn't interesting to us. 
+ */
+
+static int i2o_parse_lct(struct i2o_controller *c, u32 *lct)
+{
+	int i;
+	int max;
+	int tid;
+	u32 *p;
+	struct i2o_device *d;
+	char str[22];
+
+	max=lct[0]&0xFFFF;
+	
+	max-=3;
+	max/=9;
+	
+	printk(KERN_INFO "LCT has %d entries.\n", max);
+	
+	if(max > 128)
+	{
+		printk(KERN_INFO "LCT was truncated.\n");
+		max=128;
+	}
+	
+	if(lct[1]&(1<<0))
+		printk(KERN_WARNING "Configuration dialog desired.\n");
+		
+	p=lct+3;
+	
+	for(i=0;i<max;i++)
+	{
+		d = (struct i2o_device *)kmalloc(sizeof(struct i2o_device), GFP_KERNEL);
+		if(d==NULL)
+		{
+			printk("i2o_core: Out of memory for LCT data.\n");
+			return -ENOMEM;
+		}
+		
+		d->controller = c;
+		d->next = NULL;
+		
+		d->id = tid = (p[0]>>16)&0xFFF;
+		d->class = p[3]&0xFFF;
+		d->subclass = p[4]&0xFFF;
+		d->parent =  (p[5]>>12)&0xFFF;
+		d->flags = 0;
+		
+		printk(KERN_INFO "TID %d.\n", tid);
+
+		i2o_report_controller_unit(c, tid);
+		
+		i2o_install_device(c, d);
+		
+		printk(KERN_INFO "  Class: ");
+		
+		sprintf(str, "%-21s", i2o_get_class_name(d->class));
+		printk("%s", str);
+		
+		printk("  Subclass: 0x%03X   Flags: ",
+			d->subclass);
+			
+		if(p[2]&(1<<0))
+			printk("C");		// ConfigDialog requested
+		if(p[2]&(1<<1))
+			printk("M");		// Multi-user capable
+		if(!(p[2]&(1<<4)))
+			printk("P");		// Peer service enabled!
+		if(!(p[2]&(1<<5)))
+			printk("m");		// Mgmt service enabled!
+		printk("\n");
+		p+=9;
+	}
+	return 0;
+}
+
+#if 0
+/* Reset the IOP to sane state */
+/* I think we need handler for core (or executive class in I2O terms) */
+static int i2o_reset_adapter(struct i2o_controller *c)
+{
+	u32 m;
+	u8 *work8;
+	u32 *msg;
+	long time;
+
+	/* First stop extral operations */
+	m=i2o_wait_message(c, "quiesce IOP");
+	if(m==0xFFFFFFFF)
+		return -ETIMEDOUT;
+	
+	msg=(u32 *)(c->mem_offset+m);
+				
+	msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+	msg[1]=I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID;
+	msg[2]=0;
+	msg[3]=0;
+
+	i2o_post_message(c,m);
+
+	m=i2o_wait_reply(c, "System Quiesce timeout", 5);
+
+	if(m==0xFFFFFFFF)
+		return -ETIMEDOUT;
+
+	/* Then reset the IOP */
+	m=i2o_wait_message(c, "reset IOP");
+	if(m==0xFFFFFFFF)
+		return -ETIMEDOUT;
+
+	msg=(u32 *)(c->mem_offset+m);
+	
+	work8=(void *)kmalloc(4, GFP_KERNEL);
+	if(work8==NULL) {
+		printk(KERN_ERR "IOP reset failed - no free memory.\n");
+		return -ENOMEM;
+	}
+	
+	memset(work8, 0, 4);
+	
+	msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0;
+	msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID;
+	msg[2]=0;
+	msg[3]=0;
+	msg[4]=0;
+	msg[5]=0;
+	msg[6]=virt_to_phys(work8);
+	msg[7]=0;	/* 64bit host FIXME */
+
+	i2o_post_message(c,m);
+
+	/* Wait for a reply */
+	time=jiffies;
+
+	while(work8[0]==0x01) {
+		if((jiffies-time)>=5*HZ) {
+			printk(KERN_ERR "IOP reset timeout.\n");
+			kfree(work8);
+			return -ETIMEDOUT;
+		}
+		schedule();
+		barrier();
+	}
+
+	if (work8[0]==0x02)
+		printk(KERN_WARNING "IOP Reset rejected\n");
+
+	return 0;
+}
+#endif
+
+/*
+ *	Bring an I2O controller into HOLD state. See the 1.5
+ *	spec. Basically we go
+ *
+ *	Wait for the message queue to initialise. 
+ *	If it didnt -> controller is dead
+ *	
+ *	Send a get status using the message queue
+ *	Poll for a reply block 88 bytes long
+ *
+ *	Send an initialise outbound queue
+ *	Poll for a reply
+ *
+ *	Post our blank messages to the queue FIFO
+ *
+ *	Send GetHRT, Parse it
+ */
+
+int i2o_activate_controller(struct i2o_controller *c)
+{
+	long time;
+	u32 m;
+	u8 *workspace;
+	u32 *msg;
+	int i;
+	
+	printk(KERN_INFO "Configuring I2O controller at 0x%08X.\n", (u32)c->mem_phys);
+
+	/* First reset the IOP to sane state */
+//	i2o_reset_adapter(c)
+	
+	m=i2o_wait_message(c, "initialise");
+	if(m==0xFFFFFFFF)
+		return -ETIMEDOUT;
+
+	msg=(u32 *)(c->mem_offset+m);
+	
+	workspace = (void *)kmalloc(88, GFP_KERNEL);
+	if(workspace==NULL)
+	{
+		printk(KERN_ERR "IOP initialisation failed - no free memory.\n");
+		return -ENOMEM;
+	}
+	
+	memset(workspace, 0, 88);
+	
+	msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_0;
+	msg[1]=I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID;
+	msg[2]=0;
+	msg[3]=0;
+	msg[4]=0;
+	msg[5]=0;
+	msg[6]=virt_to_phys(workspace);
+	msg[7]=0;	/* 64bit host FIXME */
+	msg[8]=88;
+
+	i2o_post_message(c,m);
+
+	/*
+	 *	Wait for a reply
+	 */
+
+	time=jiffies;
+		 
+	while(workspace[87]!=0xFF)
+	{
+		if((jiffies-time)>=5*HZ)
+		{
+			printk(KERN_ERR "IOP get status timeout.\n");
+			kfree(workspace);
+			return -ETIMEDOUT;
+		}
+		schedule();
+		barrier();
+	}
+	
+	/*
+	 *	Ok the reply has arrived. Fill in the important stuff
+	 */
+	 
+	c->status = workspace[10];
+	c->i2oversion = (workspace[9]>>4)&0xFF;
+	c->inbound_size = (workspace[12]|(workspace[13]<<8))*4; /* 32bit words */
+	
+	/*
+	 *	If the board is running, reset it - we have no idea
+	 *	what kind of a mess the previous owner left it in.
+	 */
+	 
+//	if(c->status == ADAPTER_STATE_OPERATIONAL)
+//		i2o_reset_device(c);
+
+	
+	m=i2o_wait_message(c, "initqueue");
+	if(m==0xFFFFFFFF)
+	{	
+		kfree(workspace);
+		return -ETIMEDOUT;
+	}
+	
+	msg=(u32 *)(c->mem_offset+m);
+
+	msg[0]= EIGHT_WORD_MSG_SIZE| TRL_OFFSET_6;
+	msg[1]= I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID;
+	msg[2]= 0;
+	msg[3]= 0x0106;			/* Transaction context */
+	msg[4]= 4096;			/* Host page frame size */
+	msg[5]= MSG_FRAME_SIZE<<16|0x80;	/* Outbound msg frame size and Initcode */
+	msg[6]= 0xD0000004;		/* Simple SG LE, EOB */
+	msg[7]= virt_to_phys(workspace);
+	*((u32 *)workspace)=0;
+
+	/*
+	 *	Post it
+	 */
+
+	i2o_post_message(c,m);
+	
+	barrier();
+	
+	time=jiffies;
+	
+	while(workspace[0]!=I2O_CMD_OUTBOUND_INIT_COMPLETE)
+	{
+		if((jiffies-time)>=5*HZ)
+		{
+			printk(KERN_ERR "IOP outbound initialise failed.\n");
+			kfree(workspace);
+			return -ETIMEDOUT;
+		}
+		schedule();
+		barrier();
+	}
+	
+	kfree(workspace);
+
+	c->page_frame = kmalloc(MSG_POOL_SIZE, GFP_KERNEL);
+	if(c->page_frame==NULL)
+	{
+		printk(KERN_ERR "IOP init failed: no memory for message page.\n");
+		return -ENOMEM;
+	}
+	
+	m=virt_to_phys(c->page_frame);
+	
+	for(i=0; i< NMBR_MSG_FRAMES; i++)
+	{
+		I2O_REPLY_WRITE32(c,m);
+		mb();
+		m+=MSG_FRAME_SIZE;
+	}
+	
+	/*
+	 *	The outbound queue is initialised and loaded,
+	 *
+	 *	Now we need the Hardware Resource Table. We must ask for
+	 *	this next we can't issue random messages yet.
+	 */
+
+
+	workspace=kmalloc(2048, GFP_KERNEL);
+	if(workspace==NULL)
+	{
+		printk(KERN_ERR "IOP init failed; no memory.\n");
+		return -ENOMEM;
+	}
+	
+	m=i2o_wait_message(c, "I2O HRT timeout.");
+	if(m==0xFFFFFFFF)
+	{	
+		kfree(workspace);
+		return -ETIMEDOUT;
+	}
+	
+	msg=(u32 *)(c->mem_offset+m);
+
+	msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4;
+	msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID;
+	msg[2]= 0x0;
+	msg[3]= 0x0;				/* Transaction context */
+	msg[4]= (0xD0000000 | 2048);		/* Simple transaction , 2K */
+	msg[5]= virt_to_phys(workspace);	/* Dump it here */
+	*((u32 *)workspace)=0xFFFFFFFF;
+	
+	i2o_post_message(c,m);
+	
+	barrier();
+	
+	/*
+	 *	Now wait for a reply
+	 */
+	 
+	m=i2o_wait_reply(c, "HRT table", 5);
+	 
+	if(m==0xFFFFFFFF)
+	{
+		kfree(workspace);
+		return -ETIMEDOUT;
+	}
+	
+	msg=(u32 *)bus_to_virt(m);
+	
+	if(msg[4]>>24)
+	{
+		i2o_report_status(KERN_WARNING, "i2o_core",
+				  (msg[1]>>24)&0xFF, (msg[4]>>24)&0xFF,
+				  msg[4]&0xFFFF);
+	}
+	I2O_REPLY_WRITE32(c,m);
+	
+	i2o_parse_hrt(c, workspace);
+	
+	kfree(workspace);
+	
+	return i2o_online_controller(c);
+//	i2o_report_controller_unit(c, ADAPTER_TID);
+}
+
+
+/*
+ *	Bring a controller online. Needs completing for multiple controllers
+ */
+ 
+int i2o_online_controller(struct i2o_controller *c)
+{
+	u32 m;
+	u32 *msg;
+	u32 systab[32];
+	u32 privmem[2];
+	u32 privio[2];
+	u32 *workspace;
+	
+	systab[0]=1;
+	systab[1]=0;
+	systab[2]=0;
+	systab[3]=0;
+	systab[4]=0;			/* Organisation ID */
+	systab[5]=2;			/* Ident 2 for now */
+	systab[6]=0<<24|0<<16|I2OVERSION<<12|1; /* Memory mapped, IOPState, v1.5, segment 1 */
+	systab[7]=MSG_FRAME_SIZE>>2;	/* Message size */
+	systab[8]=0;			/* LastChanged */
+	systab[9]=0;			/* Should be IOP capabilities */
+	systab[10]=virt_to_phys(c->post_port);
+	systab[11]=0;
+	
+	privmem[0]=c->priv_mem;		/* Private memory space base address */
+	privmem[1]=c->priv_mem_size;
+	privio[0]=c->priv_io;		/* Private I/O address */
+	privio[1]=c->priv_io_size;
+	
+	m=i2o_wait_message(c, "SetSysTab");
+	if(m==0xFFFFFFFF)
+		return -ETIMEDOUT;
+	
+	/* Now we build the systab */
+	msg=(u32 *)(c->mem_offset+m);
+	
+	msg[0] = NINE_WORD_MSG_SIZE|SGL_OFFSET_6;
+	msg[1] = I2O_CMD_SYS_TAB_SET<<24 | HOST_TID<<12 | ADAPTER_TID;
+	msg[2] = 0;	/* Context not needed */
+	msg[3] = 0;
+	msg[4] = (1<<16)|(2<<12);	/* Host 1 I2O 2 */
+	msg[5] = 1;			/* Segment 1 */
+	
+	/*
+	 *	Scatter Gather List
+	 */
+
+	msg[6] = 0x54000000|48;	/* One table for now */
+	msg[7] = virt_to_phys(systab);
+	msg[8] = 0xD4000000|48;	/* One table for now */
+	msg[9] = virt_to_phys(privmem);
+/* 	msg[10] = virt_to_phys(privio); */
+	
+	i2o_post_message(c,m);
+
+	barrier();
+	
+	/*
+	 *	Now wait for a reply
+	 */
+	 
+	 
+	m=i2o_wait_reply(c, "Systab read", 5);
+		
+	if(m==0xFFFFFFFF)
+		return -ETIMEDOUT;
+	
+	msg=(u32 *)bus_to_virt(m);
+	
+	if(msg[4]>>24)
+	{
+		i2o_report_status(KERN_ERR, "i2o_core",
+				  (msg[1]>>24)&0xFF, (msg[4]>>24)&0xFF,
+				  msg[4]&0xFFFF);
+	}
+	I2O_REPLY_WRITE32(c,m);
+	
+	/*
+	 *	Finally we go online
+	 */
+	 
+	m=i2o_wait_message(c, "No message for SysEnable");
+	
+	if(m==0xFFFFFFFF)
+		return -ETIMEDOUT;
+	
+	msg=(u32 *)(c->mem_offset+m);
+	
+	msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+	msg[1] = I2O_CMD_SYS_ENABLE<<24 | HOST_TID<<12 | ADAPTER_TID;
+	msg[2] = 0;		/* Context not needed */
+	msg[3] = 0;
+
+	i2o_post_message(c,m);
+	
+	barrier();
+	
+	/*
+	 *	Now wait for a reply
+	 */
+	 
+	
+	m=i2o_wait_reply(c, "Enable", 240);
+
+	if(m==0xFFFFFFFF)
+		return -ETIMEDOUT;
+	
+	msg=(u32 *)bus_to_virt(m);
+	
+	if(msg[4]>>24)
+	{
+		i2o_report_status(KERN_ERR, "i2o_core",
+				  (msg[1]>>24)&0xFF, (msg[4]>>24)&0xFF,
+				  msg[4]&0xFFFF);
+	}
+	I2O_REPLY_WRITE32(c,m);
+	
+	/*
+	 *	Grab the LCT, see what is attached
+	 */
+	 
+	m=i2o_wait_message(c, "No message for LCT");
+	
+	if(m==0xFFFFFFFF)
+		return -ETIMEDOUT;
+	
+	msg=(u32 *)(c->mem_offset+m);
+	
+	
+	workspace = kmalloc(8192, GFP_KERNEL);
+	if(workspace==NULL)
+	{
+		msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+		msg[1]= HOST_TID<<12|ADAPTER_TID;	/* NOP */
+		i2o_post_message(c,m);
+		printk(KERN_ERR "No free memory for i2o controller buffer.\n");
+		return -ENOMEM;
+	}
+	
+	memset(workspace, 0, 8192);
+	
+	msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_6;
+	msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID;
+	msg[2] = 0;		/* Context not needed */
+	msg[3] = 0;
+	msg[4] = 0xFFFFFFFF;	/* All devices */
+	msg[5] = 0x00000000;	/* Report now */
+	msg[6] = 0xD0000000|8192;
+	msg[7] = virt_to_bus(workspace);
+	
+	i2o_post_message(c,m);
+	
+	barrier();
+
+	/*
+	 *	Now wait for a reply
+	 */
+	 
+	m=i2o_wait_reply(c, "LCT", 5);
+	
+	if(m==0xFFFFFFFF)
+	{
+		kfree(workspace);
+		return -ETIMEDOUT;
+	}
+	
+	msg=(u32 *)bus_to_virt(m);
+	
+	if(msg[4]>>24)
+	{
+		i2o_report_status(KERN_ERR, "i2o_core",
+				  (msg[1]>>24)&0xFF, (msg[4]>>24)&0xFF,
+				  msg[4]&0xFFFF);
+	}
+	
+	i2o_parse_lct(c, workspace);
+	kfree(workspace);
+	
+	I2O_REPLY_WRITE32(c,m);
+	
+	return 0;
+}
+
+/*
+ *	Run time support routines
+ */
+ 
+/*
+ *	Generic "post and forget" helpers. This is less efficient - we do
+ *	a memcpy for example that isnt strictly needed, but for most uses
+ *	this is simply not worth optimising
+ */
+
+int i2o_post_this(struct i2o_controller *c, int tid, u32 *data, int len)
+{
+	u32 m;
+	u32 *msg;
+       	unsigned long t=jiffies;
+
+	do
+	{
+		mb();
+		m = I2O_POST_READ32(c);
+	}
+	while(m==0xFFFFFFFF && (jiffies-t)<HZ);
+	
+	
+	if(m==0xFFFFFFFF)
+	{
+		printk(KERN_ERR "i2o: controller not responding.\n");
+		return -1;
+	}
+	msg = bus_to_virt(c->mem_offset + m);
+ 	memcpy(msg, data, len);
+	i2o_post_message(c,m);
+	return 0;
+}
+
+/*
+ *	Post a message and wait for a response flag to be set. This API will
+ *	change to use wait_queue's one day
+ */
+ 
+int i2o_post_wait(struct i2o_controller *c, int tid, u32 *data, int len, int *flag, int timeout)
+{
+	unsigned long t=jiffies;
+	
+	*flag = 0;
+		
+	if(i2o_post_this(c, tid, data, len))
+		return -1;
+		
+	while(!*flag && (jiffies-t)<timeout*HZ)
+	{
+		schedule();
+		mb();
+	}
+	if(*flag <= 0)
+		return -1;
+	return 0;
+}
+
+/*
+ *	Issue UTIL_CLAIM messages
+ */
+ 
+int i2o_issue_claim(struct i2o_controller *c, int tid, int context, int onoff, int *flag)
+{
+	u32 msg[6];
+
+	msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
+	if(onoff)
+		msg[1] = I2O_CMD_UTIL_CLAIM << 24 | HOST_TID<<12 | tid;
+	else	
+		msg[1] = I2O_CMD_UTIL_RELEASE << 24 | HOST_TID << 12 | tid;
+	
+	/* The 0x80000000 convention for flagging is assumed by this helper */
+	
+	msg[2] = 0x80000000|context;
+	msg[3] = (u32)flag;
+	msg[4] = 0x01<<24;	/* Primary user */
+	
+	return i2o_post_wait(c, tid, msg, 20, flag,2);
+}
+
+/*
+ *	Query a scalar value
+ */
+
+int i2o_query_scalar(struct i2o_controller *c, int tid, int context,
+		     int group, int field, void *buf, int buflen, int *flag)
+{
+	u16 *op;
+	u32 *bl;
+	u32 msg[9];
+
+	bl=kmalloc(buflen+64, GFP_KERNEL); /* Enough space for error replys */
+	if(bl==NULL)
+	{
+		printk(KERN_ERR "i2o: no memory for query buffer.\n");
+		return -ENOMEM;
+	}
+
+	op = (u16*)bl;
+	op[0]=1;			/* One Operation */
+	op[1]=0;			/* PAD */
+	op[2]=1;			/* FIELD_GET */
+	op[3]=group;			/* group number */
+	op[4]=1;			/* field count, default = 1 */
+	op[5]=field;			/* field index */
+
+	if(field == -1)
+	/* Single value or the whole group? */
+	{
+		op[4]=-1;
+		op[5]=0;
+	}
+
+	msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
+	msg[1]=I2O_CMD_UTIL_PARAMS_GET<<24|HOST_TID<<12|tid;
+	msg[2]=context|0x80000000;	/* So we can pick it out */
+	msg[3]=(u32)flag;
+	msg[4]=0;
+	msg[5]=0x54000000|12;
+	msg[6]=virt_to_bus(bl);
+		/*
+		 *	There are 8 bytes of "overhead" required to pull in
+		 *	a Params ResultsList; 2 bytes for ResultCount
+		 *	(which should have value=1), plus 2 bytes for pad,
+		 *	plus 2 bytes for BlockSize, plus 1 byte BlockStatus,
+		 *	plus 1 byte ErrorInfoSize (8 bytes total overhead).
+		 *	This is followed finally by actual result value(s).
+		 *
+		 *	Tell the IOP to return 8 + buflen bytes.
+		 */
+	msg[7]=0xD0000000|(8+buflen);
+	msg[8]=virt_to_bus(bl+3);
+	
+	bl[3]=0xFCFCFCFC;		// Pad,ResultCount
+	bl[4]=0xFAFAFCFC;		// ErrorInfoSize,BlockStatus,BlockSize
+
+	/*
+	 *	Post the message and await a reply
+	 */
+
+	if (i2o_post_wait(c, tid, msg, sizeof(msg), flag,2) < 0)
+	{
+		kfree(bl);		
+		return -1;
+	}
+
+	if(bl[4]&0x00FF00000) /* BlockStatus != SUCCESS */
+	{
+		printk(KERN_WARNING "i2o_query_scalar - Error\n"
+			"ErrorInfoSize = 0x%02x, BlockStatus = 0x%02x, "
+			"BlockSize = 0x%04x\n", 
+			bl[4]>>24, (bl[4]>>16)&0xFF, bl[4]&0xFFFF);
+		kfree(bl);
+		return -1;
+	}
+	if((bl[3] & 0xFFFF) != 1)
+	{
+		printk(KERN_ERR "i2o: query ResultCount = 0x%04x\n", bl[3]&0xFFFF);
+	}
+	
+	memcpy(buf, bl+5, buflen);
+	kfree(bl);
+	return 0;
+}
+
+
+#if 0
+/* 
+ * Query a table field 
+ * FIXME: NOT TESTED! 
+ */
+int i2o_query_table(struct i2o_controller *c, int tid, int context,
+		    void *buf, int buflen,
+		    int table,
+		    int *field, int fieldlen,
+		    u32 *key, int keylen,
+		    int *flag)
+{
+	static u16 op[32];
+	u32 *bl;
+	u32 msg[9];
+	int i;
+
+	bl=kmalloc(buflen+64, GFP_KERNEL);
+	if(bl==NULL)
+	{
+		printk(KERN_ERR "i2o: no memory for query buffer.\n");
+		return -ENOMEM;
+	}
+
+	op[0]=1;			/* Operation count */
+	op[1]=0;			/* Reserved */
+	op[2]=I2O_PARAMS_LIST_GET;	/* Operation */
+	op[3]=table;			/* Group */
+	/* Specific fields or the whole group? */
+	if(*field != -1)
+	{ /* FIXME: Fields can be variable size */
+		op[4]=fieldlen;
+		for (i=0; i < fieldlen; i++)
+			op[4+i]=field[i];
+	}
+	else
+	{
+		op[4]=-1;
+		op[5]=0;
+	}
+
+	memcpy(bl, op, 12);
+	       	
+	msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
+	msg[1]=I2O_CMD_UTIL_PARAMS_GET<<24|HOST_TID<<12|tid;
+	msg[2]=context|0x80000000;	/* So we can pick it out */
+	msg[3]=(u32)flag;
+	msg[4]=0;
+	msg[5]=0x54000000|12;
+	msg[6]=virt_to_bus(bl);
+
+	msg[7]=0xD0000000|(buflen+48);
+	msg[8]=virt_to_bus(bl+4);
+
+	/*
+	 *	Post the message and await a reply
+	 */
+
+	if(i2o_post_wait(c, tid, msg, sizeof(msg), flag,2)<0)
+		return -1;
+	
+	if(bl[5]&0x00FF00000)	/* BlockStatus != SUCCESS */
+	{
+		printk(KERN_WARNING "i2o_query_table - Error\n"
+			"ErrorInfoSize = 0x%02x, BlockStatus = 0x%02x, "
+			"BlockSize = 0x%04x\n", 
+			bl[5]>>24, (bl[5]>>16)&0xFF, bl[5]&0xFFFF);
+		kfree(bl);
+		return -1;
+	}
+
+	if((bl[4]&0xFFFF)!=1)
+		printk(KERN_ERR "i2o: query ResultCount = %0#4x\n",
+		       bl[4]&0xFFFF);
+
+	memcpy(buf, bl+6, buflen);
+	kfree(bl);
+	return 0;
+}
+#endif
+
+/*
+ * Set (for now) scalar value
+ *
+ * TODO: Add support for table groups
+ */
+
+int i2o_params_set(struct i2o_controller *c, int tid, int context, int table,
+		   int field, void *buf, int buflen, int *flag)
+{
+	static u16 opdata[]={1,0,6,0,1,4,0};
+	u32 *bl;
+	u32 msg[9];
+
+	bl=kmalloc(buflen+64, GFP_KERNEL);
+	if(bl==NULL)
+	{
+		printk(KERN_ERR "i2o: no memory for set buffer.\n");
+		return -ENOMEM;
+	}
+
+	opdata[3]=table;
+	/* Single value or the whole group? */
+	if(field != -1) {
+		opdata[4]=1;	
+		opdata[5]=field;
+		opdata[6]=*(u16 *)buf;
+	}
+	else {
+		opdata[4]=-1;
+		opdata[5]=0;
+	}
+
+	memcpy(bl, opdata, 14);
+
+	msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
+	msg[1]=I2O_CMD_UTIL_PARAMS_SET<<24|HOST_TID<<12|tid;
+	msg[2]=context|0x80000000;	/* So we can pick it out */
+	msg[3]=(u32)flag;
+	msg[4]=0;
+	msg[5]=0x54000000|14;
+	msg[6]=virt_to_bus(bl);
+	msg[7]=0xD0000000|(buflen+48);
+	msg[8]=virt_to_bus(bl+4);
+	
+	/* Post the message and wait for a reply */
+	if(i2o_post_wait(c, tid, msg, 36, flag, 5)<0)
+	{
+		kfree(bl);
+		return -1;
+	}
+
+	/* Perhaps we should check errors, eh? */
+	if(bl[5]&0x00FF00000)	/* BlockStatus != SUCCESS */
+	{
+		printk(KERN_WARNING "i2o_params_set - Error\n"
+			"ErrorInfoSize = %0#2x, BlockStatus = %0#2x, "
+			"BlockSize = %0#4x\n", 
+			bl[5]>>24, (bl[5]>>16)&0xFF, bl[5]&0xFFFF);
+		kfree(bl);
+		return -1;
+	}
+
+	if((bl[4] & 0xFFFF) != 1)
+	{
+		printk(KERN_ERR "i2o: params set ResultCount = %0#4x\n",
+		       bl[4]&0xFFFF);
+	}
+
+	kfree(bl);
+	return 0;
+}
+
+
+void report_common_status(u8 req_status)
+{
+	/* the following reply status strings are common to all classes */
+
+	static char *REPLY_STATUS[] = { 
+		"SUCCESS", 
+		"ABORT_DIRTY", 
+		"ABORT_NO_DATA_TRANSFER",
+		"ABORT_PARTIAL_TRANSFER",
+		"ERROR_DIRTY",
+		"ERROR_NO_DATA_TRANSFER",
+		"ERROR_PARTIAL_TRANSFER",
+		"PROCESS_ABORT_DIRTY",
+		"PROCESS_ABORT_NO_DATA_TRANSFER",
+		"PROCESS_ABORT_PARTIAL_TRANSFER",
+		"TRANSACTION_ERROR",
+		"PROGRESS_REPORT"	
+	};
+
+	if (req_status > I2O_REPLY_STATUS_PROGRESS_REPORT)
+		printk("%0#4x / ", req_status);
+	else
+		printk("%s / ", REPLY_STATUS[req_status]);
+	
+	return;
+}
+
+static void report_common_dsc(u16 detailed_status)
+{
+	/* The following detailed statuscodes are valid 
+	   - for executive class, utility class, DDM class and
+	   - for transaction error replies
+	*/	
+
+	static char *COMMON_DSC[] = { 
+		"SUCCESS",
+		"0x01",				// not used
+		"BAD_KEY",
+		"TCL_ERROR",
+		"REPLY_BUFFER_FULL",
+		"NO_SUCH_PAGE",
+		"INSUFFICIENT_RESOURCE_SOFT",
+		"INSUFFICIENT_RESOURCE_HARD",
+		"0x08",				// not used
+		"CHAIN_BUFFER_TOO_LARGE",
+		"UNSUPPORTED_FUNCTION",
+		"DEVICE_LOCKED",
+		"DEVICE_RESET",
+		"INAPPROPRIATE_FUNCTION",
+		"INVALID_INITIATOR_ADDRESS",
+		"INVALID_MESSAGE_FLAGS",
+		"INVALID_OFFSET",
+		"INVALID_PARAMETER",
+		"INVALID_REQUEST",
+		"INVALID_TARGET_ADDRESS",
+		"MESSAGE_TOO_LARGE",
+		"MESSAGE_TOO_SMALL",
+		"MISSING_PARAMETER",
+		"TIMEOUT",
+		"UNKNOWN_ERROR",
+		"UNKNOWN_FUNCTION",
+		"UNSUPPORTED_VERSION",
+		"DEVICE_BUSY",
+		"DEVICE_NOT_AVAILABLE"		
+	};
+
+	if (detailed_status > I2O_DSC_DEVICE_NOT_AVAILABLE)
+		printk("%0#4x.\n", detailed_status);
+	else
+		printk("%s.\n", COMMON_DSC[detailed_status]);
+
+	return;
+}
+
+void report_lan_dsc(u16 detailed_status)
+{
+	static char *LAN_DSC[] = {	// Lan detailed status code strings
+		"SUCCESS",
+		"DEVICE_FAILURE",
+		"DESTINATION_NOT_FOUND",
+		"TRANSMIT_ERROR",
+		"TRANSMIT_ABORTED",
+		"RECEIVE_ERROR",
+		"RECEIVE_ABORTED",
+		"DMA_ERROR",
+		"BAD_PACKET_DETECTED",
+		"OUT_OF_MEMORY",
+		"BUCKET_OVERRUN",
+		"IOP_INTERNAL_ERROR",
+		"CANCELED",
+		"INVALID_TRANSACTION_CONTEXT",
+		"DEST_ADDRESS_DETECTED",
+		"DEST_ADDRESS_OMITTED",
+		"PARTIAL_PACKET_RETURNED",
+		"TEMP_SUSPENDED_STATE"
+	};
+
+	if (detailed_status > I2O_LAN_DSC_TEMP_SUSPENDED_STATE)
+		printk("%0#4x.\n", detailed_status);
+	else
+		printk("%s.\n", LAN_DSC[detailed_status]);
+
+	return;	
+}
+
+static void report_util_cmd(u8 cmd)
+{
+	switch (cmd) {
+	case I2O_CMD_UTIL_NOP:
+		printk("UTIL_NOP, ");
+		break;			
+	case I2O_CMD_UTIL_ABORT:
+		printk("UTIL_ABORT, ");
+		break;
+	case I2O_CMD_UTIL_CLAIM:
+		printk("UTIL_CLAIM, ");
+		break;
+	case I2O_CMD_UTIL_RELEASE:
+		printk("UTIL_CLAIM_RELEASE, ");
+		break;
+	case I2O_CMD_UTIL_CONFIG_DIALOG:
+		printk("UTIL_CONFIG_DIALOG, ");
+		break;
+	case I2O_CMD_UTIL_DEVICE_RESERVE:
+		printk("UTIL_DEVICE_RESERVE, ");
+		break;
+	case I2O_CMD_UTIL_DEVICE_RELEASE:
+		printk("UTIL_DEVICE_RELEASE, ");
+		break;
+	case I2O_CMD_UTIL_ACK:
+		printk("UTIL_EVENT_ACKNOWLEDGE, ");
+		break;
+	case I2O_CMD_UTIL_EVT_REGISTER:
+		printk("UTIL_EVENT_REGISTER, ");
+		break;
+	case I2O_CMD_UTIL_LOCK:
+		printk("UTIL_LOCK, ");
+		break;
+	case I2O_CMD_UTIL_LOCK_RELEASE:
+		printk("UTIL_LOCK_RELEASE, ");
+		break;
+	case I2O_CMD_UTIL_PARAMS_GET:
+		printk("UTIL_PARAMS_GET, ");
+		break;
+	case I2O_CMD_UTIL_PARAMS_SET:
+		printk("UTIL_PARAMS_SET, ");
+		break;
+	case I2O_CMD_UTIL_REPLY_FAULT_NOTIFY:
+		printk("UTIL_REPLY_FAULT_NOTIFY, ");
+		break;
+	default:
+		printk("%0#2x, ",cmd);	
+	}
+
+	return;	
+}
+
+
+static void report_exec_cmd(u8 cmd)
+{
+	switch (cmd) {
+	case I2O_CMD_ADAPTER_ASSIGN:
+		printk("EXEC_ADAPTER_ASSIGN, ");
+		break;
+	case I2O_CMD_ADAPTER_READ:
+		printk("EXEC_ADAPTER_READ, ");
+		break;
+	case I2O_CMD_ADAPTER_RELEASE:
+		printk("EXEC_ADAPTER_RELEASE, ");
+		break;
+	case I2O_CMD_BIOS_INFO_SET:
+		printk("EXEC_BIOS_INFO_SET, ");
+		break;
+	case I2O_CMD_BOOT_DEVICE_SET:
+		printk("EXEC_BOOT_DEVICE_SET, ");
+		break;
+	case I2O_CMD_CONFIG_VALIDATE:
+		printk("EXEC_CONFIG_VALIDATE, ");
+		break;
+	case I2O_CMD_CONN_SETUP:
+		printk("EXEC_CONN_SETUP, ");
+		break;
+	case I2O_CMD_DDM_DESTROY:
+		printk("EXEC_DDM_DESTROY, ");
+		break;
+	case I2O_CMD_DDM_ENABLE:
+		printk("EXEC_DDM_ENABLE, ");
+		break;
+	case I2O_CMD_DDM_QUIESCE:
+		printk("EXEC_DDM_QUIESCE, ");
+		break;
+	case I2O_CMD_DDM_RESET:
+		printk("EXEC_DDM_RESET, ");
+		break;
+	case I2O_CMD_DDM_SUSPEND:
+		printk("EXEC_DDM_SUSPEND, ");
+		break;
+	case I2O_CMD_DEVICE_ASSIGN:
+		printk("EXEC_DEVICE_ASSIGN, ");
+		break;
+	case I2O_CMD_DEVICE_RELEASE:
+		printk("EXEC_DEVICE_RELEASE, ");
+		break;
+	case I2O_CMD_HRT_GET:
+		printk("EXEC_HRT_GET, ");
+		break;
+	case I2O_CMD_ADAPTER_CLEAR:
+		printk("EXEC_IOP_CLEAR, ");
+		break;
+	case I2O_CMD_ADAPTER_CONNECT:
+		printk("EXEC_IOP_CONNECT, ");
+		break;
+	case I2O_CMD_ADAPTER_RESET:
+		printk("EXEC_IOP_RESET, ");
+		break;
+	case I2O_CMD_LCT_NOTIFY:
+		printk("EXEC_LCT_NOTIFY, ");
+		break;
+	case I2O_CMD_OUTBOUND_INIT:
+		printk("EXEC_OUTBOUND_INIT, ");
+		break;
+	case I2O_CMD_PATH_ENABLE:
+		printk("EXEC_PATH_ENABLE, ");
+		break;
+	case I2O_CMD_PATH_QUIESCE:
+		printk("EXEC_PATH_QUIESCE, ");
+		break;
+	case I2O_CMD_PATH_RESET:
+		printk("EXEC_PATH_RESET, ");
+		break;
+	case I2O_CMD_STATIC_MF_CREATE:
+		printk("EXEC_STATIC_MF_CREATE, ");
+		break;
+	case I2O_CMD_STATIC_MF_RELEASE:
+		printk("EXEC_STATIC_MF_RELEASE, ");
+		break;
+	case I2O_CMD_STATUS_GET:
+		printk("EXEC_STATUS_GET, ");
+		break;
+	case I2O_CMD_SW_DOWNLOAD:
+		printk("EXEC_SW_DOWNLOAD, ");
+		break;
+	case I2O_CMD_SW_UPLOAD:
+		printk("EXEC_SW_UPLOAD, ");
+		break;
+	case I2O_CMD_SW_REMOVE:
+		printk("EXEC_SW_REMOVE, ");
+		break;
+	case I2O_CMD_SYS_ENABLE:
+		printk("EXEC_SYS_ENABLE, ");
+		break;
+	case I2O_CMD_SYS_MODIFY:
+		printk("EXEC_SYS_MODIFY, ");
+		break;
+	case I2O_CMD_SYS_QUIESCE:
+		printk("EXEC_SYS_QUIESCE, ");
+		break;
+	case I2O_CMD_SYS_TAB_SET:
+		printk("EXEC_SYS_TAB_SET, ");
+		break;
+	default:
+		printk("%02x, ",cmd);	
+	}
+
+	return;	
+}
+
+static void report_lan_cmd(u8 cmd)
+{
+	switch (cmd) {
+	case LAN_PACKET_SEND:
+		printk("LAN_PACKET_SEND, "); 
+		break;
+	case LAN_SDU_SEND:
+		printk("LAN_SDU_SEND, ");
+		break;
+	case LAN_RECEIVE_POST:
+		printk("LAN_RECEIVE_POST, ");
+		break;
+	case LAN_RESET:
+		printk("LAN_RESET, ");
+		break;
+	case LAN_SUSPEND:
+		printk("LAN_SUSPEND, ");
+		break;
+	default:
+		printk("%02x, ",cmd);	
+	}	
+
+	return;
+}
+
+/* TODO: Add support for other classes */
+void i2o_report_status(const char *severity, const char *module, u8 cmd,
+		       u8 req_status, u16 detailed_status)
+{
+	printk("%s", severity);
+	printk("%s: ", module);
+
+	if (cmd < 0x1F) { 			// Utility Class
+		report_util_cmd(cmd);
+		report_common_status(req_status);
+		report_common_dsc(detailed_status);
+		return;
+	}
+
+	if (cmd >= 0x30 && cmd <= 0x3F) {	// LAN class
+		report_lan_cmd(cmd);
+		report_common_status(req_status);		
+		report_lan_dsc(detailed_status);
+		return;
+	}
+	
+	if (cmd >= 0xA0 && cmd <= 0xEF) {	// Executive class
+		report_exec_cmd(cmd);
+		report_common_status(req_status);
+		report_common_dsc(detailed_status);
+		return;
+	}
+	
+	printk("%02x, %02x / %04x.\n", cmd, req_status, detailed_status);
+	return;
+}
+
+
+EXPORT_SYMBOL(i2o_install_handler);
+EXPORT_SYMBOL(i2o_remove_handler);
+EXPORT_SYMBOL(i2o_install_device);
+EXPORT_SYMBOL(i2o_delete_device);
+EXPORT_SYMBOL(i2o_quiesce_controller);
+EXPORT_SYMBOL(i2o_clear_controller);
+EXPORT_SYMBOL(i2o_install_controller);
+EXPORT_SYMBOL(i2o_delete_controller);
+EXPORT_SYMBOL(i2o_unlock_controller);
+EXPORT_SYMBOL(i2o_find_controller);
+EXPORT_SYMBOL(i2o_num_controllers);
+EXPORT_SYMBOL(i2o_claim_device);
+EXPORT_SYMBOL(i2o_release_device);
+EXPORT_SYMBOL(i2o_run_queue);
+EXPORT_SYMBOL(i2o_report_controller_unit);
+EXPORT_SYMBOL(i2o_activate_controller);
+EXPORT_SYMBOL(i2o_online_controller);
+EXPORT_SYMBOL(i2o_get_class_name);
+
+EXPORT_SYMBOL(i2o_query_scalar);
+EXPORT_SYMBOL(i2o_params_set);
+EXPORT_SYMBOL(i2o_post_this);
+EXPORT_SYMBOL(i2o_post_wait);
+EXPORT_SYMBOL(i2o_issue_claim);
+
+EXPORT_SYMBOL(i2o_report_status);
+EXPORT_SYMBOL(report_common_status);
+EXPORT_SYMBOL(report_lan_dsc);
+
+EXPORT_SYMBOL(i2o_wait_message);
+
+MODULE_AUTHOR("Red Hat Software");
+MODULE_DESCRIPTION("I2O Core");
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/i2o_lan.c linux/drivers/i2o/i2o_lan.c
--- v2.3.5/linux/drivers/i2o/i2o_lan.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/i2o_lan.c	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,853 @@
+/*
+ * 	linux/drivers/i2o/i2o_lan.c
+ *
+ *    	I2O LAN CLASS OSM 	Prototyping, May 7th 1999
+ *
+ *	(C) Copyright 1999 	University of Helsinki,
+ *				Department of Computer Science
+ *
+ *   	This code is still under development / test.
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.    
+ *
+ * 	Author: 	Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI>
+ *
+ *	Tested:		in FDDI environment (using SysKonnect's DDM)
+ *			in ETH environment (using Intel 82558 DDM proto)
+ *
+ *	TODO:		batch mode networking
+ *			- this one assumes that we always get one packet in a bucket
+ *			- we've not been able to test batch replies and batch receives
+ *			error checking / timeouts
+ *			- code/test for other LAN classes
+ */
+
+#include <linux/module.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/fddidevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/malloc.h>
+#include <linux/trdevice.h>
+#include <asm/io.h>
+
+#include <linux/errno.h>
+
+#include <linux/i2o.h>
+#include "i2o_lan.h"
+
+//#define DRIVERDEBUG
+#ifdef DRIVERDEBUG
+#define dprintk(s, args...) printk(s, ## args) 
+#else
+#define dprintk(s, args...)
+#endif
+
+#define MAX_LAN_CARDS 4
+static struct device *i2o_landevs[MAX_LAN_CARDS+1];
+static int unit = -1; 			/* device unit number */
+
+struct i2o_lan_local {
+	u8 unit;
+	struct i2o_device *i2o_dev;
+	int reply_flag; 		// needed by scalar/table queries
+	struct fddi_statistics stats;
+/*	first fields are same as in struct net_device_stats stats; */ 
+	unsigned short (*type_trans)(struct sk_buff *, struct device *);
+};
+
+/* function prototypes */
+static int i2o_lan_receive_post(struct device *dev);
+static int i2o_lan_receive_post_reply(struct device *dev, struct i2o_message *m);
+
+
+static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, 
+			  struct i2o_message *m)
+{  
+	u32 *msg = (u32 *)m;
+	u8 unit  = (u8)(msg[2]>>16); // InitiatorContext
+	struct device *dev = i2o_landevs[unit];
+
+#ifdef DRIVERDEBUG
+	i2o_report_status(KERN_INFO, "i2o_lan", msg[1]>>24, msg[4]>>24,
+			  msg[4]&0xFFFF);
+#endif
+    	if (msg[0] & (1<<13)) // Fail bit is set
+ 	{
+ 		printk(KERN_INFO "IOP failed to process the msg\n");
+		printk("From tid=%d to tid=%d",(msg[1]>>12)&0xFFF,msg[1]&0xFFF);
+		return;
+	}	
+
+	switch (msg[1] >> 24) {
+	case LAN_RECEIVE_POST: 
+		if (dev->start) 
+			i2o_lan_receive_post_reply(dev,m);
+		else { 
+			// we are getting unused buckets back
+			u8 trl_count  = msg[3] & 0x000000FF; 	
+			struct i2o_bucket_descriptor *bucket =
+				(struct i2o_bucket_descriptor *)&msg[6];
+			struct sk_buff *skb;
+			do {	
+				dprintk("Releasing unused bucket\n");
+				skb = (struct sk_buff *)bucket->context;
+				dev_kfree_skb(skb);
+				bucket++;
+			} while (--trl_count);
+		}			
+	break;
+
+	case LAN_PACKET_SEND:
+	case LAN_SDU_SEND: 
+	{
+		u8 trl_count  = msg[3] & 0x000000FF; 	
+		
+		if (msg[4] >> 24) 	// ReqStatus != SUCCESS
+		{
+			printk(KERN_WARNING "%s: ",dev->name); 
+			report_common_status(msg[4]>>24);
+			report_lan_dsc(msg[4]&0xFFFF);
+		}
+
+		do {	// The HDM has handled the outgoing packet
+			dev_kfree_skb((struct sk_buff *)msg[4 + trl_count]);
+			dprintk(KERN_INFO "%s: Request skb freed (trl_count=%d).\n",
+				dev->name,trl_count);
+		} while (--trl_count);
+		
+			dev->tbusy = 0;
+			mark_bh(NET_BH); /* inform upper layers */
+	}
+	break;	
+
+	default: 
+		if (msg[2] & 0x80000000)  	// reply to a util get/set
+		{	// flag for the i2o_post_wait
+	       		int *flag = (int *)msg[3];
+	       		// ReqStatus != I2O_REPLY_STATUS_SUCCESS
+	      		 *flag = (msg[4] >> 24) ? I2O_POST_WAIT_TIMEOUT 
+	       				        : I2O_POST_WAIT_OK ;
+		}
+	}
+}
+
+static struct i2o_handler i2o_lan_handler =
+{
+	i2o_lan_reply,
+	"I2O Lan OSM",
+	0		// context
+};
+static int lan_context;
+
+
+static int i2o_lan_receive_post_reply(struct device *dev, struct i2o_message *m)
+{
+	u32 *msg = (u32 *)m;
+	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+	struct i2o_bucket_descriptor *bucket = (struct i2o_bucket_descriptor *)&msg[6];
+	struct i2o_packet_info *packet;
+	
+	u8 trl_count  = msg[3] & 0x000000FF; 	
+	struct sk_buff *skb;
+
+#ifdef 0
+	dprintk(KERN_INFO "TrlFlags = 0x%02X, TrlElementSize = %d, TrlCount = %d\n"
+		"msgsize = %d, buckets_remaining = %d\n", 
+		msg[3]>>24, msg[3]&0x0000FF00, trl_count, msg[0]>>16, msg[5]);	
+#endif
+
+/* 
+ * NOTE: here we assume that also in batch mode we will get only 
+ * one packet per bucket. This can be ensured by setting the 
+ * PacketOrphanLimit to MaxPacketSize, as well as the bucket size.
+ */
+	do {
+		/* packet is not at all needed here */
+		packet = (struct i2o_packet_info *)bucket->packet_info;	
+#ifdef 0
+		dprintk(KERN_INFO "flags = 0x%02X, offset = 0x%06X, status = 0x%02X, length = %d\n",
+			packet->flags, packet->offset, packet->status, packet->len);
+#endif
+		skb = (struct sk_buff *)(bucket->context);
+		skb_put(skb,packet->len);
+		skb->dev  = dev;		
+		skb->protocol = priv->type_trans(skb, dev);
+		netif_rx(skb);
+
+		dprintk(KERN_INFO "%s: Incoming packet (%d bytes) delivered "
+			"to upper level.\n",dev->name,packet->len);
+			
+		bucket++; // to next Packet Descriptor Block
+
+	} while (--trl_count);
+
+	if (msg[5] <= I2O_BUCKET_THRESH)  	// BucketsRemaining
+		i2o_lan_receive_post(dev);
+
+	return 0;
+}
+
+/*  ====================================================
+ *  Interface to i2o:  functions to send lan class request 
+ */
+
+/* 
+ * i2o_lan_receive_post(): Post buckets to receive packets.
+ */
+static int i2o_lan_receive_post(struct device *dev)
+{	
+	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
+	struct i2o_device *i2o_dev = priv->i2o_dev;
+	struct i2o_controller *iop = i2o_dev->controller;
+	struct sk_buff *skb;
+	u32 m; u32 *msg;
+	
+	u32 bucket_len = (dev->mtu + dev->hard_header_len);
+	u32 bucket_count;
+	int n_elems = (iop->inbound_size - 16 ) / 12; // msg header + SGLs
+	u32 total = 0;
+	int i;
+
+	dprintk(KERN_INFO "%s: Allocating %d buckets (size %d).\n", 
+		dev->name, I2O_BUCKET_COUNT, bucket_len);
+
+	while (total < I2O_BUCKET_COUNT)
+	{
+		m = I2O_POST_READ32(iop);
+		if (m == 0xFFFFFFFF)
+			return -ETIMEDOUT;		
+		msg = bus_to_virt(iop->mem_offset + m);
+	
+		bucket_count = (total + n_elems < I2O_BUCKET_COUNT)
+			     ? n_elems
+			     : I2O_BUCKET_COUNT - total;
+			     
+		msg[0] = I2O_MESSAGE_SIZE(4 + 3 *  bucket_count) | 1<<12 | SGL_OFFSET_4;
+		msg[1] = LAN_RECEIVE_POST<<24 | HOST_TID<<12 | i2o_dev->id;
+		msg[2] = priv->unit << 16 | lan_context; // InitiatorContext	
+		msg[3] = bucket_count;			 // BucketCount
+
+		for (i = 0; i < bucket_count; i++)
+		{
+			skb = dev_alloc_skb(bucket_len + 2);
+			if (skb == NULL)
+				return -ENOMEM;
+			skb_reserve(skb, 2);
+			msg[4 + 3*i] = 0x51000000 | bucket_len;
+			msg[5 + 3*i] = (u32)skb;
+			msg[6 + 3*i] = virt_to_bus(skb->data);
+		}
+		msg[4 + 3*i - 3] |= 0x80000000; // set LE flag
+		i2o_post_message(iop,m);
+
+		dprintk(KERN_INFO "%s: Sending %d buckets (size %d) to LAN HDM.\n",
+			dev->name,bucket_count,bucket_len);
+
+		total += bucket_count;
+	}
+	return 0;	
+}	
+
+/* 
+ * i2o_lan_reset(): Reset the LAN adapter into the operational state and 
+ * 	restore it to full operation.
+ */
+static int i2o_lan_reset(struct device *dev) 
+{
+	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
+	struct i2o_device *i2o_dev = priv->i2o_dev;
+	struct i2o_controller *iop = i2o_dev->controller;	
+	u32 m; u32 *msg;
+
+	m = I2O_POST_READ32(iop);
+	if (m == 0xFFFFFFFF)
+		return -ETIMEDOUT;
+	msg = bus_to_virt(iop->mem_offset + m);
+
+	msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
+	msg[1] = LAN_RESET<<24 | HOST_TID<<12 | i2o_dev->id;
+	msg[2] = priv->unit << 16 | lan_context; // InitiatorContext
+	msg[3] = 0; 				 // TransactionContext
+	msg[4] = 1 << 16; 			 // return posted buckets
+
+	i2o_post_message(iop,m);
+
+	return 0; 
+}
+
+/* 
+ * i2o_lan_suspend(): Put LAN adapter into a safe, non-active state.
+ * 	Reply to any LAN class message with status error_no_data_transfer 
+ *	/ suspended.
+ */
+static int i2o_lan_suspend(struct device *dev)
+{
+	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
+	struct i2o_device *i2o_dev = priv->i2o_dev;
+	struct i2o_controller *iop = i2o_dev->controller;	
+	u32 m; u32 *msg;
+
+	m = I2O_POST_READ32(iop);
+	if (m == 0xFFFFFFFF)
+		return -ETIMEDOUT;
+	msg = bus_to_virt(iop->mem_offset + m);
+
+	msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
+	msg[1] = LAN_SUSPEND<<24 | HOST_TID<<12 | i2o_dev->id;
+	msg[2] = priv->unit << 16 | lan_context; // InitiatorContext
+	msg[3] = 0; 				 // TransactionContext
+	msg[4] = 1 << 16; 			 // return posted buckets
+
+	i2o_post_message(iop,m);
+
+	return 0;
+}
+
+/*
+ * Set DDM into batch mode.
+ */
+static void i2o_set_batch_mode(struct device *dev)
+{
+
+/* 
+ * NOTE: we have not been able to test batch mode
+ * 	 since HDMs we have, don't implement it 
+ */
+
+	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
+	struct i2o_device *i2o_dev = priv->i2o_dev;	
+	struct i2o_controller *iop = i2o_dev->controller;	
+	u32 val;
+
+	/* set LAN_BATCH_CONTROL attributes */
+
+	// enable batch mode, toggle automatically
+	val = 0x00000000;
+	if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0003, 0,
+			&val, 4, &priv->reply_flag) <0)
+		printk(KERN_WARNING "Unable to enter I2O LAN batch mode.\n");
+	else
+		dprintk(KERN_INFO "%s: I2O LAN batch mode enabled.\n",dev->name);
+
+	/*
+	 * When PacketOrphanlimit is same as the maximum packet length,
+	 * the packets will never be split into two separate buckets
+	 */
+
+	/* set LAN_OPERATION attributes */
+
+	val = dev->mtu + dev->hard_header_len; // PacketOrphanLimit
+	if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0004, 2,
+		&val, 4, &priv->reply_flag) < 0)
+		printk(KERN_WARNING "i2o_lan: Unable to set PacketOrphanLimit.\n");
+	else
+		dprintk(KERN_INFO "PacketOrphanLimit set to %d\n",val);
+
+#ifdef 0
+/*
+ * I2O spec 2.0: there should be proper default values for other attributes 
+ * used in batch mode.
+ */
+
+	/* set LAN_RECEIVE_INFO attributes */
+	
+	val = 10; // RxMaxBucketsReply
+	if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0008, 3,
+		&val, 4, &priv->reply_flag) < 0)
+		printk(KERN_WARNING "%s: Unable to set RxMaxBucketsReply.\n",
+			dev->name);
+
+	val = 10; // RxMaxPacketsBuckets
+	if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0008, 4,
+		&val, 4, &priv->reply_flag) < 0)
+		printk(KERN_WARNING "%s: Unable to set RxMaxPacketsBucket.\n",
+			dev->name);
+
+	/* set LAN_BATCH_CONTROL attributes */
+
+	val = 10; // MaxRxBatchCount
+	if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0003, 5,
+		&val, 4, &priv->reply_flag) < 0)
+		printk(KERN_WARNING "%s: Unable to set MaxRxBatchCount.\n",
+			dev->name);
+
+	val = 10; // MaxTxBatchCount
+	if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0003, 8,
+		&val, 4, &priv->reply_flag) < 0)
+		printk(KERN_WARNING "%s Unable to set MaxTxBatchCount.\n",
+			dev->name);
+#endif
+
+	return;	
+}
+
+/* 
+ * i2o_lan_open(): Open the device to send/receive packets via 
+ * the network device.	
+ */
+static int i2o_lan_open(struct device *dev)
+{
+	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
+	struct i2o_device *i2o_dev = priv->i2o_dev;	
+	struct i2o_controller *iop = i2o_dev->controller;
+
+	i2o_lan_reset(dev);
+	
+	if (i2o_issue_claim(iop, i2o_dev->id, lan_context, 1, 
+		&priv->reply_flag) < 0)
+	{
+		printk(KERN_WARNING "%s: Unable to claim the I2O LAN device.\n", dev->name);
+		return -EAGAIN;
+	}
+	dprintk(KERN_INFO "%s: I2O LAN device claimed (tid=%d).\n", dev->name, i2o_dev->id);
+
+	dev->tbusy = 0;
+	dev->start = 1;
+
+	i2o_set_batch_mode(dev);
+	i2o_lan_receive_post(dev);
+
+	MOD_INC_USE_COUNT;
+	
+	return 0;
+}
+
+/*
+ * i2o_lan_close(): End the transfering.
+ */ 
+static int i2o_lan_close(struct device *dev)
+{
+	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
+	struct i2o_device *i2o_dev = priv->i2o_dev;	
+	struct i2o_controller *iop = i2o_dev->controller;	
+
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	if (i2o_issue_claim(iop, i2o_dev->id, lan_context, 0, 
+	    &priv->reply_flag) < 0)
+	{
+		printk(KERN_WARNING "%s: Unable to unclaim I2O LAN device (tid=%d)\n",
+		       dev->name, i2o_dev->id);
+	}
+
+	i2o_lan_suspend(dev);
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+/* 
+ * i2o_lan_sdu_send(): Send a packet, MAC header added by the HDM.
+ * Must be supported by Fibre Channel, optional for Ethernet/802.3, 
+ * Token Ring, FDDI
+ */
+static int i2o_lan_sdu_send(struct sk_buff *skb, struct device *dev)
+{	
+#ifdef 0
+/* not yet tested */
+        struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; 
+        struct i2o_device *i2o_dev = priv->i2o_dev;     
+        struct i2o_controller *iop = i2o_dev->controller;
+        u32 m; u32 *msg;
+
+	dprintk(KERN_INFO "LanSDUSend called, skb->len = %d\n", skb->len);
+
+        m = *iop->post_port;
+        if (m == 0xFFFFFFFF)
+	{
+                dev_kfree_skb(skb);     
+                return -1;
+        }
+        msg = bus_to_virt(iop->mem_offset + m);
+
+        msg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_4;
+        msg[1] = LAN_SDU_SEND<<24 | HOST_TID<<12 | i2o_dev->id; 
+        msg[2] = priv->unit << 16 | lan_context; // IntiatorContext
+        msg[3] = 1<<4;		// TransmitControlWord: suppress CRC generation
+
+        // create a simple SGL, see fig. 3-26
+        // D7 = 1101 0111 = LE eob 0 1 LA dir bc1 bc0
+
+        msg[4] = 0xD7000000 | (skb->len);  // no MAC hdr included     
+        msg[5] = (u32)skb;		   // TransactionContext
+        memcpy(&msg[6], skb->data, 8);	   // Destination MAC Addr ??
+        msg[7] &= 0x0000FFFF;		   // followed by two bytes zeros
+        msg[8] = virt_to_bus(skb->data);
+        dev->trans_start = jiffies;
+        i2o_post_message(iop,m);
+
+	dprintk(KERN_INFO "%s: Packet (%d bytes) sent to network.\n",
+		dev->name,skb->len);
+#endif
+        return 0;
+}
+
+/* 
+ * i2o_lan_packet_send(): Send a packet as is, including the MAC header.
+ * 
+ * Must be supported by Ethernet/802.3, Token Ring, FDDI, optional for 
+ * Fibre Channel
+ */ 
+static int i2o_lan_packet_send(struct sk_buff *skb, struct device *dev)
+{
+	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
+	struct i2o_device *i2o_dev = priv->i2o_dev;	
+	struct i2o_controller *iop = i2o_dev->controller;
+	u32 m; u32 *msg;
+
+	m = *iop->post_port;
+	if (m == 0xFFFFFFFF) {
+		dev_kfree_skb(skb);
+		return -1;
+	}
+
+	msg = bus_to_virt(iop->mem_offset + m);
+
+	msg[0] = SEVEN_WORD_MSG_SIZE | 1<<12 | SGL_OFFSET_4;
+	msg[1] = LAN_PACKET_SEND<<24 | HOST_TID<<12 | i2o_dev->id;	
+	msg[2] = priv->unit << 16 | lan_context; // IntiatorContext
+	msg[3] = 1 << 4; 			 // TransmitControlWord
+	
+	// create a simple SGL, see fig. 3-26
+	// D5 = 1101 0101 = LE eob 0 1 LA dir bc1 bc0
+
+	msg[4] = 0xD5000000 | skb->len;		// MAC hdr included
+	msg[5] = (u32)skb; 			// TransactionContext
+	msg[6] = virt_to_bus(skb->data);
+
+	i2o_post_message(iop,m);
+
+	dprintk(KERN_INFO "%s: Packet (%d bytes) sent to network.\n",
+		dev->name, skb->len);
+
+	return 0;
+}
+
+/*
+ * net_device_stats(): Return statistical information.
+ */
+static struct net_device_stats *i2o_lan_get_stats(struct device *dev)
+{
+	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
+	struct i2o_device *i2o_dev = priv->i2o_dev;
+	struct i2o_controller *iop = i2o_dev->controller;
+	u64 val[16];
+
+	/* query LAN_HISTORICAL_STATS scalar parameter group 0x0100 */
+
+        i2o_query_scalar(iop, i2o_dev->id, lan_context, 0x0100, -1, 
+        		 &val, 16*8, &priv->reply_flag);
+        priv->stats.tx_packets = val[0];
+        priv->stats.tx_bytes   = val[1];
+        priv->stats.rx_packets = val[2];
+        priv->stats.rx_bytes   = val[3];
+        priv->stats.tx_errors  = val[4];
+        priv->stats.rx_errors  = val[5];
+	priv->stats.rx_dropped = val[6];
+
+	// other net_device_stats and FDDI class specific fields follow ...
+
+	return (struct net_device_stats *)&priv->stats;
+}
+ 
+/*
+ * i2o_lan_set_multicast_list(): Enable a network device to receive packets
+ *	not send to the protocol address.
+ */
+static void i2o_lan_set_multicast_list(struct device *dev)
+{
+	struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
+	struct i2o_device *i2o_dev = priv->i2o_dev;
+	struct i2o_controller *iop = i2o_dev->controller;
+	u32 filter_mask;
+
+	dprintk(KERN_INFO "Entered i2o_lan_set_multicast_list().\n");
+
+return;
+
+/*
+ * FIXME: For some reason this kills interrupt handler in i2o_post_wait :-(
+ * 
+ */
+	dprintk(KERN_INFO "dev->flags = 0x%08X, dev->mc_count = 0x%08X\n",
+		dev->flags,dev->mc_count);
+
+	if (i2o_query_scalar(iop, i2o_dev->id, lan_context, 0x0001, 3, 
+			     &filter_mask, 4, &priv->reply_flag) < 0 )
+		printk(KERN_WARNING "i2o_lan: Unable to query filter mask.\n");	
+
+	dprintk(KERN_INFO "filter_mask = 0x%08X\n",filter_mask);
+
+	if (dev->flags & IFF_PROMISC)
+	{
+        	// Enable promiscuous mode
+
+		filter_mask |= 0x00000002; 
+		if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0001, 3,
+				&filter_mask, 4, &priv->reply_flag) <0)
+			printk(KERN_WARNING "i2o_lan: Unable to enable promiscuous multicast mode.\n");
+		else
+			dprintk(KERN_INFO "i2o_lan: Promiscuous multicast mode enabled.\n");
+
+		return;
+        }
+        
+// 	if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > HW_MAX_ADDRS)
+// 	{
+//        	// Disable promiscuous mode, use normal mode.
+// 		hardware_set_filter(NULL);
+//
+//		dprintk(KERN_INFO "i2o_lan: Disabled promiscuous mode, uses normal mode\n"); 
+//
+//		filter_mask = 0x00000000; 
+//		i2o_params_set(iop, i2o_dev->id, lan_context, 0x0001, 3,
+//				&filter_mask, 4, &priv->reply_flag);
+//		
+//		return;
+//      }
+
+        if (dev->mc_count)
+	{
+        	// Walk the address list, and load the filter
+//              hardware_set_filter(dev->mc_list);
+
+                filter_mask = 0x00000004; 
+		if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0001, 3,
+				   &filter_mask, 4, &priv->reply_flag) <0)
+			printk(KERN_WARNING "i2o_lan: Unable to enable Promiscuous multicast mode.\n");
+		else
+			dprintk(KERN_INFO "i2o_lan: Promiscuous multicast mode enabled.\n");			
+        
+       		return;
+	}
+        
+        // Unicast
+        
+        filter_mask |= 0x00000300; // Broadcast, Multicast disabled
+	if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0001, 3,
+			&filter_mask, 4, &priv->reply_flag) <0)
+		printk(KERN_WARNING "i2o_lan: Unable to enable unicast mode.\n");
+	else
+		dprintk(KERN_INFO "i2o_lan: Unicast mode enabled.\n");	
+
+	return;
+}
+
+struct device *i2o_lan_register_device(struct i2o_device *i2o_dev)
+{
+	struct device *dev = NULL;
+	struct i2o_lan_local *priv = NULL;
+	u8 hw_addr[8];
+	unsigned short (*type_trans)(struct sk_buff *, struct device *);
+
+	switch (i2o_dev->subclass)
+	{
+	case I2O_LAN_ETHERNET:
+		/* Note: init_etherdev calls 
+			 ether_setup() and register_netdevice() 
+			 and allocates the priv structure	 */
+
+        	dev = init_etherdev(NULL, sizeof(struct i2o_lan_local));
+		if (dev == NULL)
+			return NULL;
+		type_trans = eth_type_trans;
+		break;
+
+/*
+#ifdef CONFIG_ANYLAN
+	case I2O_LAN_100VG:
+		printk(KERN_WARNING "i2o_lan: 100base VG not yet supported\n");
+		break;
+#endif
+*/
+
+#ifdef CONFIG_TR
+	case I2O_LAN_TR:
+		dev = init_trdev(NULL, sizeof(struct i2o_lan_local));
+		if(dev==NULL)
+			return NULL;
+		type_trans = tr_type_trans;
+		break;
+#endif
+
+#ifdef CONFIG_FDDI
+	case I2O_LAN_FDDI:
+	{
+		int size = sizeof(struct device) + sizeof(struct i2o_lan_local)
+			   + sizeof("fddi%d ");
+
+        	dev = (struct device *) kmalloc(size, GFP_KERNEL);
+        	memset((char *)dev, 0, size);
+            	dev->priv = (void *)(dev + 1);
+                dev->name = (char *)(dev + 1) + sizeof(struct i2o_lan_local);
+
+		if (dev_alloc_name(dev,"fddi%d") < 0)
+		{
+			printk(KERN_WARNING "i2o_lan: Too many FDDI devices.\n");
+			kfree(dev);
+			return NULL;
+		}
+		type_trans = fddi_type_trans;
+
+		fddi_setup(dev);
+		register_netdev(dev);
+      	}
+	break;
+#endif
+
+/*
+#ifdef CONFIG_FIBRE_CHANNEL
+	case I2O_LAN_FIBRE_CHANNEL:
+		printk(KERN_WARNING "i2o_lan: Fibre Channel not yet supported\n");
+	break;
+#endif
+*/
+	case I2O_LAN_UNKNOWN:
+	default:
+		printk(KERN_WARNING "i2o_lan: LAN type 0x%08X not supported\n",
+		       i2o_dev->subclass);
+		return NULL;
+	}
+
+	priv = (struct i2o_lan_local *)dev->priv;
+	priv->i2o_dev = i2o_dev;
+	priv->type_trans = type_trans;
+
+        if (i2o_query_scalar(i2o_dev->controller, i2o_dev->id, lan_context,
+			     0x0001, 0, &hw_addr, 8, &priv->reply_flag) < 0)
+	{
+        	printk("%s: Unable to query hardware address.\n",
+		       dev->name);
+      		return NULL;  
+        }
+        
+	dprintk("%s hwaddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+      		dev->name,hw_addr[0], hw_addr[1], hw_addr[2], hw_addr[3],
+      		hw_addr[4], hw_addr[5]);
+
+	dev->addr_len = 6;
+	memcpy(dev->dev_addr, hw_addr, 6);
+
+	dev->open               = i2o_lan_open;
+	dev->stop               = i2o_lan_close;
+	dev->hard_start_xmit    = i2o_lan_packet_send;
+	dev->get_stats          = i2o_lan_get_stats;
+	dev->set_multicast_list = i2o_lan_set_multicast_list;
+
+	return dev;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	struct device *dev;
+	struct i2o_lan_local *priv;
+        int i;
+
+        if (i2o_install_handler(&i2o_lan_handler) < 0)
+	{
+ 		printk(KERN_ERR "Unable to register I2O LAN OSM.\n");
+		return -EINVAL;
+        }   
+
+	lan_context = i2o_lan_handler.context;
+
+        for (i=0; i < MAX_I2O_CONTROLLERS; i++)
+	{
+                struct i2o_controller *iop = i2o_find_controller(i);
+                struct i2o_device *i2o_dev;
+
+                if (iop==NULL)
+			continue;
+
+                for (i2o_dev=iop->devices;i2o_dev != NULL;i2o_dev=i2o_dev->next)
+		{
+                        int class = i2o_dev->class;
+
+                        if (class != 0x020) /* not I2O_CLASS_LAN device*/ 
+                                continue;
+
+			if (unit == MAX_LAN_CARDS)
+			{
+				printk(KERN_WARNING "Too many I2O LAN devices.\n");
+				return -EINVAL;
+			}
+
+			dev = i2o_lan_register_device(i2o_dev);
+ 			if (dev == NULL)
+			{
+				printk(KERN_WARNING "Unable to register I2O LAN device\n");
+				continue; // try next one
+			}
+			priv = (struct i2o_lan_local *)dev->priv;	
+
+			unit++;
+			i2o_landevs[unit] = dev;
+			priv->unit = unit;
+
+			printk(KERN_INFO "%s: I2O LAN device registered, tid = %d,"
+				" subclass = 0x%08X, unit = %d.\n",
+				dev->name, i2o_dev->id, i2o_dev->subclass, 
+				priv->unit);
+                }
+        }
+
+	dprintk(KERN_INFO "%d I2O LAN devices found and registered.\n", unit+1);
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int i;
+
+	for (i = 0; i <= unit; i++)
+	{
+		struct device *dev = i2o_landevs[i];
+		struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;	
+		struct i2o_device *i2o_dev = priv->i2o_dev;	
+
+		switch (i2o_dev->subclass)
+		{
+		case I2O_LAN_ETHERNET:
+			unregister_netdev(dev);
+			kfree(dev);
+			break;
+#ifdef CONFIG_FDDI
+		case I2O_LAN_FDDI:
+			unregister_netdevice(dev);
+			kfree(dev);
+			break;
+#endif
+#ifdef CONFIG_TR
+		case I2O_LAN_TR:
+			unregister_netdev(dev);
+			kfree(dev);
+			break;
+#endif
+		default:
+			printk(KERN_WARNING "i2o_lan: Spurious I2O LAN subclass 0x%08X.\n",
+			       i2o_dev->subclass);
+		}
+
+		dprintk(KERN_INFO "%s: I2O LAN device unregistered.\n", 
+			dev->name);
+	}
+
+        i2o_remove_handler(&i2o_lan_handler);
+}
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Univ of Helsinki, CS Department");
+MODULE_DESCRIPTION("I2O Lan OSM");
+
+#endif    
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/i2o_lan.h linux/drivers/i2o/i2o_lan.h
--- v2.3.5/linux/drivers/i2o/i2o_lan.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/i2o_lan.h	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,112 @@
+/*
+ *   	i2o_lan.h		LAN Class specific definitions
+ *
+ *      I2O LAN CLASS OSM       Prototyping, May 7th 1999
+ *
+ *      (C) Copyright 1999      University of Helsinki,
+ *                              Department of Computer Science
+ *
+ *      This code is still under development / test.
+ *
+ *      Author:         Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI>
+ *    
+ */
+
+#ifndef I2O_LAN_H
+#define I2O_LAN_H
+
+/* Tunable parameters first */
+
+#define I2O_BUCKET_COUNT 	64
+#define I2O_BUCKET_THRESH	5
+
+/* LAN types */
+#define I2O_LAN_ETHERNET	0x0030
+#define I2O_LAN_100VG		0x0040
+#define I2O_LAN_TR		0x0050
+#define I2O_LAN_FDDI		0x0060
+#define I2O_LAN_FIBRE_CHANNEL	0x0070
+#define I2O_LAN_UNKNOWN		0x00000000
+
+/* Connector types */
+
+/* Ethernet */
+#define I2O_LAN_AUI		(I2O_LAN_ETHERNET << 4) + 0x00000001
+#define I2O_LAN_10BASE5		(I2O_LAN_ETHERNET << 4) + 0x00000002
+#define I2O_LAN_FIORL		(I2O_LAN_ETHERNET << 4) + 0x00000003
+#define I2O_LAN_10BASE2		(I2O_LAN_ETHERNET << 4) + 0x00000004
+#define I2O_LAN_10BROAD36	(I2O_LAN_ETHERNET << 4) + 0x00000005
+#define I2O_LAN_10BASE_T	(I2O_LAN_ETHERNET << 4) + 0x00000006
+#define I2O_LAN_10BASE_FP	(I2O_LAN_ETHERNET << 4) + 0x00000007
+#define I2O_LAN_10BASE_FB	(I2O_LAN_ETHERNET << 4) + 0x00000008
+#define I2O_LAN_10BASE_FL	(I2O_LAN_ETHERNET << 4) + 0x00000009
+#define I2O_LAN_100BASE_TX	(I2O_LAN_ETHERNET << 4) + 0x0000000A
+#define I2O_LAN_100BASE_FX	(I2O_LAN_ETHERNET << 4) + 0x0000000B
+#define I2O_LAN_100BASE_T4	(I2O_LAN_ETHERNET << 4) + 0x0000000C
+#define I2O_LAN_1000BASE_SX	(I2O_LAN_ETHERNET << 4) + 0x0000000D
+#define I2O_LAN_1000BASE_LX	(I2O_LAN_ETHERNET << 4) + 0x0000000E
+#define I2O_LAN_1000BASE_CX	(I2O_LAN_ETHERNET << 4) + 0x0000000F
+#define I2O_LAN_1000BASE_T	(I2O_LAN_ETHERNET << 4) + 0x00000010
+
+/* AnyLAN */
+#define I2O_LAN_100VG_ETHERNET	(I2O_LAN_100VG << 4) + 0x00000001
+#define I2O_LAN_100VG_TR	(I2O_LAN_100VG << 4) + 0x00000002
+
+/* Token Ring */
+#define I2O_LAN_4MBIT		(I2O_LAN_TR << 4) + 0x00000001
+#define I2O_LAN_16MBIT		(I2O_LAN_TR << 4) + 0x00000002
+
+/* FDDI */
+#define I2O_LAN_125MBAUD	(I2O_LAN_FDDI << 4) + 0x00000001
+
+/* Fibre Channel */
+#define I2O_LAN_POINT_POINT	(I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000001
+#define I2O_LAN_ARB_LOOP	(I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000002
+#define I2O_LAN_PUBLIC_LOOP	(I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000003
+#define I2O_LAN_FABRIC		(I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000004
+
+#define I2O_LAN_EMULATION	0x00000F00
+#define I2O_LAN_OTHER		0x00000F01
+#define I2O_LAN_DEFAULT		0xFFFFFFFF
+
+/* LAN class functions */
+
+#define LAN_PACKET_SEND		0x3B
+#define LAN_SDU_SEND		0x3D
+#define LAN_RECEIVE_POST	0x3E
+#define LAN_RESET		0x35
+#define LAN_SUSPEND	0x37
+
+/* LAN DetailedStatusCode defines */
+#define I2O_LAN_DSC_SUCCESS			0x00
+#define I2O_LAN_DSC_DEVICE_FAILURE		0x01
+#define I2O_LAN_DSC_DESTINATION_NOT_FOUND	0x02
+#define	I2O_LAN_DSC_TRANSMIT_ERROR		0x03
+#define I2O_LAN_DSC_TRANSMIT_ABORTED		0x04
+#define I2O_LAN_DSC_RECEIVE_ERROR		0x05
+#define I2O_LAN_DSC_RECEIVE_ABORTED		0x06
+#define I2O_LAN_DSC_DMA_ERROR			0x07
+#define I2O_LAN_DSC_BAD_PACKET_DETECTED		0x08
+#define I2O_LAN_DSC_OUT_OF_MEMORY		0x09
+#define I2O_LAN_DSC_BUCKET_OVERRUN		0x0A
+#define I2O_LAN_DSC_IOP_INTERNAL_ERROR		0x0B
+#define I2O_LAN_DSC_CANCELED			0x0C
+#define I2O_LAN_DSC_INVALID_TRANSACTION_CONTEXT	0x0D
+#define I2O_LAN_DSC_DEST_ADDRESS_DETECTED	0x0E
+#define I2O_LAN_DSC_DEST_ADDRESS_OMITTED	0x0F
+#define I2O_LAN_DSC_PARTIAL_PACKET_RETURNED	0x10
+#define I2O_LAN_DSC_TEMP_SUSPENDED_STATE	0x11
+
+struct i2o_packet_info {
+	u32 offset : 24;
+	u32 flags  : 8;
+	u32 len    : 24;
+	u32 status : 8;
+};
+
+struct i2o_bucket_descriptor {
+	u32 context; 			/* FIXME: 64bit support */
+	struct i2o_packet_info packet_info[1];
+};
+ 
+#endif /* I2O_LAN_H */
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/i2o_pci.c linux/drivers/i2o/i2o_pci.c
--- v2.3.5/linux/drivers/i2o/i2o_pci.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/i2o_pci.c	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,243 @@
+/*
+ *	Find I2O capable controllers on the PCI bus, and register/install
+ *	them with the I2O layer
+ *
+ *	(C) Copyright 1999   Red Hat Software
+ *	
+ *	Written by Alan Cox, Building Number Three Ltd
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ * 	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+ 
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/i2o.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <asm/io.h>
+
+/*
+ *	Free bus specific resources
+ */
+ 
+static void i2o_pci_dispose(struct i2o_controller *c)
+{
+	I2O_IRQ_WRITE32(c,0xFFFFFFFF);
+	if(c->bus.pci.irq > 0)
+		free_irq(c->bus.pci.irq, c);
+	iounmap(((u8 *)c->post_port)-0x40);
+}
+
+/*
+ *	No real bus specific handling yet (note that later we will
+ *	need to 'steal' PCI devices on i960 mainboards)
+ */
+ 
+static int i2o_pci_bind(struct i2o_controller *c, struct i2o_device *dev)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int i2o_pci_unbind(struct i2o_controller *c, struct i2o_device *dev)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ *	Bus specific interrupt handler
+ */
+ 
+static void i2o_pci_interrupt(int irq, void *dev_id, struct pt_regs *r)
+{
+	struct i2o_controller *c = dev_id;
+	i2o_run_queue(c);
+}	
+
+/*
+ *	Install a PCI (or in theory AGP) i2o controller
+ */
+ 
+int __init i2o_pci_install(struct pci_dev *dev)
+{
+	struct i2o_controller *c=kmalloc(sizeof(struct i2o_controller),
+						GFP_KERNEL);
+	u8 *mem;
+	u32 memptr = 0;
+	u32 size;
+	
+	int i;
+	
+	if(c==NULL)
+	{
+		printk(KERN_ERR "i2o_pci: insufficient memory to add controller.\n");
+		return -ENOMEM;
+	}
+	memset(c, 0, sizeof(*c));
+	
+	for(i=0; i<6; i++)
+	{
+		/* Skip I/O spaces */
+		if(!(dev->base_address[i]&PCI_BASE_ADDRESS_SPACE))
+		{
+			memptr=PCI_BASE_ADDRESS_MEM_MASK&dev->base_address[i];
+			break;
+		}
+	}
+	
+	if(i==6)
+	{
+		printk(KERN_ERR "i2o_pci: I2O controller has no memory regions defined.\n");
+		return -ENOMEM;
+	}
+	
+	pci_write_config_dword(dev, PCI_BASE_ADDRESS_0+4*i, 0xFFFFFFFF);
+	pci_read_config_dword(dev, PCI_BASE_ADDRESS_0+4*i, &size);
+	pci_write_config_dword(dev, PCI_BASE_ADDRESS_0+4*i, dev->base_address[i]);
+	
+	/* Map the I2O controller */
+	
+	printk(KERN_INFO "PCI I2O controller at 0x%08X size=%d\n", memptr, -size);
+	mem = ioremap(memptr, -size);
+	
+	c->bus.pci.irq = -1;
+
+	c->irq_mask = (volatile u32 *)(mem+0x34);
+	c->post_port = (volatile u32 *)(mem+0x40);
+	c->reply_port = (volatile u32 *)(mem+0x44);
+
+	c->mem_phys = memptr;
+	c->mem_offset = (u32)mem;
+	c->destructor = i2o_pci_dispose;
+	
+	c->bind = i2o_pci_bind;
+	c->unbind = i2o_pci_unbind;
+	
+	c->type = I2O_TYPE_PCI;
+
+	I2O_IRQ_WRITE32(c,0xFFFFFFFF);
+	
+	i = i2o_install_controller(c);
+	
+	if(i<0)
+	{
+		printk(KERN_ERR "i2o: unable to install controller.\n");
+		return i;
+	}
+
+	c->bus.pci.irq = dev->irq;
+	if(c->bus.pci.irq)
+	{
+		i=request_irq(dev->irq, i2o_pci_interrupt, SA_SHIRQ,
+			c->name, c);
+		if(i<0)
+		{
+			printk(KERN_ERR "%s: unable to allocate interrupt %d.\n",
+				c->name, dev->irq);
+			c->bus.pci.irq = -1;
+			i2o_delete_controller(c);
+			return -EBUSY;
+		}
+	}
+	return 0;	
+}
+
+int __init i2o_pci_scan(void)
+{
+	struct pci_dev *dev;
+	int count=0;
+	
+	printk(KERN_INFO "Checking for PCI I2O controllers...\n");
+	
+	for(dev=pci_devices; dev!=NULL; dev=dev->next)
+	{
+		if((dev->class>>8)!=PCI_CLASS_INTELLIGENT_I2O)
+			continue;
+		if((dev->class&0xFF)>1)
+		{
+			printk(KERN_INFO "I2O controller found but does not support I2O 1.5 (skipping).\n");
+			continue;
+		}
+		printk(KERN_INFO "I2O controller on bus %d at %d.\n",
+			dev->bus->number, dev->devfn);
+		if(!dev->master)
+			printk(KERN_WARNING "Controller not master enabled.\n");
+		if(i2o_pci_install(dev)==0)
+			count++;
+	}
+	if(count)
+		printk(KERN_INFO "%d I2O controller%s found and installed.\n", count,
+			count==1?"":"s");
+	return count?count:-ENODEV;
+}
+
+static void i2o_pci_unload(void)
+{
+	int i=0;
+	struct i2o_controller *c;
+	
+	for(i = 0; i < MAX_I2O_CONTROLLERS; i++)
+	{
+		c=i2o_find_controller(i);
+		if(c==NULL)
+			continue;		
+		if(c->type == I2O_TYPE_PCI)
+			i2o_delete_controller(c);
+		i2o_unlock_controller(c);
+	}
+}
+
+static void i2o_pci_activate(void)
+{
+	int i=0;
+	struct i2o_controller *c;
+	
+	for(i = 0; i < MAX_I2O_CONTROLLERS; i++)
+	{
+		c=i2o_find_controller(i);
+		if(c==NULL)
+			continue;		
+		if(c->type == I2O_TYPE_PCI)
+		{
+			if(i2o_activate_controller(c))
+			{
+				printk("I2O: Failed to initialize iop%d\n", c->unit);
+				i2o_unlock_controller(c);
+				free_irq(c->bus.pci.irq, c);
+				i2o_delete_controller(c);
+				continue;
+			}
+		
+			I2O_IRQ_WRITE32(c,0);
+		}
+		i2o_unlock_controller(c);
+	}
+}
+
+#ifdef MODULE
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Red Hat Software");
+MODULE_DESCRIPTION("I2O PCI Interface");
+
+int init_module(void)
+{
+	if(i2o_pci_scan()<0)
+		return -ENODEV;
+	i2o_pci_activate();
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	i2o_pci_unload();
+}
+
+#endif
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/i2o_proc.c linux/drivers/i2o/i2o_proc.c
--- v2.3.5/linux/drivers/i2o/i2o_proc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/i2o_proc.c	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,2382 @@
+/*
+ *   procfs handler for Linux I2O subsystem
+ *
+ *   Copyright (c) 1999 Intel Corporation
+ *   
+ *   Originally written by Deepak Saxena(deepak.saxena@intel.com)
+ *
+ *   This program is free software. You can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation; either version
+ *   2 of the License, or (at your option) any later version.
+ *
+ *   This is an initial test release. The code is based on the design
+ *   of the ide procfs system (drivers/block/ide-proc.c). Some code
+ *   taken from i2o-core module by Alan Cox.
+ *
+ *   DISCLAIMER: This code is still under development/test and may cause
+ *   your system to behave unpredictably.  Use at your own discretion.
+ *
+ *   LAN entries by Juha Sievänen(Juha.Sievanen@cs.Helsinki.FI),
+ *   University of Helsinki, Department of Computer Science
+ *
+ */
+
+/*
+ * set tabstop=3
+ */
+
+/*
+ * TODO List
+ *
+ * - Add support for any version 2.0 spec changes once 2.0 IRTOS is
+ *   is available to test with
+ * - Clean up code to use official structure definitions 
+ */
+
+// FIXME!
+#define FMT_U64_HEX "0x%08x%08x"
+#define U64_VAL(pu64) *((u32*)(pu64)+1), *((u32*)(pu64))
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/i2o.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <asm/spinlock.h>
+
+
+#include "i2o_proc.h"
+
+#include "i2o_lan.h"
+
+/*
+ * Structure used to define /proc entries
+ */
+typedef struct _i2o_proc_entry_t
+{
+	char *name;						/* entry name */
+	mode_t mode;					/* mode */
+	read_proc_t *read_proc;		/* read func */
+	write_proc_t *write_proc;	/* write func */
+} i2o_proc_entry;
+
+static int proc_context = 0;
+
+
+static int i2o_proc_read_lct(char *, char **, off_t, int, int *, void *);
+static int i2o_proc_read_hrt(char *, char **, off_t, int, int *, void *);
+static int i2o_proc_read_stat(char *, char **, off_t, int, int *, void *);
+static int i2o_proc_read_hw(char *, char **, off_t, int, int *, void *);
+static int i2o_proc_read_dev(char *, char **, off_t, int, int *, void *);
+static int i2o_proc_read_dev_name(char *, char **, off_t, int, int *, void *);
+static int i2o_proc_read_ddm(char *, char **, off_t, int, int *, void *);
+static int i2o_proc_read_uinfo(char *, char **, off_t, int, int *, void *);
+static int print_serial_number(char *, int, u8 *, int);
+static int i2o_proc_create_entries(void *, 
+	i2o_proc_entry *p, struct proc_dir_entry *);
+static void i2o_proc_remove_entries(i2o_proc_entry *p, 
+	struct proc_dir_entry *);
+static int i2o_proc_add_controller(struct i2o_controller *, 
+	struct proc_dir_entry * );
+static void i2o_proc_remove_controller(struct i2o_controller *, 
+	struct proc_dir_entry * );
+static int create_i2o_procfs(void);
+static int destroy_i2o_procfs(void);
+static void i2o_proc_reply(struct i2o_handler *, struct i2o_controller *,
+			struct i2o_message *);
+
+static int i2o_proc_read_lan_dev_info(char *, char **, off_t, int, int *,
+				      void *);
+static int i2o_proc_read_lan_mac_addr(char *, char **, off_t, int, int *,
+				      void *);
+static int i2o_proc_read_lan_curr_addr(char *, char **, off_t, int, int *,
+				       void *);
+#if 0
+static int i2o_proc_read_lan_mcast_addr(char *, char **, off_t, int, int *,
+					void *);
+#endif
+static int i2o_proc_read_lan_batch_control(char *, char **, off_t, int, int *,
+					   void *);
+static int i2o_proc_read_lan_operation(char *, char **, off_t, int, int *,
+				       void *);
+static int i2o_proc_read_lan_media_operation(char *, char **, off_t, int,
+					     int *, void *);
+#if 0
+static int i2o_proc_read_lan_alt_addr(char *, char **, off_t, int, int *,
+				      void *);
+#endif
+static int i2o_proc_read_lan_tx_info(char *, char **, off_t, int, int *,
+				     void *);
+static int i2o_proc_read_lan_rx_info(char *, char **, off_t, int, int *,
+				     void *);
+static int i2o_proc_read_lan_hist_stats(char *, char **, off_t, int, int *,
+					void *);
+static int i2o_proc_read_lan_opt_tx_hist_stats(char *, char **, off_t, int,
+					       int *, void *);
+static int i2o_proc_read_lan_opt_rx_hist_stats(char *, char **, off_t, int,
+					       int *, void *);
+static int i2o_proc_read_lan_fddi_stats(char *, char **, off_t, int, int *,
+					void *);
+
+#if 0
+/* Do we really need this??? */
+
+static loff_t i2o_proc_lseek(struct file *file, loff_t off, int whence)
+{
+	return 0;
+}
+#endif
+
+static struct proc_dir_entry *i2o_proc_dir_root;
+
+/*
+ * Message handler
+ */
+static struct i2o_handler i2o_proc_handler =
+{
+	(void *)i2o_proc_reply,
+	"I2O procfs Layer",
+	0
+};
+
+/*
+ * IOP specific entries...write field just in case someone 
+ * ever wants one.
+ */
+static i2o_proc_entry generic_iop_entries[] = 
+{
+	{"hrt", S_IFREG|S_IRUGO, i2o_proc_read_hrt, NULL},
+	{"lct", S_IFREG|S_IRUGO, i2o_proc_read_lct, NULL},
+	{"stat", S_IFREG|S_IRUGO, i2o_proc_read_stat, NULL},
+	{"hw", S_IFREG|S_IRUGO, i2o_proc_read_hw, NULL},
+	{NULL, 0, NULL, NULL}
+};
+
+/*
+ * Device specific entries
+ */
+static i2o_proc_entry generic_dev_entries[] = 
+{
+	{"dev_identity", S_IFREG|S_IRUGO, i2o_proc_read_dev, NULL},
+	{"ddm_identity", S_IFREG|S_IRUGO, i2o_proc_read_ddm, NULL},
+	{"user_info", S_IFREG|S_IRUGO, i2o_proc_read_uinfo, NULL},
+	{NULL, 0, NULL, NULL}
+};
+
+/*
+ *  Storage unit specific entries (SCSI Periph, BS) with device names
+ */
+static i2o_proc_entry rbs_dev_entries[] = 
+{
+	{"dev_name", S_IFREG|S_IRUGO, i2o_proc_read_dev_name, NULL},
+	{NULL, 0, NULL, NULL}
+};
+
+#define SCSI_TABLE_SIZE	13
+	static char *scsi_devices[] = 
+		{
+			"Direct-Access Read/Write",
+			"Sequential-Access Storage",
+			"Printer",
+			"Processor",
+			"WORM Device",
+			"CD-ROM Device",
+			"Scanner Device",
+			"Optical Memory Device",
+			"Medium Changer Device",
+			"Communications Device",
+			"Graphics Art Pre-Press Device",
+			"Graphics Art Pre-Press Device",
+			"Array Controller Device"
+		};
+
+/* private */
+
+/*
+ * LAN specific entries
+ * 
+ * Should groups with r/w entries have their own subdirectory?
+ *
+ */
+static i2o_proc_entry lan_entries[] = 
+{
+	/* LAN param groups 0000h-0008h */
+	{"lan_dev_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_dev_info, NULL},
+	{"lan_mac_addr", S_IFREG|S_IRUGO, i2o_proc_read_lan_mac_addr, NULL},
+#if 0
+	{"lan_mcast_addr", S_IFREG|S_IRUGO|S_IWUSR,
+	 i2o_proc_read_lan_mcast_addr, NULL},
+#endif
+	{"lan_batch_ctrl", S_IFREG|S_IRUGO|S_IWUSR,
+	 i2o_proc_read_lan_batch_control, NULL},
+	{"lan_operation", S_IFREG|S_IRUGO, i2o_proc_read_lan_operation, NULL},
+	{"lan_media_operation", S_IFREG|S_IRUGO,
+	 i2o_proc_read_lan_media_operation, NULL},
+#if 0
+	{"lan_alt_addr", S_IFREG|S_IRUGO, i2o_proc_read_lan_alt_addr, NULL},
+#endif
+	{"lan_tx_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_tx_info, NULL},
+	{"lan_rx_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_rx_info, NULL},
+	{"lan_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_hist_stats, NULL},
+	{"lan_opt_tx_stats", S_IFREG|S_IRUGO,
+	 i2o_proc_read_lan_opt_tx_hist_stats, NULL},
+	{"lan_opt_rx_stats", S_IFREG|S_IRUGO,
+	 i2o_proc_read_lan_opt_rx_hist_stats, NULL},
+	{"lan_fddi_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_fddi_stats, NULL},
+	/* some useful r/w entries, no write yet */
+	{"lan_curr_addr", S_IFREG|S_IRUGO|S_IWUSR,
+	 i2o_proc_read_lan_curr_addr, NULL},
+	{NULL, 0, NULL, NULL}
+};
+
+static u32 i2o_proc_token = 0;
+
+static char* bus_strings[] = 
+{ 
+	"Local Bus", 
+	"ISA", 
+	"EISA", 
+	"MCA", 
+	"PCI",
+	"PCMCIA", 
+	"NUBUS", 
+	"CARDBUS"
+};
+
+static spinlock_t i2o_proc_lock = SPIN_LOCK_UNLOCKED;
+
+void i2o_proc_reply(struct i2o_handler *phdlr, struct i2o_controller *pctrl,
+	struct i2o_message *pmsg)
+{
+	i2o_proc_token = I2O_POST_WAIT_OK;
+}
+
+int i2o_proc_read_hrt(char *buf, char **start, off_t offset, int len, 
+	int *eof, void *data)
+{
+	struct i2o_controller *c = (struct i2o_controller *)data;
+	pi2o_hrt hrt;
+	u32 msg[6];
+	u32 *workspace;
+	u32 bus;
+	int count;
+	int i;
+	int token;
+
+	spin_lock(&i2o_proc_lock);
+
+	len = 0;
+
+	workspace = kmalloc(2048, GFP_KERNEL);
+	hrt = (pi2o_hrt)workspace;
+	if(workspace==NULL)
+	{
+		len += sprintf(buf, "No free memory for HRT buffer\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	memset(workspace, 0, 2048);
+
+	msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4;
+	msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID;
+	msg[2]= (u32)proc_context;
+	msg[3]= 0;
+	msg[4]= (0xD0000000 | 2048); 
+	msg[5]= virt_to_phys(workspace); 
+
+	token = i2o_post_wait(c, ADAPTER_TID, msg, 6*4, &i2o_proc_token,2);
+	if(token == I2O_POST_WAIT_TIMEOUT)
+	{
+		kfree(workspace);
+		len += sprintf(buf, "Timeout waiting for HRT\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	if(hrt->hrt_version)
+	{
+		len += sprintf(buf+len, 
+					"HRT table for controller is too new a version.\n");
+		return len;
+	}
+
+	count = hrt->num_entries;
+
+	if((count * hrt->entry_len + 8) > 2048) {
+		printk(KERN_WARNING "i2o_proc: HRT does not fit into buffer\n");
+		len += sprintf(buf+len,
+			       "HRT table too big to fit in buffer.\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+	
+	len += sprintf(buf+len, "HRT has %d entries of %d bytes each.\n",
+		       count, hrt->entry_len);
+
+	for(i = 0; i < count; i++)
+	{
+		len += sprintf(buf+len, "Entry %d:\n", i);
+		len += sprintf(buf+len, "   Adapter ID: %0#10x\n", 
+					hrt->hrt_entry[i].adapter_id);
+		len += sprintf(buf+len, "   Controlled by: %0#6x\n",
+					hrt->hrt_entry[i].parent_tid);
+		len += sprintf(buf+len, "   Bus#%d\n", 
+					hrt->hrt_entry[i].bus_num);
+
+		if(hrt->hrt_entry[i].bus_type != 0x80)
+		{
+			bus = hrt->hrt_entry[i].bus_type;
+			len += sprintf(buf+len, "   %s Information\n", bus_strings[bus]);
+
+			switch(bus)
+			{
+				case I2O_BUS_LOCAL:
+					len += sprintf(buf+len, "     IOBase: %0#6x,",
+								hrt->hrt_entry[i].bus.local_bus.LbBaseIOPort);
+					len += sprintf(buf+len, " MemoryBase: %0#10x\n",
+								hrt->hrt_entry[i].bus.local_bus.LbBaseMemoryAddress);
+					break;
+
+				case I2O_BUS_ISA:
+					len += sprintf(buf+len, "     IOBase: %0#6x,",
+								hrt->hrt_entry[i].bus.isa_bus.IsaBaseIOPort);
+					len += sprintf(buf+len, " MemoryBase: %0#10x,",
+								hrt->hrt_entry[i].bus.isa_bus.IsaBaseMemoryAddress);
+					len += sprintf(buf+len, " CSN: %0#4x,",
+								hrt->hrt_entry[i].bus.isa_bus.CSN);
+					break;
+
+				case I2O_BUS_EISA:
+					len += sprintf(buf+len, "     IOBase: %0#6x,",
+								hrt->hrt_entry[i].bus.eisa_bus.EisaBaseIOPort);
+					len += sprintf(buf+len, " MemoryBase: %0#10x,",
+								hrt->hrt_entry[i].bus.eisa_bus.EisaBaseMemoryAddress);
+					len += sprintf(buf+len, " Slot: %0#4x,",
+								hrt->hrt_entry[i].bus.eisa_bus.EisaSlotNumber);
+					break;
+			 
+				case I2O_BUS_MCA:
+					len += sprintf(buf+len, "     IOBase: %0#6x,",
+								hrt->hrt_entry[i].bus.mca_bus.McaBaseIOPort);
+					len += sprintf(buf+len, " MemoryBase: %0#10x,",
+								hrt->hrt_entry[i].bus.mca_bus.McaBaseMemoryAddress);
+					len += sprintf(buf+len, " Slot: %0#4x,",
+								hrt->hrt_entry[i].bus.mca_bus.McaSlotNumber);
+					break;
+
+				case I2O_BUS_PCI:
+					len += sprintf(buf+len, "     Bus: %0#4x",
+								hrt->hrt_entry[i].bus.pci_bus.PciBusNumber);
+					len += sprintf(buf+len, " Dev: %0#4x",
+								hrt->hrt_entry[i].bus.pci_bus.PciDeviceNumber);
+					len += sprintf(buf+len, " Func: %0#4x",
+								hrt->hrt_entry[i].bus.pci_bus.PciFunctionNumber);
+					len += sprintf(buf+len, " Vendor: %0#6x",
+								hrt->hrt_entry[i].bus.pci_bus.PciVendorID);
+					len += sprintf(buf+len, " Device: %0#6x\n",
+								hrt->hrt_entry[i].bus.pci_bus.PciDeviceID);
+					break;
+
+				default:
+					len += sprintf(buf+len, "      Unsupported Bus Type\n");
+			}
+		}
+		else
+			len += sprintf(buf+len, "   Unknown Bus Type\n");
+	}
+
+	kfree(workspace);
+
+	spin_unlock(&i2o_proc_lock);
+	
+	return len;
+}
+
+int i2o_proc_read_lct(char *buf, char **start, off_t offset, int len,
+	int *eof, void *data)
+{
+	struct i2o_controller *c = (struct i2o_controller*)data;
+	u32 msg[8];
+	u32 *workspace;
+	pi2o_lct lct; /* = (pi2o_lct)c->lct; */
+	int entries;
+	int token;
+	int i;
+
+#define BUS_TABLE_SIZE 3
+	static char *bus_ports[] =
+		{
+			"Generic Bus",
+			"SCSI Bus",
+			"Fibre Channel Bus"
+		};
+
+	spin_lock(&i2o_proc_lock);
+
+	len = 0;
+
+	workspace = kmalloc(8192, GFP_KERNEL);
+	lct = (pi2o_lct)workspace;
+	if(workspace==NULL)
+	{
+		len += sprintf(buf, "No free memory for LCT buffer\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	memset(workspace, 0, 8192);
+
+	msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_6;
+	msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID;
+	msg[2] = (u32)proc_context;
+	msg[3] = 0;
+	msg[4] = 0xFFFFFFFF; /* All devices */
+	msg[5] = 0x00000000; /* Report now */
+	msg[6] = 0xD0000000|8192;
+	msg[7] = virt_to_bus(workspace);
+
+	token = i2o_post_wait(c, ADAPTER_TID, msg, 8*4, &i2o_proc_token,2);
+	if(token == I2O_POST_WAIT_TIMEOUT)
+	{
+		kfree(workspace);
+		len += sprintf(buf, "Timeout waiting for LCT\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	entries = (lct->table_size - 3)/9;
+
+	len += sprintf(buf, "LCT contains %d %s\n", entries,
+						entries == 1 ? "entry" : "entries");
+	if(lct->boot_tid)	
+		len += sprintf(buf+len, "Boot Device @ ID %d\n", lct->boot_tid);
+
+	for(i = 0; i < entries; i++)
+	{
+		len += sprintf(buf+len, "Entry %d\n", i);
+
+		len += sprintf(buf+len, "  %s", i2o_get_class_name(lct->lct_entry[i].class_id));
+	
+		/*
+		 *	Classes which we'll print subclass info for
+		 */
+		switch(lct->lct_entry[i].class_id & 0xFFF)
+		{
+			case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+				switch(lct->lct_entry[i].sub_class)
+				{
+					case 0x00:
+						len += sprintf(buf+len, ": Direct-Access Read/Write");
+						break;
+
+					case 0x04:
+						len += sprintf(buf+len, ": WORM Drive");
+						break;
+	
+					case 0x05:
+						len += sprintf(buf+len, ": CD-ROM Drive");
+						break;
+
+					case 0x07:
+						len += sprintf(buf+len, ": Optical Memory Device");
+						break;
+
+					default:
+						len += sprintf(buf+len, ": Unknown");
+						break;
+				}
+				break;
+
+			case I2O_CLASS_LAN:
+				switch(lct->lct_entry[i].sub_class & 0xFF)
+				{
+					case 0x30:
+						len += sprintf(buf+len, ": Ethernet");
+						break;
+
+					case 0x40:
+						len += sprintf(buf+len, ": 100base VG");
+						break;
+
+					case 0x50:
+						len += sprintf(buf+len, ": IEEE 802.5/Token-Ring");
+						break;
+
+					case 0x60:
+						len += sprintf(buf+len, ": ANSI X3T9.5 FDDI");
+						break;
+		
+					case 0x70:
+						len += sprintf(buf+len, ": Fibre Channel");
+						break;
+
+					default:
+						len += sprintf(buf+len, ": Unknown Sub-Class");
+						break;
+				}
+				break;
+
+			case I2O_CLASS_SCSI_PERIPHERAL:
+				if(lct->lct_entry[i].sub_class < SCSI_TABLE_SIZE)
+					len += sprintf(buf+len, ": %s", 
+								scsi_devices[lct->lct_entry[i].sub_class]);
+				else
+					len += sprintf(buf+len, ": Unknown Device Type");
+				break;
+
+			case I2O_CLASS_BUS_ADAPTER_PORT:
+				if(lct->lct_entry[i].sub_class < BUS_TABLE_SIZE)
+					len += sprintf(buf+len, ": %s", 
+								bus_ports[lct->lct_entry[i].sub_class]);
+				else
+					len += sprintf(buf+len, ": Unknown Bus Type");
+				break;
+		}
+		len += sprintf(buf+len, "\n");
+		
+		len += sprintf(buf+len, "  Local TID: 0x%03x\n", lct->lct_entry[i].tid);
+		len += sprintf(buf+len, "  User TID: 0x%03x\n", lct->lct_entry[i].user_tid);
+		len += sprintf(buf+len, "  Parent TID: 0x%03x\n", 
+					lct->lct_entry[i].parent_tid);
+		len += sprintf(buf+len, "  Identity Tag: 0x%x%x%x%x%x%x%x%x\n",
+					lct->lct_entry[i].identity_tag[0],
+					lct->lct_entry[i].identity_tag[1],
+					lct->lct_entry[i].identity_tag[2],
+					lct->lct_entry[i].identity_tag[3],
+					lct->lct_entry[i].identity_tag[4],
+					lct->lct_entry[i].identity_tag[5],
+					lct->lct_entry[i].identity_tag[6],
+					lct->lct_entry[i].identity_tag[7]);
+		len += sprintf(buf+len, "  Change Indicator: %0#10x\n", 
+				lct->lct_entry[i].change_ind);
+		len += sprintf(buf+len, "  Device Flags: %0#10x\n", 
+				lct->lct_entry[i].device_flags);
+	}
+
+	kfree(workspace);
+	spin_unlock(&i2o_proc_lock);
+	
+	return len;
+}
+
+int i2o_proc_read_stat(char *buf, char **start, off_t offset, int len, 
+	int *eof, void *data)
+{
+	struct i2o_controller *c = (struct i2o_controller*)data;
+	u32 *msg;
+	u32 m;
+	u8 *workspace;
+	u16 *work16;
+	u32 *work32;
+	long time;
+	char prodstr[25];
+	int version;
+	
+	spin_lock(&i2o_proc_lock);
+
+	len = 0;
+
+	workspace = (u8*)kmalloc(88, GFP_KERNEL);
+	if(!workspace)
+	{
+		len += sprintf(buf, "No memory for status transfer\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	m = I2O_POST_READ32(c);
+	if(m == 0xFFFFFFFF)
+	{
+		len += sprintf(buf, "Could not get inbound message frame from IOP!\n");	
+		kfree(workspace);
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+	
+	msg = (u32 *)(m+c->mem_offset);
+
+	memset(workspace, 0, 88);
+	work32 = (u32*)workspace;
+	work16 = (u16*)workspace;
+
+	msg[0] = NINE_WORD_MSG_SIZE|SGL_OFFSET_0;
+	msg[1] = I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID;
+	msg[2] = msg[3] = msg[4] = msg[5] = 0;
+	msg[6] = virt_to_phys(workspace);
+	msg[7] = 0; /* FIXME: 64-bit */
+	msg[8] = 88;
+
+	/* 
+	 * hmm...i2o_post_message should just take ptr to message, and 
+	 * determine offset on it's own...less work for OSM developers
+	 */
+	i2o_post_message(c, m);
+
+	time = jiffies;
+
+	while(workspace[87] != 0xFF)
+	{
+		if(jiffies-time >= 2*HZ)
+		{
+			len += sprintf(buf, "Timeout waiting for status reply\n");
+			kfree(workspace);
+			spin_unlock(&i2o_proc_lock);
+			return len;
+		}
+		schedule();
+		barrier();
+	}
+
+	len += sprintf(buf+len, "Organization ID: %0#6x\n", work16[0]);
+
+	version = workspace[9]&0xF0>>4;
+	if(version == 0x02) {
+		len += sprintf(buf+len, "Lowest I2O version supported: ");
+		switch(workspace[2]) {
+		case 0x00:
+		case 0x01:
+			len += sprintf(buf+len, "1.5\n");
+			break;
+		case 0x02:
+			len += sprintf(buf+len, "2.0\n");
+			break;
+		}
+
+		len += sprintf(buf+len, "Highest I2O version supported: ");
+		switch(workspace[3]) {
+		case 0x00:
+		case 0x01:
+			len += sprintf(buf+len, "1.5\n");
+			break;
+		case 0x02:
+			len += sprintf(buf+len, "2.0\n");
+			break;
+		}
+	}
+
+	len += sprintf(buf+len, "IOP ID: %0#5x\n", work16[2]&0xFFF);
+	len += sprintf(buf+len, "Host Unit ID: %0#6x\n", work16[3]);
+	len += sprintf(buf+len, "Segment Number: %0#5x\n", work16[4]&0XFFF);
+
+	len += sprintf(buf+len, "I2O Version: ");
+	switch(version)
+	{
+	case 0x00:
+	case 0x01:
+		len += sprintf(buf+len, "1.5\n");
+		break;
+	case 0x02:
+		len += sprintf(buf+len, "2.0\n");
+		break;
+	default:
+		len += sprintf(buf+len, "Unknown version\n");
+	}
+
+	len += sprintf(buf+len, "IOP State: ");
+	switch(workspace[10])
+	{
+		case 0x01:
+			len += sprintf(buf+len, "Init\n");
+			break;
+
+		case 0x02:
+			len += sprintf(buf+len, "Reset\n");
+			break;
+
+		case 0x04:
+			len += sprintf(buf+len, "Hold\n");
+			break;
+
+		case 0x05:
+			len += sprintf(buf+len, "Hold\n");
+			break;
+
+		case 0x08:
+			len += sprintf(buf+len, "Operational\n");
+			break;
+
+		case 0x10:
+			len += sprintf(buf+len, "FAILED\n");
+			break;
+
+		case 0x11:
+			len += sprintf(buf+len, "FAULTED\n");
+			break;
+
+		default:
+			len += sprintf(buf+len, "Unknown\n");
+			break;
+	}
+
+	/* 0x00 is the only type supported w/spec 1.5 */
+	/* Added 2.0 types */
+	len += sprintf(buf+len, "Messenger Type: ");
+	switch (workspace[11])
+	{
+	case 0x00:
+		len += sprintf(buf+len, "Memory Mapped\n");
+		break;
+	case 0x01:
+		len += sprintf(buf+len, "Memory mapped only\n");
+		break;
+	case 0x02:
+		len += sprintf(buf+len, "Remote only\n");
+		break;
+	case 0x03:
+		len += sprintf(buf+len, "Memory mapped and remote\n");
+		break;
+	default:
+		len += sprintf(buf+len, "Unknown\n");
+		break;
+	}
+	len += sprintf(buf+len, "Inbound Frame Size: %d bytes\n", work16[6]*4);
+	len += sprintf(buf+len, "Max Inbound Frames: %d\n", work32[4]);
+	len += sprintf(buf+len, "Current Inbound Frames: %d\n", work32[5]);
+	len += sprintf(buf+len, "Max Outbound Frames: %d\n", work32[6]);
+
+	/* Spec doesn't say if NULL terminated or not... */
+	memcpy(prodstr, work32+7, 24);
+	prodstr[24] = '\0';
+	len += sprintf(buf+len, "Product ID: %s\n", prodstr);
+
+	len += sprintf(buf+len, "LCT Size: %d\n", work32[13]);
+
+	len += sprintf(buf+len, "Desired Private Memory Space: %d kB\n", 
+						work32[15]>>10);
+	len += sprintf(buf+len, "Allocated Private Memory Space: %d kB\n", 
+						work32[16]>>10);
+	len += sprintf(buf+len, "Private Memory Base Address: %0#10x\n", 
+						work32[17]);
+	len += sprintf(buf+len, "Desired Private I/O Space: %d kB\n", 
+						work32[18]>>10);
+	len += sprintf(buf+len, "Allocated Private I/O Space: %d kB\n", 
+						work32[19]>>10);
+	len += sprintf(buf+len, "Private I/O Base Address: %0#10x\n", 
+						work32[20]);
+
+	spin_unlock(&i2o_proc_lock);
+
+	return len;
+}
+
+int i2o_proc_read_hw(char *buf, char **start, off_t offset, int len, 
+	int *eof, void *data)
+{
+	struct i2o_controller *c = (struct i2o_controller*)data;
+	static u32 work32[5];
+	static u8 *work8 = (u8*)work32;
+	static u16 *work16 = (u16*)work32;
+	int token;
+	u32 hwcap;
+
+	static char *cpu_table[] =
+	{
+		"Intel 80960 Series",
+		"AMD2900 Series",
+		"Motorola 68000 Series",
+		"ARM Series",
+		"MIPS Series",
+		"Sparc Series",
+		"PowerPC Series",
+		"Intel x86 Series"
+	};
+
+	spin_lock(&i2o_proc_lock);
+
+	len = 0;
+
+	token = i2o_query_scalar(c, ADAPTER_TID, proc_context, 
+					0,		// ParamGroup 0x0000h
+					-1,		// all fields
+					&work32,
+					sizeof(work32),
+					&i2o_proc_token);
+
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "IOP Hardware Information Table\n");
+
+	len += sprintf(buf+len, "I2O Vendor ID: %0#6x\n", work16[0]);
+	len += sprintf(buf+len, "Product ID: %0#6x\n", work16[1]);
+	len += sprintf(buf+len, "RAM: %dkB\n", work32[1]>>10);
+	len += sprintf(buf+len, "Non-Volatile Storage: %dkB\n", work32[2]>>10);
+
+	hwcap = work32[3];
+	len += sprintf(buf+len, "Capabilities:\n");
+	if(hwcap&0x00000001)
+		len += sprintf(buf+len, "   Self-booting\n");
+	if(hwcap&0x00000002)
+		len += sprintf(buf+len, "   Upgradable IRTOS\n");
+	if(hwcap&0x00000004)
+		len += sprintf(buf+len, "   Supports downloading DDMs\n");
+	if(hwcap&0x00000008)
+		len += sprintf(buf+len, "   Supports installing DDMs\n");
+	if(hwcap&0x00000010)
+		len += sprintf(buf+len, "   Battery-backed RAM\n");
+
+	len += sprintf(buf+len, "CPU: ");
+	if(work8[16] > 8)
+		len += sprintf(buf+len, "Unknown\n");
+	else
+		len += sprintf(buf+len, "%s\n", cpu_table[work8[16]]);
+	/* Anyone using ProcessorVersion? */
+	
+	spin_unlock(&i2o_proc_lock);
+
+	return len;
+}
+
+int i2o_proc_read_dev(char *buf, char **start, off_t offset, int len, 
+	int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u32 work32[128];		// allow for "stuff" + up to 256 byte (max) serial number
+					// == (allow) 512d bytes (max)
+	static u16 *work16 = (u16*)work32;
+	char sz[17];
+	int token;
+
+	spin_lock(&i2o_proc_lock);
+	
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+					0xF100,		// ParamGroup F100h (Device Identity)
+					-1,		// all fields
+					&work32,
+					sizeof(work32),
+					&i2o_proc_token);
+
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "Device Class: %s\n", i2o_get_class_name(work16[0]));
+
+	len += sprintf(buf+len, "Owner TID:    %0#5x\n", work16[2]);
+	len += sprintf(buf+len, "Parent TID:   %0#5x\n", work16[3]);
+
+	memcpy(sz, work32+2, 16);
+	sz[16] = '\0';
+	len += sprintf(buf+len, "Vendor Info:  %s\n", sz);
+
+	memcpy(sz, work32+6, 16);
+	sz[16] = '\0';
+	len += sprintf(buf+len, "Product Info: %s\n", sz);
+
+	memcpy(sz, work32+10, 16);
+	sz[16] = '\0';
+	len += sprintf(buf+len, "Description:  %s\n", sz);
+
+	memcpy(sz, work32+14, 8);
+	sz[8] = '\0';
+	len += sprintf(buf+len, "Product Revision: %s\n", sz);
+
+	len += sprintf(buf+len, "Serial Number: ");
+	len = print_serial_number(buf, len,
+			(u8*)(work32+16),
+							/* allow for SNLen plus
+							 * possible trailing '\0'
+							 */
+			sizeof(work32)-(16*sizeof(u32))-2
+				);
+	len +=  sprintf(buf+len, "\n");
+
+	spin_unlock(&i2o_proc_lock);
+
+	return len;
+}
+
+
+int i2o_proc_read_dev_name(char *buf, char **start, off_t offset, int len,
+	int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+
+	if ( d->dev_name[0] == '\0' )
+		return 0;
+
+	len = sprintf(buf, "%s\n", d->dev_name);
+
+	return len;
+}
+
+
+
+int i2o_proc_read_ddm(char *buf, char **start, off_t offset, int len, 
+	int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u32 work32[128];
+	static u16 *work16 = (u16*)work32;
+	int token;
+	char mod[25];
+
+	spin_lock(&i2o_proc_lock);
+	
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+					0xF101,		// ParamGroup F101h (DDM Identity)
+					-1,		// all fields
+					&work32,
+					sizeof(work32),
+					&i2o_proc_token);
+
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "Registering DDM TID: 0x%03x\n", work16[0]&0xFFF);
+
+	memcpy(mod, (char*)(work16+1), 24);
+	mod[24] = '\0';
+	len += sprintf(buf+len, "Module Name: %s\n", mod);
+
+	memcpy(mod, (char*)(work16+13), 8);
+	mod[8] = '\0';
+	len += sprintf(buf+len, "Module Rev: %s\n", mod);
+
+	len += sprintf(buf+len, "Serial Number: ");
+	len = print_serial_number(buf, len,
+			(u8*)(work16+17),
+							/* allow for SNLen plus
+							 * possible trailing '\0'
+							 */
+			sizeof(work32)-(17*sizeof(u16))-2
+				);
+	len += sprintf(buf+len, "\n");
+
+	spin_unlock(&i2o_proc_lock);
+
+	return len;
+}
+
+int i2o_proc_read_uinfo(char *buf, char **start, off_t offset, int len, 
+	int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u32 work32[128];
+	int token;
+	char sz[65];
+
+	spin_lock(&i2o_proc_lock);
+	
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+					0xF102,		// ParamGroup F102h (User Information)
+					-1,		// all fields
+					&work32,
+					sizeof(work32),
+					&i2o_proc_token);
+
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	memcpy(sz, (char*)work32, 64);
+	sz[64] = '\0';
+	len += sprintf(buf, "Device Name: %s\n", sz);
+
+	memcpy(sz, (char*)(work32+16), 64);
+	sz[64] = '\0';
+	len += sprintf(buf+len, "Service Name: %s\n", sz);
+
+	memcpy(sz, (char*)(work32+32), 64);
+	sz[64] = '\0';
+	len += sprintf(buf+len, "Physical Name: %s\n", sz);
+
+	memcpy(sz, (char*)(work32+48), 4);
+	sz[4] = '\0';
+	len += sprintf(buf+len, "Instance Number: %s\n", sz);
+
+	spin_unlock(&i2o_proc_lock);
+
+	return len;
+}
+
+static int print_serial_number(char *buff, int pos, u8 *serialno, int max_len)
+{
+	int i;
+
+	/* 19990419 -sralston
+	 *	The I2O v1.5 (and v2.0 so far) "official specification"
+	 *	got serial numbers WRONG!
+	 *	Apparently, and despite what Section 3.4.4 says and
+	 *	Figure 3-35 shows (pg 3-39 in the pdf doc),
+	 *	the convention / consensus seems to be:
+	 *	  + First byte is SNFormat
+	 *	  + Second byte is SNLen (but only if SNFormat==7 (?))
+	 *	  + (v2.0) SCSI+BS may use IEEE Registered (64 or 128 bit) format
+	 */
+	switch(serialno[0])
+	{
+		case I2O_SNFORMAT_BINARY:		/* Binary */
+			pos += sprintf(buff+pos, "0x");
+			for(i = 0; i < serialno[1]; i++)
+			{
+				pos += sprintf(buff+pos, "%02X", serialno[2+i]);
+			}
+			break;
+	
+		case I2O_SNFORMAT_ASCII:		/* ASCII */
+			if ( serialno[1] < ' ' )	/* printable or SNLen? */
+			{
+				/* sanity */
+				max_len = (max_len < serialno[1]) ? max_len : serialno[1];
+				serialno[1+max_len] = '\0';
+
+				/* just print it */
+				pos += sprintf(buff+pos, "%s", &serialno[2]);
+			}
+			else
+			{
+				/* print chars for specified length */
+				for(i = 0; i < serialno[1]; i++)
+				{
+					pos += sprintf(buff+pos, "%c", serialno[2+i]);
+				}
+			}
+			break;
+
+		case I2O_SNFORMAT_UNICODE:		/* UNICODE */
+			pos += sprintf(buff+pos, "UNICODE Format.  Can't Display\n");
+			break;
+
+		case I2O_SNFORMAT_LAN48_MAC:		/* LAN-48 MAC Address */
+			pos += sprintf(buff+pos, 
+						"LAN-48 MAC Address @ %02X:%02X:%02X:%02X:%02X:%02X",
+						serialno[2], serialno[3],
+						serialno[4], serialno[5],
+						serialno[6], serialno[7]);
+
+		case I2O_SNFORMAT_WAN:			/* WAN MAC Address */
+			/* FIXME: Figure out what a WAN access address looks like?? */
+			pos += sprintf(buff+pos, "WAN Access Address");
+			break;
+
+
+/* plus new in v2.0 */
+		case I2O_SNFORMAT_LAN64_MAC:		/* LAN-64 MAC Address */
+			/* FIXME: Figure out what a LAN-64 address really looks like?? */
+			pos += sprintf(buff+pos, 
+						"LAN-64 MAC Address @ [?:%02X:%02X:?] %02X:%02X:%02X:%02X:%02X:%02X",
+						serialno[8], serialno[9],
+						serialno[2], serialno[3],
+						serialno[4], serialno[5],
+						serialno[6], serialno[7]);
+			break;
+
+
+		case I2O_SNFORMAT_DDM:			/* I2O DDM */
+			pos += sprintf(buff+pos, 
+						"DDM: Tid=%03Xh, Rsvd=%04Xh, OrgId=%04Xh",
+						*(u16*)&serialno[2],
+						*(u16*)&serialno[4],
+						*(u16*)&serialno[6]);
+			break;
+
+		case I2O_SNFORMAT_IEEE_REG64:		/* IEEE Registered (64-bit) */
+		case I2O_SNFORMAT_IEEE_REG128:		/* IEEE Registered (128-bit) */
+			/* FIXME: Figure if this is even close?? */
+			pos += sprintf(buff+pos, 
+						"IEEE NodeName(hi,lo)=(%08Xh:%08Xh), PortName(hi,lo)=(%08Xh:%08Xh)\n",
+						*(u32*)&serialno[2],
+						*(u32*)&serialno[6],
+						*(u32*)&serialno[10],
+						*(u32*)&serialno[14]);
+			break;
+
+
+		case I2O_SNFORMAT_UNKNOWN:		/* Unknown 0    */
+		case I2O_SNFORMAT_UNKNOWN2:		/* Unknown 0xff */
+		default:
+			pos += sprintf(buff+pos, "Unknown Data Format");
+			break;
+	}
+
+	return pos;
+}
+
+/* LAN group 0000h - Device info (scalar) */
+int i2o_proc_read_lan_dev_info(char *buf, char **start, off_t offset, int len, 
+			       int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u32 work32[56];
+	static u8 *work8 = (u8*)work32;
+	static u16 *work16 = (u16*)work32;
+	static u64 *work64 = (u64*)work32;
+	int token;
+
+	spin_lock(&i2o_proc_lock);
+
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+				 0x0000, -1, &work32, 56*4, &i2o_proc_token);
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "LAN Type ........... ");
+	switch (work16[0])
+	{
+	case 0x0030:
+		len += sprintf(buf+len, "Ethernet, ");
+		break;
+	case 0x0040:
+		len += sprintf(buf+len, "100Base VG, ");
+		break;
+	case 0x0050:
+		len += sprintf(buf+len, "Token Ring, ");
+		break;
+	case 0x0060:
+		len += sprintf(buf+len, "FDDI, ");
+		break;
+	case 0x0070:
+		len += sprintf(buf+len, "Fibre Channel, ");
+		break;
+	default:
+		len += sprintf(buf+len, "Unknown type, ");
+		break;
+	}
+
+	if (work16[1]&0x00000001)
+		len += sprintf(buf+len, "emulated LAN, ");
+	else
+		len += sprintf(buf+len, "physical LAN port, ");
+
+	if (work16[1]&0x00000002)
+		len += sprintf(buf+len, "full duplex\n");
+	else
+		len += sprintf(buf+len, "simplex\n");
+
+	len += sprintf(buf+len, "Address format:      ");
+	switch(work8[4]) {
+	case 0x00:
+		len += sprintf(buf+len, "IEEE 48bit\n");
+		break;
+	case 0x01:
+		len += sprintf(buf+len, "FC IEEE\n");
+		break;
+	default:
+		len += sprintf(buf+len, "Unknown\n");
+		break;
+	}
+
+	len += sprintf(buf+len, "State:               ");
+	switch(work8[5])
+	{
+	case 0x00:
+		len += sprintf(buf+len, "Unknown\n");
+		break;
+	case 0x01:
+		len += sprintf(buf+len, "Unclaimed\n");
+		break;
+	case 0x02:
+		len += sprintf(buf+len, "Operational\n");
+		break;
+	case 0x03:
+		len += sprintf(buf+len, "Suspended\n");
+		break;
+	case 0x04:
+		len += sprintf(buf+len, "Resetting\n");
+		break;
+	case 0x05:
+		len += sprintf(buf+len, "Error\n");
+		break;
+	case 0x06:
+		len += sprintf(buf+len, "Operational no Rx\n");
+		break;
+	case 0x07:
+		len += sprintf(buf+len, "Suspended no Rx\n");
+		break;
+	default:
+		len += sprintf(buf+len, "Unspecified\n");
+		break;
+	}
+
+	len += sprintf(buf+len, "Error status:      ");
+	if(work16[3]&0x0001)
+		len += sprintf(buf+len, "Transmit Control Unit Inoperative ");
+	if(work16[3]&0x0002)
+		len += sprintf(buf+len, "Receive Control Unit Inoperative\n");
+	if(work16[3]&0x0004)
+		len += sprintf(buf+len, "Local memory Allocation Error\n");
+	len += sprintf(buf+len, "\n");
+
+	len += sprintf(buf+len, "Min Packet size:     %d\n", work32[2]);
+	len += sprintf(buf+len, "Max Packet size:     %d\n", work32[3]);
+	len += sprintf(buf+len, "HW Address:          "
+		       "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+		       work8[16],work8[17],work8[18],work8[19],
+		       work8[20],work8[21],work8[22],work8[23]);
+
+	len += sprintf(buf+len, "Max Tx Wire Speed:   " FMT_U64_HEX " bps\n", U64_VAL(&work64[3]));
+	len += sprintf(buf+len, "Max Rx Wire Speed:   " FMT_U64_HEX " bps\n", U64_VAL(&work64[4]));
+
+	len += sprintf(buf+len, "Min SDU packet size: 0x%08x\n", work32[10]);
+	len += sprintf(buf+len, "Max SDU packet size: 0x%08x\n", work32[11]);
+
+	spin_unlock(&i2o_proc_lock);
+	return len;
+}
+
+/* LAN group 0001h - MAC address table (scalar) */
+int i2o_proc_read_lan_mac_addr(char *buf, char **start, off_t offset, int len, 
+			       int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u32 work32[48];
+	static u8 *work8 = (u8*)work32;
+	int token;
+
+	spin_lock(&i2o_proc_lock);	
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+				 0x0001, -1, &work32, 48*4, &i2o_proc_token);
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "Active address: "
+		       "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+		       work8[0],work8[1],work8[2],work8[3],
+		       work8[4],work8[5],work8[6],work8[7]);
+	len += sprintf(buf+len, "Current address: "
+		       "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+		       work8[8],work8[9],work8[10],work8[11],
+		       work8[12],work8[13],work8[14],work8[15]);
+	len += sprintf(buf+len, "Functional address mask: "
+		       "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+		       work8[16],work8[17],work8[18],work8[19],
+		       work8[20],work8[21],work8[22],work8[23]);
+
+	len += sprintf(buf+len, "Filter mask: 0x%08x\n", work32[6]);
+	len += sprintf(buf+len, "HW/DDM capabilities: 0x%08x\n", work32[7]);
+	len += sprintf(buf+len, "    Unicast packets %ssupported (%sabled)\n",
+		       (work32[7]&0x00000001)?"":"not ",
+		       (work32[6]&0x00000001)?"en":"dis");
+	len += sprintf(buf+len, "    Promiscuous mode %ssupported (%sabled)\n",
+		       (work32[7]&0x00000002)?"":"not",
+		       (work32[6]&0x00000002)?"en":"dis");
+	len += sprintf(buf+len,
+		       "    Multicast promiscuous mode %ssupported (%sabled)\n",
+		       (work32[7]&0x00000004)?"":"not ",
+		       (work32[6]&0x00000004)?"en":"dis");
+	len += sprintf(buf+len,
+		       "    Broadcast Reception disabling %ssupported (%sabled)\n",
+		       (work32[7]&0x00000100)?"":"not ",
+		       (work32[6]&0x00000100)?"en":"dis");
+	len += sprintf(buf+len,
+		       "    Multicast Reception disabling %ssupported (%sabled)\n",
+		       (work32[7]&0x00000200)?"":"not ",
+		       (work32[6]&0x00000200)?"en":"dis");
+	len += sprintf(buf+len,
+		       "    Functional address disabling %ssupported (%sabled)\n",
+		       (work32[7]&0x00000400)?"":"not ",
+		       (work32[6]&0x00000400)?"en":"dis");
+	len += sprintf(buf+len, "    MAC reporting %ssupported\n",
+		       (work32[7]&0x00000800)?"":"not ");		       
+
+	len += sprintf(buf+len, "    MAC Reporting mode: ");
+	if (work32[6]&0x00000800)
+		len += sprintf(buf+len, "Pass only priority MAC packets\n");
+	else if (work32[6]&0x00001000)
+		len += sprintf(buf+len, "Pass all MAC packets\n");
+	else if (work32[6]&0x00001800)
+		len += sprintf(buf+len, "Pass all MAC packets (promiscuous)\n");
+	else
+		len += sprintf(buf+len, "Do not pass MAC packets\n");
+
+	len += sprintf(buf+len, "Number of multicast addesses: %d\n", work32[8]);
+	len += sprintf(buf+len, "Perfect filtering for max %d multicast addesses\n",
+		       work32[9]);
+	len += sprintf(buf+len, "Imperfect filtering for max %d multicast addesses\n",
+		       work32[10]);
+
+	spin_unlock(&i2o_proc_lock);
+
+	return len;
+}
+
+/* LAN group 0001h, field 1 - Current MAC (scalar) */
+int i2o_proc_read_lan_curr_addr(char *buf, char **start, off_t offset, int len,
+				int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u32 work32[2];
+	static u8 *work8 = (u8*)work32;
+	int token;
+
+	spin_lock(&i2o_proc_lock);	
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+				 0x0001, 2, &work32, 8, &i2o_proc_token);
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "Current address: "
+		       "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+		       work8[0],work8[1],work8[2],work8[3],
+		       work8[4],work8[5],work8[6],work8[7]);
+
+	spin_unlock(&i2o_proc_lock);
+	return len;
+}
+
+
+#if 0
+/* LAN group 0002h - Multicast MAC address table (table) */
+int i2o_proc_read_lan_mcast_addr(char *buf, char **start, off_t offset, int len, 
+				 int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u8 work8[32];
+	static u32 field32[8];
+	static u8 *field8 = (u8 *)field32;
+	int token;
+
+	spin_lock(&i2o_proc_lock);	
+	len = 0;
+
+	token = i2o_query_table_polled(d->controller, d->id, &work8, 32,
+				       0x0002, 0, field32, 8);
+
+	switch (token) {
+	case -ETIMEDOUT:
+		len += sprintf(buf, "Timeout reading table.\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+		break;
+	case -ENOMEM:
+		len += sprintf(buf, "No free memory to read the table.\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+		break;
+	case -EBADR:
+		len += sprintf(buf, "Error reading field.\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+		break;
+	default:
+		break;
+	}
+
+	len += sprintf(buf, "Multicast MAC address: "
+		       "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+		       field8[0],field8[1],field8[2],field8[3],
+		       field8[4],field8[5],field8[6],field8[7]);
+
+	spin_unlock(&i2o_proc_lock);
+	return len;
+}
+#endif
+
+/* LAN group 0003h - Batch Control (scalar) */
+int i2o_proc_read_lan_batch_control(char *buf, char **start, off_t offset,
+				    int len, int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u32 work32[18];
+	int token;
+
+	spin_lock(&i2o_proc_lock);	
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+				 0x0003, -1, &work32, 72, &i2o_proc_token);
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "Batch mode ");
+	if (work32[0]&0x00000001)
+		len += sprintf(buf+len, "disabled");
+	else
+		len += sprintf(buf+len, "enabled");
+	if (work32[0]&0x00000002)
+		len += sprintf(buf+len, " (current setting)");
+	if (work32[0]&0x00000004)
+		len += sprintf(buf+len, ", forced");
+	else
+		len += sprintf(buf+len, ", toggle");
+	len += sprintf(buf+len, "\n");
+
+	if(d->i2oversion == 0x00) { /* Reserved in 1.53 and 2.0 */
+		len += sprintf(buf+len, "Rising Load Delay: %d ms\n",
+			       work32[1]/10);
+		len += sprintf(buf+len, "Rising Load Threshold: %d ms\n",
+			       work32[2]/10);
+		len += sprintf(buf+len, "Falling Load Delay: %d ms\n",
+			       work32[3]/10);
+		len += sprintf(buf+len, "Falling Load Threshold: %d ms\n",
+			       work32[4]/10);
+	}
+
+	len += sprintf(buf+len, "Max Rx Batch Count: %d\n", work32[5]);
+	len += sprintf(buf+len, "Max Rx Batch Delay: %d\n", work32[6]);
+
+	if(d->i2oversion == 0x00) {
+		len += sprintf(buf+len,
+			       "Transmission Completion Reporting Delay: %d ms\n",
+			       work32[7]);
+	} else {
+		len += sprintf(buf+len, "Max Tx Batch Delay: %d\n", work32[7]);
+		len += sprintf(buf+len, "Max Tx Batch Count: %d\n", work32[8]);
+	}
+
+	spin_unlock(&i2o_proc_lock);
+	return len;
+}
+
+/* LAN group 0004h - LAN Operation (scalar) */
+int i2o_proc_read_lan_operation(char *buf, char **start, off_t offset, int len,
+				int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u32 work32[5];
+	int token;
+
+	spin_lock(&i2o_proc_lock);	
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+				 0x0004, -1, &work32, 20, &i2o_proc_token);
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "Packet prepadding (32b words): %d\n", work32[0]);
+	len += sprintf(buf+len, "Transmission error reporting: %s\n",
+		       (work32[1]&1)?"on":"off");
+	len += sprintf(buf+len, "Bad packet handling: %s\n",
+		       (work32[1]&0x2)?"by host":"by DDM");		      
+	len += sprintf(buf+len, "Packet orphan limit: %d\n", work32[2]);
+
+	len += sprintf(buf+len, "Tx modes:\n");
+	if (work32[3]&0x00000004)
+		len += sprintf(buf+len, "    HW CRC supressed\n");
+	else
+		len += sprintf(buf+len, "    HW CRC\n");
+	if (work32[3]&0x00000100)
+		len += sprintf(buf+len, "    HW IPv4 checksumming\n");
+	if (work32[3]&0x00000200)
+		len += sprintf(buf+len, "    HW TCP checksumming\n");
+	if (work32[3]&0x00000400)
+		len += sprintf(buf+len, "    HW UDP checksumming\n");
+	if (work32[3]&0x00000800)
+		len += sprintf(buf+len, "    HW RSVP checksumming\n");
+	if (work32[3]&0x00001000)
+		len += sprintf(buf+len, "    HW ICMP checksumming\n");
+	if (work32[3]&0x00002000)
+		len += sprintf(buf+len, "    Loopback packet not delivered\n");
+
+	len += sprintf(buf+len, "Rx modes:\n");
+	if (work32[4]&0x00000004)
+		len += sprintf(buf+len, "    FCS in payload\n");
+	if (work32[4]&0x00000100)
+		len += sprintf(buf+len, "    HW IPv4 checksum validation\n");
+	if (work32[4]&0x00000200)
+		len += sprintf(buf+len, "    HW TCP checksum validation\n");
+	if (work32[4]&0x00000400)
+		len += sprintf(buf+len, "    HW UDP checksum validation\n");
+	if (work32[4]&0x00000800)
+		len += sprintf(buf+len, "    HW RSVP checksum validation\n");
+	if (work32[4]&0x00001000)
+		len += sprintf(buf+len, "    HW ICMP checksum validation\n");
+ 
+	spin_unlock(&i2o_proc_lock);
+	return len;
+}
+
+/* LAN group 0005h - Media operation (scalar) */
+int i2o_proc_read_lan_media_operation(char *buf, char **start, off_t offset,
+				      int len, int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u32 work32[9];
+	static u8 *work8 = (u8*)work32;
+        static u64 *work64 = (u64*)work32;
+	int token;
+
+	spin_lock(&i2o_proc_lock);	
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+				 0x0005, -1, &work32, 36, &i2o_proc_token);
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "Connector type: ");
+	switch(work32[0])
+	{
+	case 0x00000000:
+		len += sprintf(buf+len, "OTHER\n");
+		break;
+	case 0x00000001:
+		len += sprintf(buf+len, "UNKNOWN\n");
+		break;
+	case 0x00000002:
+		len += sprintf(buf+len, "AUI\n");
+		break;
+	case 0x00000003:
+		len += sprintf(buf+len, "UTP\n");
+		break;
+	case 0x00000004:
+		len += sprintf(buf+len, "BNC\n");
+		break;
+	case 0x00000005:
+		len += sprintf(buf+len, "RJ45\n");
+		break;
+	case 0x00000006:
+		len += sprintf(buf+len, "STP DB9\n");
+		break;
+	case 0x00000007:
+		len += sprintf(buf+len, "FIBER MIC\n");
+		break;
+	case 0x00000008:
+		len += sprintf(buf+len, "APPLE AUI\n");
+		break;
+	case 0x00000009:
+		len += sprintf(buf+len, "MII\n");
+		break;
+	case 0x0000000A:
+		len += sprintf(buf+len, "DB9\n");
+		break;
+	case 0x0000000B:
+		len += sprintf(buf+len, "HSSDC\n");
+		break;
+	case 0x0000000C:
+		len += sprintf(buf+len, "DUPLEX SC FIBER\n");
+		break;
+	case 0x0000000D:
+		len += sprintf(buf+len, "DUPLEX ST FIBER\n");
+		break;
+	case 0x0000000E:
+		len += sprintf(buf+len, "TNC/BNC\n");
+		break;
+	case 0xFFFFFFFF:
+		len += sprintf(buf+len, "HW DEFAULT\n");
+		break;
+	}
+
+	len += sprintf(buf+len, "Connection type: ");
+	switch(work32[1])
+	{
+	case I2O_LAN_UNKNOWN:
+		len += sprintf(buf+len, "UNKNOWN\n");
+		break;
+	case I2O_LAN_AUI:
+		len += sprintf(buf+len, "AUI\n");
+		break;
+	case I2O_LAN_10BASE5:
+		len += sprintf(buf+len, "10BASE5\n");
+		break;
+	case I2O_LAN_FIORL:
+		len += sprintf(buf+len, "FIORL\n");
+		break;
+	case I2O_LAN_10BASE2:
+		len += sprintf(buf+len, "10BASE2\n");
+		break;
+	case I2O_LAN_10BROAD36:
+		len += sprintf(buf+len, "10BROAD36\n");
+		break;
+	case I2O_LAN_10BASE_T:
+		len += sprintf(buf+len, "10BASE-T\n");
+		break;
+	case I2O_LAN_10BASE_FP:
+		len += sprintf(buf+len, "10BASE-FP\n");
+		break;
+	case I2O_LAN_10BASE_FB:
+		len += sprintf(buf+len, "10BASE-FB\n");
+		break;
+	case I2O_LAN_10BASE_FL:
+		len += sprintf(buf+len, "10BASE-FL\n");
+		break;
+	case I2O_LAN_100BASE_TX:
+		len += sprintf(buf+len, "100BASE-TX\n");
+		break;
+	case I2O_LAN_100BASE_FX:
+		len += sprintf(buf+len, "100BASE-FX\n");
+		break;
+	case I2O_LAN_100BASE_T4:
+		len += sprintf(buf+len, "100BASE-T4\n");
+		break;
+	case I2O_LAN_1000BASE_SX:
+		len += sprintf(buf+len, "1000BASE-SX\n");
+		break;
+	case I2O_LAN_1000BASE_LX:
+		len += sprintf(buf+len, "1000BASE-LX\n");
+		break;
+	case I2O_LAN_1000BASE_CX:
+		len += sprintf(buf+len, "1000BASE-CX\n");
+		break;
+	case I2O_LAN_1000BASE_T:
+		len += sprintf(buf+len, "1000BASE-T\n");
+		break;
+	case I2O_LAN_100VG_ETHERNET:
+		len += sprintf(buf+len, "100VG-ETHERNET\n");
+		break;
+	case I2O_LAN_100VG_TR:
+		len += sprintf(buf+len, "100VG-TOKEN RING\n");
+		break;
+	case I2O_LAN_4MBIT:
+		len += sprintf(buf+len, "4MBIT TOKEN RING\n");
+		break;
+	case I2O_LAN_16MBIT:
+		len += sprintf(buf+len, "16 Mb Token Ring\n");
+		break;
+	case I2O_LAN_125MBAUD:
+		len += sprintf(buf+len, "125 MBAUD FDDI\n");
+		break;
+	case I2O_LAN_POINT_POINT:
+		len += sprintf(buf+len, "Point-to-point\n");
+		break;
+	case I2O_LAN_ARB_LOOP:
+		len += sprintf(buf+len, "Arbitrated loop\n");
+		break;
+	case I2O_LAN_PUBLIC_LOOP:
+		len += sprintf(buf+len, "Public loop\n");
+		break;
+	case I2O_LAN_FABRIC:
+		len += sprintf(buf+len, "Fabric\n");
+		break;
+	case I2O_LAN_EMULATION:
+		len += sprintf(buf+len, "Emulation\n");
+		break;
+	case I2O_LAN_OTHER:
+		len += sprintf(buf+len, "Other\n");
+		break;
+	case I2O_LAN_DEFAULT:
+		len += sprintf(buf+len, "HW default\n");
+		break;
+	}
+
+	len += sprintf(buf+len, "Current Tx Wire Speed: " FMT_U64_HEX " bps\n",
+		       U64_VAL(&work64[1]));
+	len += sprintf(buf+len, "Current Rx Wire Speed: " FMT_U64_HEX " bps\n",
+		       U64_VAL(&work64[2]));
+
+	len += sprintf(buf+len, "%s duplex\n", (work8[24]&1)?"Full":"Half");
+
+	len += sprintf(buf+len, "Link status: ");
+	if(work8[25] == 0x00)
+		len += sprintf(buf+len, "Unknown\n");
+	else if(work8[25] == 0x01)
+		len += sprintf(buf+len, "Normal\n");
+	else if(work8[25] == 0x02)
+		len += sprintf(buf+len, "Failure\n");
+	else if(work8[25] == 0x03)
+		len += sprintf(buf+len, "Reset\n");
+	else
+		len += sprintf(buf+len, "Unspecified\n");
+
+	if (d->i2oversion == 0x00) { /* Reserved in 1.53 and 2.0 */
+		len += sprintf(buf+len, "Bad packets handled by: %s\n",
+			       (work8[26] == 0xFF)?"host":"DDM");
+	}
+	if (d->i2oversion != 0x00) {
+		len += sprintf(buf+len, "Duplex mode target: ");
+		switch (work8[27]) {
+		case 0:
+			len += sprintf(buf+len, "Half Duplex\n");
+			break;
+		case 1:
+			len += sprintf(buf+len, "Full Duplex\n");
+			break;
+		default:
+			len += sprintf(buf+len, "\n");
+			break;
+		}
+
+		len += sprintf(buf+len, "Connector type target: ");
+		switch(work32[7])
+		{
+		case 0x00000000:
+			len += sprintf(buf+len, "OTHER\n");
+			break;
+		case 0x00000001:
+			len += sprintf(buf+len, "UNKNOWN\n");
+			break;
+		case 0x00000002:
+			len += sprintf(buf+len, "AUI\n");
+			break;
+		case 0x00000003:
+			len += sprintf(buf+len, "UTP\n");
+			break;
+		case 0x00000004:
+			len += sprintf(buf+len, "BNC\n");
+			break;
+		case 0x00000005:
+			len += sprintf(buf+len, "RJ45\n");
+			break;
+		case 0x00000006:
+			len += sprintf(buf+len, "STP DB9\n");
+			break;
+		case 0x00000007:
+			len += sprintf(buf+len, "FIBER MIC\n");
+			break;
+		case 0x00000008:
+			len += sprintf(buf+len, "APPLE AUI\n");
+			break;
+		case 0x00000009:
+			len += sprintf(buf+len, "MII\n");
+			break;
+		case 0x0000000A:
+			len += sprintf(buf+len, "DB9\n");
+			break;
+		case 0x0000000B:
+			len += sprintf(buf+len, "HSSDC\n");
+			break;
+		case 0x0000000C:
+			len += sprintf(buf+len, "DUPLEX SC FIBER\n");
+			break;
+		case 0x0000000D:
+			len += sprintf(buf+len, "DUPLEX ST FIBER\n");
+			break;
+		case 0x0000000E:
+			len += sprintf(buf+len, "TNC/BNC\n");
+			break;
+		case 0xFFFFFFFF:
+			len += sprintf(buf+len, "HW DEFAULT\n");
+			break;
+		default:
+			len += sprintf(buf+len, "\n");
+			break;
+		}
+
+		len += sprintf(buf+len, "Connection type target: ");
+		switch(work32[8])
+		{
+		case I2O_LAN_UNKNOWN:
+			len += sprintf(buf+len, "UNKNOWN\n");
+			break;
+		case I2O_LAN_AUI:
+			len += sprintf(buf+len, "AUI\n");
+			break;
+		case I2O_LAN_10BASE5:
+			len += sprintf(buf+len, "10BASE5\n");
+			break;
+		case I2O_LAN_FIORL:
+			len += sprintf(buf+len, "FIORL\n");
+			break;
+		case I2O_LAN_10BASE2:
+			len += sprintf(buf+len, "10BASE2\n");
+			break;
+		case I2O_LAN_10BROAD36:
+			len += sprintf(buf+len, "10BROAD36\n");
+			break;
+		case I2O_LAN_10BASE_T:
+			len += sprintf(buf+len, "10BASE-T\n");
+			break;
+		case I2O_LAN_10BASE_FP:
+			len += sprintf(buf+len, "10BASE-FP\n");
+			break;
+		case I2O_LAN_10BASE_FB:
+			len += sprintf(buf+len, "10BASE-FB\n");
+			break;
+		case I2O_LAN_10BASE_FL:
+			len += sprintf(buf+len, "10BASE-FL\n");
+			break;
+		case I2O_LAN_100BASE_TX:
+			len += sprintf(buf+len, "100BASE-TX\n");
+			break;
+		case I2O_LAN_100BASE_FX:
+			len += sprintf(buf+len, "100BASE-FX\n");
+			break;
+		case I2O_LAN_100BASE_T4:
+			len += sprintf(buf+len, "100BASE-T4\n");
+			break;
+		case I2O_LAN_1000BASE_SX:
+			len += sprintf(buf+len, "1000BASE-SX\n");
+			break;
+		case I2O_LAN_1000BASE_LX:
+			len += sprintf(buf+len, "1000BASE-LX\n");
+			break;
+		case I2O_LAN_1000BASE_CX:
+			len += sprintf(buf+len, "1000BASE-CX\n");
+			break;
+		case I2O_LAN_1000BASE_T:
+			len += sprintf(buf+len, "1000BASE-T\n");
+			break;
+		case I2O_LAN_100VG_ETHERNET:
+			len += sprintf(buf+len, "100VG-ETHERNET\n");
+			break;
+		case I2O_LAN_100VG_TR:
+			len += sprintf(buf+len, "100VG-TOKEN RING\n");
+			break;
+		case I2O_LAN_4MBIT:
+			len += sprintf(buf+len, "4MBIT TOKEN RING\n");
+			break;
+		case I2O_LAN_16MBIT:
+			len += sprintf(buf+len, "16 Mb Token Ring\n");
+			break;
+		case I2O_LAN_125MBAUD:
+			len += sprintf(buf+len, "125 MBAUD FDDI\n");
+			break;
+		case I2O_LAN_POINT_POINT:
+			len += sprintf(buf+len, "Point-to-point\n");
+			break;
+		case I2O_LAN_ARB_LOOP:
+			len += sprintf(buf+len, "Arbitrated loop\n");
+			break;
+		case I2O_LAN_PUBLIC_LOOP:
+			len += sprintf(buf+len, "Public loop\n");
+			break;
+		case I2O_LAN_FABRIC:
+			len += sprintf(buf+len, "Fabric\n");
+			break;
+		case I2O_LAN_EMULATION:
+			len += sprintf(buf+len, "Emulation\n");
+			break;
+		case I2O_LAN_OTHER:
+			len += sprintf(buf+len, "Other\n");
+			break;
+		case I2O_LAN_DEFAULT:
+			len += sprintf(buf+len, "HW default\n");
+			break;
+		default:
+			len += sprintf(buf+len, "\n");
+			break;
+		}
+	}
+	spin_unlock(&i2o_proc_lock);
+	return len;
+}
+
+#if 0
+/* LAN group 0006h - Alternate address (table) */
+int i2o_proc_read_lan_alt_addr(char *buf, char **start, off_t offset, int len,
+			       int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u8 work8[32];
+	static u32 field32[2];
+	static u8 *field8 = (u8 *)field32;
+	int token;
+
+	spin_lock(&i2o_proc_lock);	
+	len = 0;
+
+	token = i2o_query_table_polled(d->controller, d->id, &work8, 32,
+				       0x0006, 0, field32, 8);
+	switch (token) {
+	case -ETIMEDOUT:
+		len += sprintf(buf, "Timeout reading table.\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+		break;
+	case -ENOMEM:
+		len += sprintf(buf, "No free memory to read the table.\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+		break;
+	case -EBADR:
+		len += sprintf(buf, "Error reading field.\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+		break;
+	default:
+		break;
+	}
+
+	len += sprintf(buf, "Alternate Address: "
+		       "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+                       field8[0],field8[1],field8[2],field8[3],
+                       field8[4],field8[5],field8[6],field8[7]);
+
+	spin_unlock(&i2o_proc_lock);
+	return len;
+}
+#endif
+
+/* LAN group 0007h - Transmit info (scalar) */
+int i2o_proc_read_lan_tx_info(char *buf, char **start, off_t offset, int len, 
+				    int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u32 work32[10];
+	int token;
+
+	spin_lock(&i2o_proc_lock);	
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+				 0x0007, -1, &work32, 8, &i2o_proc_token);
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "Max SG Elements per packet: %d\n", work32[0]);
+	len += sprintf(buf+len, "Max SG Elements per chain: %d\n", work32[1]);
+	len += sprintf(buf+len, "Max outstanding packets: %d\n", work32[2]);
+	len += sprintf(buf+len, "Max packets per request: %d\n", work32[3]);
+
+	len += sprintf(buf+len, "Tx modes:\n");
+	if(work32[4]&0x00000002)
+		len += sprintf(buf+len, "    No DA in SGL\n");
+	if(work32[4]&0x00000004)
+		len += sprintf(buf+len, "    CRC suppression\n");
+	if(work32[4]&0x00000008)
+		len += sprintf(buf+len, "    Loop suppression\n");
+	if(work32[4]&0x00000010)
+		len += sprintf(buf+len, "    MAC insertion\n");
+	if(work32[4]&0x00000020)
+		len += sprintf(buf+len, "    RIF insertion\n");
+	if(work32[4]&0x00000100)
+		len += sprintf(buf+len, "    IPv4 Checksum\n");
+	if(work32[4]&0x00000200)
+		len += sprintf(buf+len, "    TCP Checksum\n");
+	if(work32[4]&0x00000400)
+		len += sprintf(buf+len, "    UDP Checksum\n");
+	if(work32[4]&0x00000800)
+		len += sprintf(buf+len, "    RSVP Checksum\n");
+	if(work32[4]&0x00001000)
+		len += sprintf(buf+len, "    ICMP Checksum\n");
+	if (d->i2oversion == 0x00) {
+		if(work32[4]&0x00008000)
+			len += sprintf(buf+len, "    Loopback Enabled\n");
+		if(work32[4]&0x00010000)
+			len += sprintf(buf+len, "    Loopback Suppression Enabled\n");
+	} else {
+		if(work32[4]&0x00010000)
+			len += sprintf(buf+len, "    Loopback Enabled\n");
+		if(work32[4]&0x00020000)
+			len += sprintf(buf+len, "    Loopback Suppression Enabled\n");
+	}
+
+	spin_unlock(&i2o_proc_lock);
+	return len;
+}
+
+/* LAN group 0008h - Receive info (scalar) */
+int i2o_proc_read_lan_rx_info(char *buf, char **start, off_t offset, int len, 
+				    int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u32 work32[10];
+	int token;
+
+	spin_lock(&i2o_proc_lock);	
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+				 0x0008, -1, &work32, 8, &i2o_proc_token);
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "Max size of chain element: %d\n", work32[0]);
+	len += sprintf(buf+len, "Max number of buckets: %d\n", work32[1]);
+
+	if (d->i2oversion > 0x00) { /* not in 1.5 */
+		len += sprintf(buf+len, "Rx modes: %d\n", work32[2]);
+		len += sprintf(buf+len, "RxMaxBucketsReply: %d\n", work32[3]);
+		len += sprintf(buf+len, "RxMaxPacketsPerBuckets: %d\n", work32[4]);
+		len += sprintf(buf+len, "RxMaxPostBuckets: %d\n", work32[5]);
+	}
+
+	spin_unlock(&i2o_proc_lock);
+	return len;
+}
+
+
+/* LAN group 0100h - LAN Historical statistics (scalar) */
+int i2o_proc_read_lan_hist_stats(char *buf, char **start, off_t offset, int len,
+				 int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u64 work64[9];
+	int token;
+
+	spin_lock(&i2o_proc_lock);	
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+				 0x0100, -1, &work64, 9*8, &i2o_proc_token);
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "Tx packets: " FMT_U64_HEX "\n", U64_VAL(&work64[0]));
+	len += sprintf(buf+len, "Tx bytes: " FMT_U64_HEX "\n", U64_VAL(&work64[1]));
+	len += sprintf(buf+len, "Rx packets: " FMT_U64_HEX "\n", U64_VAL(&work64[2]));
+	len += sprintf(buf+len, "Rx bytes: " FMT_U64_HEX "\n", U64_VAL(&work64[3]));
+	len += sprintf(buf+len, "Tx errors: " FMT_U64_HEX "\n", U64_VAL(&work64[4]));
+	len += sprintf(buf+len, "Rx errors: " FMT_U64_HEX "\n", U64_VAL(&work64[5]));
+	len += sprintf(buf+len, "Rx dropped: " FMT_U64_HEX "\n", U64_VAL(&work64[6]));
+	len += sprintf(buf+len, "Adapter resets: " FMT_U64_HEX "\n", U64_VAL(&work64[7]));
+	len += sprintf(buf+len, "Adapter suspends: " FMT_U64_HEX "\n", U64_VAL(&work64[8]));
+
+	spin_unlock(&i2o_proc_lock);
+	return len;
+}
+
+
+/* LAN group 0182h - Optional Non Media Specific Transmit Historical Statistics
+ * (scalar) */
+int i2o_proc_read_lan_opt_tx_hist_stats(char *buf, char **start, off_t offset,
+					int len, int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u64 work64[9];
+	int token;
+
+	spin_lock(&i2o_proc_lock);	
+
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+				 0x0182, -1, &work64, 9*8, &i2o_proc_token);
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "TxRetryCount: " FMT_U64_HEX "\n", U64_VAL(&work64[0]));
+	len += sprintf(buf+len, "DirectedBytesTx: " FMT_U64_HEX "\n", U64_VAL(&work64[1]));
+	len += sprintf(buf+len, "DirectedPacketsTx: " FMT_U64_HEX "\n", U64_VAL(&work64[2]));
+	len += sprintf(buf+len, "MulticastBytesTx: " FMT_U64_HEX "\n", U64_VAL(&work64[3]));
+	len += sprintf(buf+len, "MulticastPacketsTx: " FMT_U64_HEX "\n", U64_VAL(&work64[4]));
+	len += sprintf(buf+len, "BroadcastBytesTx: " FMT_U64_HEX "\n", U64_VAL(&work64[5]));
+	len += sprintf(buf+len, "BroadcastPacketsTx: " FMT_U64_HEX "\n", U64_VAL(&work64[6]));
+	len += sprintf(buf+len, "TotalGroupAddrTxCount: " FMT_U64_HEX "\n", U64_VAL(&work64[7]));
+	len += sprintf(buf+len, "TotalTxPacketsTooShort: " FMT_U64_HEX "\n", U64_VAL(&work64[8]));
+
+	spin_unlock(&i2o_proc_lock);
+	return len;
+}
+
+/* LAN group 0183h - Optional Non Media Specific Receive Historical Statistics
+ * (scalar) */
+int i2o_proc_read_lan_opt_rx_hist_stats(char *buf, char **start, off_t offset,
+					int len, int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u64 work64[11];
+	int token;
+
+	spin_lock(&i2o_proc_lock);	
+
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+				 0x0183, -1, &work64, 11*8, &i2o_proc_token);
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "ReceiveCRCErrorCount: " FMT_U64_HEX "\n", U64_VAL(&work64[0]));
+	len += sprintf(buf+len, "DirectedBytesRx: " FMT_U64_HEX "\n", U64_VAL(&work64[1]));
+	len += sprintf(buf+len, "DirectedPacketsRx: " FMT_U64_HEX "\n", U64_VAL(&work64[2]));
+	len += sprintf(buf+len, "MulticastBytesRx: " FMT_U64_HEX "\n", U64_VAL(&work64[3]));
+	len += sprintf(buf+len, "MulticastPacketsRx: " FMT_U64_HEX "\n", U64_VAL(&work64[4]));
+	len += sprintf(buf+len, "BroadcastBytesRx: " FMT_U64_HEX "\n", U64_VAL(&work64[5]));
+	len += sprintf(buf+len, "BroadcastPacketsRx: " FMT_U64_HEX "\n", U64_VAL(&work64[6]));
+	len += sprintf(buf+len, "TotalGroupAddrRxCount: " FMT_U64_HEX "\n", U64_VAL(&work64[7]));
+	len += sprintf(buf+len, "TotalRxPacketsTooShort: " FMT_U64_HEX "\n", U64_VAL(&work64[8]));
+	len += sprintf(buf+len, "TotalRxPacketsTooLong: " FMT_U64_HEX "\n", U64_VAL(&work64[9]));
+	len += sprintf(buf+len, "TotalRuntPacketsReceived: " FMT_U64_HEX "\n", U64_VAL(&work64[10]));
+
+	spin_unlock(&i2o_proc_lock);
+	return len;
+}
+
+
+/* LAN group 0400h - Required FDDI Statistics (scalar) */
+int i2o_proc_read_lan_fddi_stats(char *buf, char **start, off_t offset,
+				 int len, int *eof, void *data)
+{
+	struct i2o_device *d = (struct i2o_device*)data;
+	static u64 work64[11];
+	int token;
+
+	static char *conf_state[] =
+	{
+		"Isolated",
+		"Local a",
+		"Local b",
+		"Local ab",
+		"Local s",
+		"Wrap a",
+		"Wrap b",
+		"Wrap ab",
+		"Wrap s",
+		"C-Wrap a",
+		"C-Wrap b",
+		"C-Wrap s",
+		"Through",
+	};
+
+	static char *ring_state[] =
+	{
+		"Isolated",
+		"Non-op",
+		"Rind-op",
+		"Detect",
+		"Non-op-Dup",
+		"Ring-op-Dup",
+		"Directed",
+		"Trace"
+	};
+
+	static char *link_state[] =
+	{
+		"Off",
+		"Break",
+		"Trace",
+		"Connect",
+		"Next",
+		"Signal",
+		"Join",
+		"Verify",
+		"Active",
+		"Maintenance"
+	};
+
+	spin_lock(&i2o_proc_lock);
+	
+	len = 0;
+
+	token = i2o_query_scalar(d->controller, d->id, proc_context, 
+				 0x0400, -1, &work64, 11*8, &i2o_proc_token);
+	if(token < 0)
+	{
+		len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+		spin_unlock(&i2o_proc_lock);
+		return len;
+	}
+
+	len += sprintf(buf, "ConfigurationState: %s\n", conf_state[work64[0]]);
+	len += sprintf(buf+len, "UpstreamNode: " FMT_U64_HEX "\n", U64_VAL(&work64[1]));
+	len += sprintf(buf+len, "DownStreamNode: " FMT_U64_HEX "\n", U64_VAL(&work64[2]));
+	len += sprintf(buf+len, "FrameErrors: " FMT_U64_HEX "\n", U64_VAL(&work64[3]));
+	len += sprintf(buf+len, "FramesLost: " FMT_U64_HEX "\n", U64_VAL(&work64[4]));
+	len += sprintf(buf+len, "RingMgmtState: %s\n", ring_state[work64[5]]);
+	len += sprintf(buf+len, "LCTFailures: " FMT_U64_HEX "\n", U64_VAL(&work64[6]));
+	len += sprintf(buf+len, "LEMRejects: " FMT_U64_HEX "\n", U64_VAL(&work64[7]));
+	len += sprintf(buf+len, "LEMCount: " FMT_U64_HEX "\n", U64_VAL(&work64[8]));
+	len += sprintf(buf+len, "LConnectionState: %s\n", link_state[work64[9]]);
+
+	spin_unlock(&i2o_proc_lock);
+	return len;
+}
+
+static int i2o_proc_create_entries(void *data,
+	i2o_proc_entry *pentry, struct proc_dir_entry *parent)
+{
+	struct proc_dir_entry *ent;
+	
+	while(pentry->name != NULL)
+	{
+		ent = create_proc_entry(pentry->name, pentry->mode, parent);
+		if(!ent) return -1;
+
+		ent->data = data;
+		ent->read_proc = pentry->read_proc;
+		ent->write_proc = pentry->write_proc;
+		ent->nlink = 1;
+
+		pentry++;
+	}
+
+	return 0;
+}
+
+static void i2o_proc_remove_entries(i2o_proc_entry *pentry, 
+	struct proc_dir_entry *parent)
+{
+	while(pentry->name != NULL)
+	{
+		remove_proc_entry(pentry->name, parent);
+		pentry++;
+	}
+}
+
+static int i2o_proc_add_controller(struct i2o_controller *pctrl, 
+	struct proc_dir_entry *root )
+{
+	struct proc_dir_entry *dir, *dir1;
+	struct i2o_device *dev;
+	char buff[10];
+
+	sprintf(buff, "iop%d", pctrl->unit);
+
+	dir = create_proc_entry(buff, S_IFDIR, root);
+	if(!dir)
+		return -1;
+
+	pctrl->proc_entry = dir;
+
+	i2o_proc_create_entries(pctrl, generic_iop_entries, dir);
+	
+	for(dev = pctrl->devices; dev; dev = dev->next)
+	{
+		sprintf(buff, "%0#5x", dev->id);
+
+		dir1 = create_proc_entry(buff, S_IFDIR, dir);
+		dev->proc_entry = dir1;
+
+		if(!dir1)
+			printk(KERN_INFO "i2o_proc: Could not allocate proc dir\n");
+		
+		i2o_proc_create_entries(dev, generic_dev_entries, dir1);
+
+		switch(dev->class)
+		{
+		case I2O_CLASS_SCSI_PERIPHERAL:
+		case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+			i2o_proc_create_entries(dev, rbs_dev_entries, dir1);
+			break;
+		case I2O_CLASS_LAN:
+			i2o_proc_create_entries(dev, lan_entries, dir1);
+			break;
+		default:
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static void i2o_proc_remove_controller(struct i2o_controller *pctrl, 
+	struct proc_dir_entry *parent)
+{
+	char buff[10];
+
+	sprintf(buff, "iop%d", pctrl->unit);
+
+	i2o_proc_remove_entries(generic_iop_entries, pctrl->proc_entry);
+
+	remove_proc_entry(buff, parent);
+
+	pctrl->proc_entry = NULL;
+}
+
+static int create_i2o_procfs(void)
+{
+	struct i2o_controller *pctrl = NULL;
+	int i;
+
+	i2o_proc_dir_root = create_proc_entry("i2o", S_IFDIR, 0);
+	if(!i2o_proc_dir_root)
+		return -1;
+
+	for(i = 0; i < MAX_I2O_CONTROLLERS; i++)
+	{
+		pctrl = i2o_find_controller(i);
+		if(pctrl)
+			i2o_proc_add_controller(pctrl, i2o_proc_dir_root);
+	};
+
+	return 0;
+}
+
+static int destroy_i2o_procfs(void)
+{
+	struct i2o_controller *pctrl = NULL;
+	int i;
+
+	if(!i2o_find_controller(0))
+		return -1;
+	
+	for(i = 0; i < MAX_I2O_CONTROLLERS; i++)
+	{
+		pctrl = i2o_find_controller(i);
+		if(pctrl)
+			i2o_proc_remove_controller(pctrl, i2o_proc_dir_root);
+	};
+
+	remove_proc_entry("i2o", 0);
+	return 0;
+}
+		
+#ifdef MODULE
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("I2O procfs Handler");
+
+int init_module(void)
+{
+	if(create_i2o_procfs())
+		return -EBUSY;
+
+	if (i2o_install_handler(&i2o_proc_handler) < 0)
+	{
+		printk(KERN_ERR "i2o_proc: Unable to install PROC handler.\n");
+		return 0;
+	}
+
+	proc_context = i2o_proc_handler.context;
+
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	destroy_i2o_procfs();
+	i2o_remove_handler(&i2o_proc_handler);
+}
+#endif
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/i2o_proc.h linux/drivers/i2o/i2o_proc.h
--- v2.3.5/linux/drivers/i2o/i2o_proc.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/i2o_proc.h	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,141 @@
+#ifndef i2oproc_h
+#define i2oproc_h
+
+/*
+ * Fixme: make this dependent on architecture
+ * The official header files to this already...but we can't use them
+ */
+#define     I2O_64BIT_CONTEXT          0
+
+typedef struct _i2o_msg {
+	u8 ver_offset;
+	u8 msg_flags;
+	u16 msg_size;
+	u32 target_addr:12;
+	u32 initiator_addr:12;
+	u32 function:8;
+	u32 init_context;	/* FIXME: 64-bit support! */
+} i2o_msg, *pi2o_msg;
+
+typedef struct _i2o_reply_message {
+	i2o_msg msg_frame;
+	u32 tctx;		/* FIXME: 64-bit */
+	u16 detailed_status_code;
+	u8 reserved;
+	u8 req_status;
+} i2o_reply_msg, *pi2o_reply_msg;
+
+typedef struct _i2o_mult_reply_message {
+	i2o_msg msg_frame;
+	u32 tctx;		/* FIXME: 64-bit */
+	u16 detailed_status_code;
+	u8 reserved;
+	u8 req_status;
+} i2o_mult_reply_msg, *pi2o_mult_reply_msg;
+
+/**************************************************************************
+ * HRT related constants and structures
+ **************************************************************************/
+#define    I2O_BUS_LOCAL                               0
+#define    I2O_BUS_ISA                                 1
+#define    I2O_BUS_EISA                                2
+#define    I2O_BUS_MCA                                 3
+#define    I2O_BUS_PCI                                 4
+#define    I2O_BUS_PCMCIA                              5
+#define    I2O_BUS_NUBUS                               6
+#define    I2O_BUS_CARDBUS                             7
+#define    I2O_BUS_UNKNOWN                            0x80
+
+typedef struct _i2o_pci_bus {
+	u8 PciFunctionNumber;
+	u8 PciDeviceNumber;
+	u8 PciBusNumber;
+	u8 reserved;
+	u16 PciVendorID;
+	u16 PciDeviceID;
+} i2o_pci_bus, *pi2o_pci_bus;
+
+typedef struct _i2o_local_bus {
+	u16 LbBaseIOPort;
+	u16 reserved;
+	u32 LbBaseMemoryAddress;
+} i2o_local_bus, *pi2o_local_bus;
+
+typedef struct _i2o_isa_bus {
+	u16 IsaBaseIOPort;
+	u8 CSN;
+	u8 reserved;
+	u32 IsaBaseMemoryAddress;
+} i2o_isa_bus, *pi2o_isa_bus;
+
+/* I2O_EISA_BUS_INFO  */
+typedef struct _i2o_eisa_bus_info {
+	u16 EisaBaseIOPort;
+	u8 reserved;
+	u8 EisaSlotNumber;
+	u32 EisaBaseMemoryAddress;
+} i2o_eisa_bus, *pi2o_eisa_bus;
+
+typedef struct _i2o_mca_bus {
+	u16 McaBaseIOPort;
+	u8 reserved;
+	u8 McaSlotNumber;
+	u32 McaBaseMemoryAddress;
+} i2o_mca_bus, *pi2o_mca_bus;
+
+typedef struct _i2o_other_bus {
+	u16 BaseIOPort;
+	u16 reserved;
+	u32 BaseMemoryAddress;
+} i2o_other_bus, *pi2o_other_bus;
+
+
+typedef struct _i2o_hrt_entry {
+	u32 adapter_id;
+	u32 parent_tid:12;
+	u32 state:4;
+	u32 bus_num:8;
+	u32 bus_type:8;
+	union {
+		i2o_pci_bus pci_bus;
+		i2o_local_bus local_bus;
+		i2o_isa_bus isa_bus;
+		i2o_eisa_bus eisa_bus;
+		i2o_mca_bus mca_bus;
+		i2o_other_bus other_bus;
+	} bus;
+} i2o_hrt_entry, *pi2o_hrt_entry;
+
+typedef struct _i2o_hrt {
+	u16 num_entries;
+	u8 entry_len;
+	u8 hrt_version;
+	u32 change_ind;
+	i2o_hrt_entry hrt_entry[1];
+} i2o_hrt, *pi2o_hrt;
+
+typedef struct _i2o_lct_entry {
+	u32 entry_size:16;
+	u32 tid:12;
+	u32 reserved:4;
+	u32 change_ind;
+	u32 device_flags;
+	u32 class_id;
+	u32 sub_class;
+	u32 user_tid:12;
+	u32 parent_tid:12;
+	u32 bios_info:8;
+	u8 identity_tag[8];
+	u32 event_capabilities;
+} i2o_lct_entry, *pi2o_lct_entry;
+
+typedef struct _i2o_lct {
+	u32 table_size:16;
+	u32 boot_tid:12;
+	u32 lct_ver:4;
+	u32 iop_flags;
+	u32 current_change_ind;
+	i2o_lct_entry lct_entry[1];
+} i2o_lct, *pi2o_lct;
+
+#endif				/* i2oproc_h */
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/i2o_scsi.c linux/drivers/i2o/i2o_scsi.c
--- v2.3.5/linux/drivers/i2o/i2o_scsi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/i2o_scsi.c	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,871 @@
+/* 
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the
+ *  Free Software Foundation; either version 2, or (at your option) any
+ *  later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  Complications for I2O scsi
+ *
+ *	o	Each (bus,lun) is a logical device in I2O. We keep a map
+ *		table. We spoof failed selection for unmapped units
+ *	o	Request sense buffers can come back for free. 
+ *	o	Scatter gather is a bit dynamic. We have to investigate at
+ *		setup time.
+ *	o	Some of our resources are dynamically shared. The i2o core
+ *		needs a message reservation protocol to avoid swap v net
+ *		deadlocking. We need to back off queue requests.
+ *	
+ *	In general the firmware wants to help. Where its help isn't performance
+ *	useful we just ignore the aid. Its not worth the code in truth.
+ *
+ *	Fixes:
+ *		Steve Ralston	:	Scatter gather now works
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include <linux/blk.h>
+#include <linux/version.h>
+#include <linux/i2o.h>
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+#include "i2o_scsi.h"
+
+#define VERSION_STRING        "Version 0.0.1"
+
+#define dprintk(x)
+
+#define MAXHOSTS 32
+
+struct proc_dir_entry proc_scsi_i2o_scsi = {
+	PROC_SCSI_I2O, 8, "i2o_scsi", S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+struct i2o_scsi_host
+{
+	struct i2o_controller *controller;
+	s16 task[16][8];		/* Allow 16 devices for now */
+	unsigned long tagclock[16][8];	/* Tag clock for queueing */
+	s16 bus_task;		/* The adapter TID */
+};
+
+static int scsi_context;
+static int lun_done;
+static int i2o_scsi_hosts;
+
+static u32 *retry[32];
+static struct i2o_controller *retry_ctrl[32];
+static struct timer_list retry_timer;
+static int retry_ct = 0;
+
+static atomic_t queue_depth;
+
+/*
+ *	SG Chain buffer support...
+ */
+#define SG_MAX_FRAGS		64
+
+/*
+ *	FIXME: we should allocate one of these per bus we find as we
+ *	locate them not in a lump at boot.
+ */
+ 
+typedef struct _chain_buf
+{
+	u32 sg_flags_cnt[SG_MAX_FRAGS];
+	u32 sg_buf[SG_MAX_FRAGS];
+} chain_buf;
+
+#define SG_CHAIN_BUF_SZ sizeof(chain_buf)
+
+#define SG_MAX_BUFS		(i2o_num_controllers * I2O_SCSI_CAN_QUEUE)
+#define SG_CHAIN_POOL_SZ	(SG_MAX_BUFS * SG_CHAIN_BUF_SZ)
+
+static int max_sg_len = 0;
+static chain_buf *sg_chain_pool = NULL;
+static int sg_chain_tag = 0;
+static int sg_max_frags = SG_MAX_FRAGS;
+
+/*
+ *	Retry congested frames. This actually needs pushing down into
+ *	i2o core. We should only bother the OSM with this when we can't
+ *	queue and retry the frame. Or perhaps we should call the OSM
+ *	and its default handler should be this in the core, and this
+ *	call a 2nd "I give up" handler in the OSM ?
+ */
+ 
+static void i2o_retry_run(unsigned long f)
+{
+	int i;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	for(i=0;i<retry_ct;i++)
+		i2o_post_message(retry_ctrl[i], virt_to_bus(retry[i]));
+	retry_ct=0;
+	
+	restore_flags(flags);
+}
+
+static void flush_pending(void)
+{
+	int i;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	for(i=0;i<retry_ct;i++)
+	{
+		retry[i][0]&=~0xFFFFFF;
+		retry[i][0]|=I2O_CMD_UTIL_NOP<<24;
+		i2o_post_message(retry_ctrl[i],virt_to_bus(retry[i]));
+	}
+	retry_ct=0;
+	
+	restore_flags(flags);
+}
+
+static void i2o_scsi_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *msg)
+{
+	Scsi_Cmnd *current_command;
+	u32 *m = (u32 *)msg;
+	u8 as,ds,st;
+	
+	if(m[0] & (1<<13))
+	{
+		printk("IOP fail.\n");
+		printk("From %d To %d Cmd %d.\n",
+			(m[1]>>12)&0xFFF,
+			m[1]&0xFFF,
+			m[1]>>24);
+		printk("Failure Code %d.\n", m[4]>>24);
+		if(m[4]&(1<<16))
+			printk("Format error.\n");
+		if(m[4]&(1<<17))
+			printk("Path error.\n");
+		if(m[4]&(1<<18))
+			printk("Path State.\n");
+		if(m[4]&(1<<18))
+			printk("Congestion.\n");
+		
+		m=(u32 *)bus_to_virt(m[7]);
+		printk("Failing message is %p.\n", m);
+		
+		if((m[4]&(1<<18)) && retry_ct < 32)
+		{
+			retry_ctrl[retry_ct]=c;
+			retry[retry_ct]=m;
+			if(!retry_ct++)
+			{
+				retry_timer.expires=jiffies+1;
+				add_timer(&retry_timer);
+			}
+		}
+		else
+		{
+			/* Create a scsi error for this */
+			current_command = (Scsi_Cmnd *)m[3];
+			printk("Aborted %ld\n", current_command->serial_number);
+
+			spin_lock_irq(&io_request_lock);			
+			current_command->result = DID_ERROR << 16;
+			current_command->scsi_done(current_command);
+			spin_unlock_irq(&io_request_lock);			
+			
+			/* Now flush the message by making it a NOP */
+			m[0]&=0x00FFFFFF;
+			m[0]|=(I2O_CMD_UTIL_NOP)<<24;
+			i2o_post_message(c,virt_to_bus(m));
+		}
+		return;
+	}
+			
+	
+	/* Low byte is the adapter status, next is the device */
+	as=(u8)m[4]; 
+	ds=(u8)(m[4]>>8);
+	st=(u8)(m[4]>>24);
+	
+	dprintk(("i2o got a scsi reply %08X: ", m[0]));
+	dprintk(("m[2]=%08X: ", m[2]));
+	dprintk(("m[4]=%08X\n", m[4]));
+
+	if(m[2]&0x80000000)
+	{
+		if(m[2]&0x40000000)
+		{
+			dprintk(("Event.\n"));
+			lun_done=1;
+			return;
+		}
+		printk(KERN_ERR "i2o_scsi: bus reset reply.\n");
+		return;
+	}
+	
+	current_command = (Scsi_Cmnd *)m[3];
+	
+	/*
+	 *	Is this a control request coming back - eg an abort ?
+	 */
+	 
+	if(current_command==NULL)
+	{
+		if(st)
+			dprintk(("SCSI abort: %08X", m[4]));
+		dprintk(("SCSI abort completed.\n"));
+		return;
+	}
+	
+	dprintk(("Completed %ld\n", current_command->serial_number));
+	
+	atomic_dec(&queue_depth);
+	
+	if(st == 0x06)
+	{
+		if(m[5] < current_command->underflow)
+		{
+			int i;
+			printk(KERN_ERR "SCSI: underflow 0x%08X 0x%08X\n",
+				m[5], current_command->underflow);
+			printk("Cmd: ");
+			for(i=0;i<15;i++)
+				printk("%02X ", current_command->cmnd[i]);
+			printk(".\n");
+		}
+		else st=0;
+	}
+	
+	if(st)
+	{
+		/* An error has occured */
+
+		dprintk((KERN_DEBUG "SCSI error %08X", m[4]));
+			
+		if (ds == 0x0E) 
+			/* SCSI Reset */
+			current_command->result = DID_RESET << 16;
+		else if (ds == 0x0F)
+			current_command->result = DID_PARITY << 16;
+		else
+			current_command->result = DID_ERROR << 16;
+	}
+	else
+		/*
+		 *	It worked maybe ?
+		 */		
+		current_command->result = DID_OK << 16 | ds;
+	spin_lock(&io_request_lock);
+	current_command->scsi_done(current_command);
+	spin_unlock(&io_request_lock);
+	return;
+}
+
+struct i2o_handler i2o_scsi_handler=
+{
+	i2o_scsi_reply,
+	"I2O SCSI OSM",
+	0
+};
+
+static int i2o_find_lun(struct i2o_controller *c, struct i2o_device *d, int *target, int *lun)
+{
+	u8 reply[8];
+	
+	if(i2o_query_scalar(c, d->id, scsi_context|0x40000000, 
+		0, 3, reply, 4, &lun_done)<0)
+		return -1;
+		
+	*target=reply[0];
+	
+	if(i2o_query_scalar(c, d->id, scsi_context|0x40000000, 
+		0, 4, reply, 8, &lun_done)<0)
+		return -1;
+
+	*lun=reply[1];
+
+	dprintk(("SCSI (%d,%d)\n", *target, *lun));
+	return 0;
+}
+
+static void i2o_scsi_init(struct i2o_controller *c, struct i2o_device *d, struct Scsi_Host *shpnt)
+{
+	struct i2o_device *unit;
+	struct i2o_scsi_host *h =(struct i2o_scsi_host *)shpnt->hostdata;
+	int lun;
+	int target;
+	
+	h->controller=c;
+	h->bus_task=d->id;
+	
+	for(target=0;target<16;target++)
+		for(lun=0;lun<8;lun++)
+			h->task[target][lun] = -1;
+			
+	for(unit=c->devices;unit!=NULL;unit=unit->next)
+	{
+		dprintk(("Class %03X, parent %d, want %d.\n",
+			unit->class, unit->parent, d->id));
+			
+		/* Only look at scsi and fc devices */
+		if (    (unit->class != I2O_CLASS_SCSI_PERIPHERAL)
+		     && (unit->class != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL)
+		   )
+			continue;
+
+		/* On our bus ? */
+		dprintk(("Found a disk.\n"));
+		if (    (unit->parent == d->id)
+		     || (unit->parent == d->parent)
+		   )
+		{
+			u16 limit;
+			dprintk(("Its ours.\n"));
+			if(i2o_find_lun(c, unit, &target, &lun)==-1)
+			{
+				printk(KERN_ERR "i2o_scsi: Unable to get lun for tid %d.\n", d->id);
+				continue;
+			}
+			dprintk(("Found disk %d %d.\n", target, lun));
+			h->task[target][lun]=unit->id;
+			h->tagclock[target][lun]=jiffies;
+
+			/* Get the max fragments/request */
+			i2o_query_scalar(c, d->id, scsi_context|0x40000000,
+					0xF103, 3, &limit, 2, &lun_done);
+			
+			/* sanity */
+			if ( limit == 0 )
+			{
+				printk(KERN_WARNING "i2o_scsi: Ignoring unreasonable SG limit of 0 from IOP!\n");
+				limit = 1;
+			}
+			
+			shpnt->sg_tablesize = limit;
+
+			dprintk(("i2o_scsi: set scatter-gather to %d.\n", 
+				shpnt->sg_tablesize));
+		}
+	}		
+}
+
+int i2o_scsi_detect(Scsi_Host_Template * tpnt)
+{
+	unsigned long flags;
+	struct Scsi_Host *shpnt = NULL;
+	int i;
+	int count;
+
+	printk("i2o_scsi.c: %s\n", VERSION_STRING);
+
+	if(i2o_install_handler(&i2o_scsi_handler)<0)
+	{
+		printk(KERN_ERR "i2o_scsi: Unable to install OSM handler.\n");
+		return 0;
+	}
+	scsi_context = i2o_scsi_handler.context;
+	
+	if((sg_chain_pool = kmalloc(SG_CHAIN_POOL_SZ, GFP_KERNEL)) == NULL)
+	{
+		printk("i2o_scsi: Unable to alloc %d byte SG chain buffer pool.\n", SG_CHAIN_POOL_SZ);
+		printk("i2o_scsi: SG chaining DISABLED!\n");
+		sg_max_frags = 11;
+	}
+	else
+	{
+		printk("  chain_pool: %d bytes @ %p\n", SG_CHAIN_POOL_SZ, sg_chain_pool);
+		printk("  (%d byte buffers X %d can_queue X %d i2o controllers)\n",
+				SG_CHAIN_BUF_SZ, I2O_SCSI_CAN_QUEUE, i2o_num_controllers);
+		sg_max_frags = SG_MAX_FRAGS;    // 64
+	}
+	
+	init_timer(&retry_timer);
+	retry_timer.data = 0UL;
+	retry_timer.function = i2o_retry_run;
+	
+//	printk("SCSI OSM at %d.\n", scsi_context);
+
+	for (count = 0, i = 0; i < MAX_I2O_CONTROLLERS; i++)
+	{
+		struct i2o_controller *c=i2o_find_controller(i);
+		struct i2o_device *d;
+		/*
+		 *	This controller doesn't exist.
+		 */
+		
+		if(c==NULL)
+			continue;
+			
+		/*
+		 *	Fixme - we need some altered device locking. This
+		 *	is racing with device addition in theory. Easy to fix.
+		 */
+		
+		for(d=c->devices;d!=NULL;d=d->next)
+		{
+			/*
+			 *	bus_adapter, SCSI (obsolete), or FibreChannel busses only
+			 */
+			if(    (d->class!=I2O_CLASS_BUS_ADAPTER_PORT)	// bus_adapter
+			    && (d->class!=I2O_CLASS_FIBRE_CHANNEL_PORT)	// FC_PORT
+			  )
+				continue;
+		
+//			printk("Found a controller.\n");	
+			shpnt = scsi_register(tpnt, sizeof(struct i2o_scsi_host));
+			save_flags(flags);
+			cli();
+			shpnt->unique_id = (u32)d;
+			shpnt->io_port = 0;
+			shpnt->n_io_port = 0;
+			shpnt->irq = 0;
+			shpnt->this_id = /* Good question */15;
+			restore_flags(flags);
+//			printk("Scanning I2O port %d.\n", d->id);
+			i2o_scsi_init(c, d, shpnt);
+			count++;
+		}
+	}
+	i2o_scsi_hosts = count;
+	
+	if(count==0)
+	{
+		if(sg_chain_pool!=NULL)
+		{
+			kfree(sg_chain_pool);
+			sg_chain_pool = NULL;
+		}
+		flush_pending();
+		del_timer(&retry_timer);
+		i2o_remove_handler(&i2o_scsi_handler);
+	}
+	
+	return count;
+}
+
+int i2o_scsi_release(struct Scsi_Host *host)
+{
+	if(--i2o_scsi_hosts==0)
+	{
+		if(sg_chain_pool!=NULL)
+		{
+			kfree(sg_chain_pool);
+			sg_chain_pool = NULL;
+		}
+		flush_pending();
+		del_timer(&retry_timer);
+		i2o_remove_handler(&i2o_scsi_handler);
+	}
+	return 0;
+}
+
+
+const char *i2o_scsi_info(struct Scsi_Host *SChost)
+{
+	struct i2o_scsi_host *hostdata;
+
+	hostdata = (struct i2o_scsi_host *)SChost->hostdata;
+
+	return(&hostdata->controller->name[0]);
+}
+
+
+/*
+ * From the wd93 driver:
+ * Returns true if there will be a DATA_OUT phase with this command, 
+ * false otherwise.
+ * (Thanks to Joerg Dorchain for the research and suggestion.)
+ *
+ */
+static int is_dir_out(Scsi_Cmnd *cmd)
+{
+	switch (cmd->cmnd[0]) 
+	{
+     		case WRITE_6:           case WRITE_10:          case WRITE_12:
+      		case WRITE_LONG:        case WRITE_SAME:        case WRITE_BUFFER:
+      		case WRITE_VERIFY:      case WRITE_VERIFY_12:      
+      		case COMPARE:           case COPY:              case COPY_VERIFY:
+      		case SEARCH_EQUAL:      case SEARCH_HIGH:       case SEARCH_LOW:
+      		case SEARCH_EQUAL_12:   case SEARCH_HIGH_12:    case SEARCH_LOW_12:      
+      		case FORMAT_UNIT:       case REASSIGN_BLOCKS:   case RESERVE:
+      		case MODE_SELECT:       case MODE_SELECT_10:    case LOG_SELECT:
+      		case SEND_DIAGNOSTIC:   case CHANGE_DEFINITION: case UPDATE_BLOCK:
+      		case SET_WINDOW:        case MEDIUM_SCAN:       case SEND_VOLUME_TAG:
+      		case 0xea:
+        		return 1;
+		default:
+        		return 0;
+	}
+}
+
+int i2o_scsi_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
+{
+	int i;
+	int tid;
+	struct i2o_controller *c;
+	Scsi_Cmnd *current_command;
+	struct Scsi_Host *host;
+	struct i2o_scsi_host *hostdata;
+	u32 *msg, *mptr;
+	u32 m;
+	u32 *lenptr;
+	int direction;
+	int scsidir;
+	u32 len;
+	
+	static int max_qd = 1;
+	
+	/*
+	 *	The scsi layer should be handling this stuff
+	 */
+
+	if(is_dir_out(SCpnt))
+	{
+		direction=0x04000000;
+		scsidir=0x80000000;
+	}
+	else
+	{
+		scsidir=0x40000000;
+		direction=0x00000000;
+	}
+	
+	/*
+	 *	Do the incoming paperwork
+	 */
+	 
+	host = SCpnt->host;
+	hostdata = (struct i2o_scsi_host *)host->hostdata;
+	SCpnt->scsi_done = done;
+	
+	if(SCpnt->target > 15)
+	{
+		printk(KERN_ERR "i2o_scsi: Wild target %d.\n", SCpnt->target);
+		return -1;
+	}
+	
+	tid = hostdata->task[SCpnt->target][SCpnt->lun];
+	
+	dprintk(("qcmd: Tid = %d\n", tid));
+	
+	current_command = SCpnt;		/* set current command                */
+	current_command->scsi_done = done;	/* set ptr to done function           */
+
+	/* We don't have such a device. Pretend we did the command 
+	   and that selection timed out */
+	
+	if(tid == -1)
+	{
+		SCpnt->result = DID_NO_CONNECT << 16;
+		done(SCpnt);
+		return 0;
+	}
+	
+	dprintk(("Real scsi messages.\n"));
+
+	c = hostdata->controller;
+	
+	/*
+	 *	Obtain an I2O message. Right now we _have_ to obtain one
+	 *	until the scsi layer stuff is cleaned up.
+	 */	
+	 
+	do
+	{
+		mb();
+		m = I2O_POST_READ32(c);
+	}
+	while(m==0xFFFFFFFF);
+	msg = bus_to_virt(c->mem_offset + m);
+	
+	/*
+	 *	Put together a scsi execscb message
+	 */
+	
+	msg[1] = I2O_CMD_SCSI_EXEC<<24|HOST_TID<<12|tid;
+	msg[2] = scsi_context;		/* So the I2O layer passes to us */
+	/* Sorry 64bit folks. FIXME */
+	msg[3] = (u32)SCpnt;		/* We want the SCSI control block back */
+	/* Direction, disconnect ok, no tagging (yet) */
+	msg[4] = scsidir|(1<<29)|SCpnt->cmd_len;
+
+	/*
+	 *	Attach tags to the devices
+	 */	
+	if(SCpnt->device->tagged_supported)
+	{
+		/*
+		 *	Some drives are too stupid to handle fairness issues
+		 *	with tagged queueing. We throw in the odd ordered
+		 *	tag to stop them starving themselves.
+		 */
+		if((jiffies - hostdata->tagclock[SCpnt->target][SCpnt->lun]) > (5*HZ))
+		{
+			msg[4]|=(1<<23)|(1<<24);
+			hostdata->tagclock[SCpnt->target][SCpnt->lun]=jiffies;
+		}
+		else switch(SCpnt->tag)
+		{
+			case SIMPLE_QUEUE_TAG:
+				msg[4]|=(1<<23);
+				break;
+			case HEAD_OF_QUEUE_TAG:
+				msg[4]|=(1<<24);
+				break;
+			case ORDERED_QUEUE_TAG:
+				msg[4]|=(1<<23)|(1<<24);
+				break;
+			default:
+				msg[4]|=(1<<23);
+		}
+	}
+
+	mptr=msg+5;
+
+	/* 
+	 *	Write SCSI command into the message - always 16 byte block 
+	 */
+	 
+	memcpy(mptr, SCpnt->cmnd, 16);
+	mptr+=4;
+	lenptr=mptr++;		/* Remember me - fill in when we know */
+	
+			
+	/*
+	 *	Now fill in the SGList and command 
+	 *
+	 *	FIXME: we need to set the sglist limits according to the 
+	 *	message size of the I2O controller. We might only have room
+	 *	for 6 or so worst case
+	 */
+	
+	if(SCpnt->use_sg)
+	{
+		struct scatterlist *sg = (struct scatterlist *)SCpnt->request_buffer;
+		
+		if((sg_max_frags > 11) && (SCpnt->use_sg > 11))
+		{
+			/*
+			 *	Need to chain!
+			 */
+			SCpnt->host_scribble = (void*)(sg_chain_pool + sg_chain_tag);
+			*mptr++=direction|0xB0000000|(SCpnt->use_sg*2*4);
+			*mptr=virt_to_bus(SCpnt->host_scribble);
+			mptr = (u32*)SCpnt->host_scribble;
+			if (SCpnt->use_sg > max_sg_len)
+			{
+				max_sg_len = SCpnt->use_sg;
+				printk("i2o_scsi: Chain SG! SCpnt=%p, SG_FragCnt=%d, SG_idx=%d\n",
+					SCpnt, SCpnt->use_sg, (chain_buf*)SCpnt->host_scribble-sg_chain_pool);
+			}
+			if ( ++sg_chain_tag == SG_MAX_BUFS )
+				sg_chain_tag = 0;
+		}
+		
+		len = 0;
+		
+		for(i = 0 ; i < SCpnt->use_sg; i++)
+		{
+			*mptr++=direction|0x10000000|sg->length;
+			len+=sg->length;
+			*mptr++=virt_to_bus(sg->address);
+			sg++;
+		}
+		mptr[-2]|=0xC0000000;	/* End of List and block */
+		*lenptr=len;
+		if(len != SCpnt->underflow)
+			printk("Cmd len %08X Cmd underflow %08X\n",
+				len, SCpnt->underflow);
+	}
+	else
+	{
+		dprintk(("non sg for %p, %d\n", SCpnt->request_buffer,
+				SCpnt->request_bufflen));
+		*mptr++=0xD0000000|direction|SCpnt->request_bufflen;
+		*mptr++=virt_to_bus(SCpnt->request_buffer);
+		*lenptr = len = SCpnt->request_bufflen;
+		/* No transfer ? - fix up the request */
+		if(len == 0)
+			msg[4]&=~0xC0000000;
+	}
+	
+	/*
+	 *	Stick the headers on 
+	 */
+
+	msg[0] = (mptr-msg)<<16 | SGL_OFFSET_10;
+	
+	/* Queue the message */
+	i2o_post_message(c,m);
+	
+	atomic_inc(&queue_depth);
+	
+	if(atomic_read(&queue_depth)> max_qd)
+	{
+		max_qd=atomic_read(&queue_depth);
+		printk("Queue depth now %d.\n", max_qd);
+	}
+	
+	mb();
+	dprintk(("Issued %ld\n", current_command->serial_number));
+	
+	return 0;
+}
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+{
+	SCpnt->SCp.Status++;
+}
+
+int i2o_scsi_command(Scsi_Cmnd * SCpnt)
+{
+	i2o_scsi_queuecommand(SCpnt, internal_done);
+	SCpnt->SCp.Status = 0;
+	while (!SCpnt->SCp.Status)
+		barrier();
+	return SCpnt->result;
+}
+
+int i2o_scsi_abort(Scsi_Cmnd * SCpnt)
+{
+	struct i2o_controller *c;
+	struct Scsi_Host *host;
+	struct i2o_scsi_host *hostdata;
+	u32 *msg;
+	u32 m;
+	int tid;
+	
+	printk("i2o_scsi_abort\n");
+	
+	host = SCpnt->host;
+	hostdata = (struct i2o_scsi_host *)host->hostdata;
+	tid = hostdata->task[SCpnt->target][SCpnt->lun];
+	if(tid==-1)
+	{
+		printk(KERN_ERR "impossible command to abort.\n");
+		return SCSI_ABORT_NOT_RUNNING;
+	}
+	c = hostdata->controller;
+	
+	/*
+	 *	Obtain an I2O message. Right now we _have_ to obtain one
+	 *	until the scsi layer stuff is cleaned up.
+	 */	
+	 
+	do
+	{
+		mb();
+		m = I2O_POST_READ32(c);
+	}
+	while(m==0xFFFFFFFF);
+	msg = bus_to_virt(c->mem_offset + m);
+	
+	msg[0] = FIVE_WORD_MSG_SIZE;
+	msg[1] = I2O_CMD_SCSI_ABORT<<24|HOST_TID<<12|tid;
+	msg[2] = scsi_context;
+	msg[3] = 0;	/* Not needed for an abort */
+	msg[4] = (u32)SCpnt;	
+	wmb();
+	i2o_post_message(c,m);
+	wmb();
+//	SCpnt->result = DID_RESET << 16;
+//	SCpnt->scsi_done(SCpnt);
+	return SCSI_ABORT_PENDING;
+}
+
+int i2o_scsi_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+{
+	int tid;
+	struct i2o_controller *c;
+	struct Scsi_Host *host;
+	struct i2o_scsi_host *hostdata;
+	u32 m;
+	u32 *msg;
+
+	printk("i2o_scsi_reset\n");
+	
+	/*
+	 *	Find the TID for the bus
+	 */
+
+	host = SCpnt->host;
+	hostdata = (struct i2o_scsi_host *)host->hostdata;
+	tid = hostdata->bus_task;
+	c = hostdata->controller;
+
+	/*
+	 *	Now send a SCSI reset request. Any remaining commands
+	 *	will be aborted by the IOP. We need to catch the reply
+	 *	possibly ?
+	 */
+	 
+	m = I2O_POST_READ32(c);
+	
+	/*
+	 *	No free messages, try again next time - no big deal
+	 */
+	 
+	if(m == 0xFFFFFFFF)
+		return SCSI_RESET_PUNT;
+	
+	msg = bus_to_virt(c->mem_offset + m);
+	msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+	msg[1] = I2O_CMD_SCSI_BUSRESET<<24|HOST_TID<<12|tid;
+	msg[2] = scsi_context|0x80000000;	
+	/* We use the top bit to split controller and unit transactions */
+	/* Now store unit,tid so we can tie the completion back to a specific device */
+	msg[3] = c->unit << 16 | tid;
+	i2o_post_message(c,m);
+	return SCSI_RESET_PENDING;
+}
+
+/*
+ *	This is anyones guess quite frankly.
+ */
+ 
+int i2o_scsi_bios_param(Disk * disk, kdev_t dev, int *ip)
+{
+	int size;
+
+	size = disk->capacity;
+	ip[0] = 64;		/* heads                        */
+	ip[1] = 32;		/* sectors                      */
+	if ((ip[2] = size >> 11) > 1024) {	/* cylinders, test for big disk */
+		ip[0] = 255;	/* heads                        */
+		ip[1] = 63;	/* sectors                      */
+		ip[2] = size / (255 * 63);	/* cylinders                    */
+	}
+	return 0;
+}
+
+/* Loadable module support */
+#ifdef MODULE
+
+MODULE_AUTHOR("Red Hat Software");
+
+Scsi_Host_Template driver_template = I2OSCSI;
+
+#include "../scsi/scsi_module.c"
+#endif
diff -u --recursive --new-file v2.3.5/linux/drivers/i2o/i2o_scsi.h linux/drivers/i2o/i2o_scsi.h
--- v2.3.5/linux/drivers/i2o/i2o_scsi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/i2o/i2o_scsi.h	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,48 @@
+#ifndef _I2O_SCSI_H
+#define _I2O_SCSI_H
+
+#if !defined(LINUX_VERSION_CODE)
+#include <linux/version.h>
+#endif
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#define I2O_SCSI_ID 15
+#define I2O_SCSI_CAN_QUEUE 8
+#define I2O_SCSI_CMD_PER_LUN 6
+
+extern struct proc_dir_entry proc_scsi_i2o_scsi;
+
+extern int i2o_scsi_detect(Scsi_Host_Template *);
+extern const char *i2o_scsi_info(struct Scsi_Host *);
+extern int i2o_scsi_command(Scsi_Cmnd *);
+extern int i2o_scsi_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int i2o_scsi_abort(Scsi_Cmnd *);
+extern int i2o_scsi_reset(Scsi_Cmnd *, unsigned int);
+extern int i2o_scsi_bios_param(Disk *, kdev_t, int *);
+extern void i2o_scsi_setup(char *str, int *ints);
+
+#define I2OSCSI {                                          \
+		  next: NULL,				    \
+                  proc_dir:          &proc_scsi_i2o_scsi,   \
+                  name:              "I2O SCSI Layer", 	    \
+                  detect:            i2o_scsi_detect,       \
+                  release:	     i2o_scsi_release,	    \
+                  info:              i2o_scsi_info,         \
+                  command:           i2o_scsi_command,      \
+                  queuecommand:      i2o_scsi_queuecommand, \
+                  abort:             i2o_scsi_abort,        \
+                  reset:             i2o_scsi_reset,        \
+                  bios_param:        i2o_scsi_bios_param,   \
+                  can_queue:         I2O_SCSI_CAN_QUEUE,    \
+                  this_id:           I2O_SCSI_ID,           \
+                  sg_tablesize:      8,                     \
+                  cmd_per_lun:       I2O_SCSI_CMD_PER_LUN,  \
+                  unchecked_isa_dma: 0,                     \
+                  use_clustering:    ENABLE_CLUSTERING     \
+                  }
+
+#endif
diff -u --recursive --new-file v2.3.5/linux/drivers/macintosh/Makefile linux/drivers/macintosh/Makefile
--- v2.3.5/linux/drivers/macintosh/Makefile	Thu Apr 29 12:53:48 1999
+++ linux/drivers/macintosh/Makefile	Mon Jun  7 12:12:32 1999
@@ -17,6 +17,8 @@
 
 ifndef CONFIG_MBX
 L_OBJS   := via-cuda.o macio-adb.o via-pmu.o mediabay.o
+endif
+ifeq ($(CONFIG_MAC_KEYBOARD),y)
 LX_OBJS  := adb.o
 endif
 
diff -u --recursive --new-file v2.3.5/linux/drivers/macintosh/adb.c linux/drivers/macintosh/adb.c
--- v2.3.5/linux/drivers/macintosh/adb.c	Mon May 17 09:55:22 1999
+++ linux/drivers/macintosh/adb.c	Mon Jun  7 12:12:32 1999
@@ -24,6 +24,7 @@
 #include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/sched.h>
+#include <linux/wait.h>
 #include <asm/prom.h>
 #include <asm/adb.h>
 #include <asm/cuda.h>
@@ -375,7 +376,7 @@
 	spinlock_t	lock;
 	atomic_t	n_pending;
 	struct adb_request *completed;
-	wait_queue_head_t wait_queue;
+  	wait_queue_head_t wait_queue;
 	int		inuse;
 };
 
@@ -421,7 +422,7 @@
 	spin_lock_init(&state->lock);
 	atomic_set(&state->n_pending, 0);
 	state->completed = NULL;
-	state->wait_queue = NULL;
+	init_waitqueue_head(&state->wait_queue);
 	state->inuse = 1;
 
 	return 0;
@@ -458,7 +459,7 @@
 	int ret;
 	struct adbdev_state *state = file->private_data;
 	struct adb_request *req;
-	DECLARE_WAITQUEUE(wait, current);
+	wait_queue_t wait = __WAITQUEUE_INITIALIZER(wait,current);
 	unsigned long flags;
 
 	if (count < 2)
diff -u --recursive --new-file v2.3.5/linux/drivers/macintosh/mac_keyb.c linux/drivers/macintosh/mac_keyb.c
--- v2.3.5/linux/drivers/macintosh/mac_keyb.c	Mon May 17 09:55:22 1999
+++ linux/drivers/macintosh/mac_keyb.c	Mon Jun  7 12:12:32 1999
@@ -251,7 +251,6 @@
 extern int console_loglevel;
 
 extern struct kbd_struct kbd_table[];
-extern struct wait_queue_head_t keypress_wait;
 
 extern void handle_scancode(unsigned char, int);
 
diff -u --recursive --new-file v2.3.5/linux/drivers/macintosh/macserial.c linux/drivers/macintosh/macserial.c
--- v2.3.5/linux/drivers/macintosh/macserial.c	Mon May 17 09:55:22 1999
+++ linux/drivers/macintosh/macserial.c	Mon Jun  7 12:12:32 1999
@@ -137,11 +137,11 @@
  * memory if large numbers of serial ports are open.
  */
 static unsigned char tmp_buf[4096]; /* This is cheating */
-static struct semaphore tmp_buf_sem = MUTEX;
+DECLARE_MUTEX(tmp_buf_sem);
 
-__openfirmware
 
-static inline int serial_paranoia_check(struct mac_serial *info,
+static inline int __pmac
+serial_paranoia_check(struct mac_serial *info,
 					dev_t device, const char *routine)
 {
 #ifdef SERIAL_PARANOIA_CHECK
@@ -165,7 +165,7 @@
 /* 
  * Reading and writing Z8530 registers.
  */
-static inline unsigned char read_zsreg(struct mac_zschannel *channel,
+static inline unsigned char __pmac read_zsreg(struct mac_zschannel *channel,
 				       unsigned char reg)
 {
 	unsigned char retval;
@@ -185,7 +185,7 @@
 	return retval;
 }
 
-static inline void write_zsreg(struct mac_zschannel *channel,
+static inline void __pmac write_zsreg(struct mac_zschannel *channel,
 			       unsigned char reg, unsigned char value)
 {
 	unsigned long flags;
@@ -201,7 +201,7 @@
 	return;
 }
 
-static inline unsigned char read_zsdata(struct mac_zschannel *channel)
+static inline unsigned char __pmac read_zsdata(struct mac_zschannel *channel)
 {
 	unsigned char retval;
 
@@ -1997,8 +1997,8 @@
 		info->tqueue.data = info;
 		info->callout_termios =callout_driver.init_termios;
 		info->normal_termios = serial_driver.init_termios;
-		info->open_wait = 0;
-		info->close_wait = 0;
+		init_waitqueue_head(&info->open_wait);
+		init_waitqueue_head(&info->close_wait);
 		info->timeout = HZ;
 		printk("tty%02d at 0x%08x (irq = %d)", info->line, 
 			info->port, info->irq);
diff -u --recursive --new-file v2.3.5/linux/drivers/misc/BUGS-parport linux/drivers/misc/BUGS-parport
--- v2.3.5/linux/drivers/misc/BUGS-parport	Fri Jan 23 20:04:03 1998
+++ linux/drivers/misc/BUGS-parport	Thu Jun  3 16:21:47 1999
@@ -2,3 +2,4 @@
 
 o lp doesn't allow you to read status while printing is in progress.
 
+See <URL:http://www.cyberelk.demon.co.uk/parport.html>.
diff -u --recursive --new-file v2.3.5/linux/drivers/misc/Makefile linux/drivers/misc/Makefile
--- v2.3.5/linux/drivers/misc/Makefile	Tue May 11 09:55:49 1999
+++ linux/drivers/misc/Makefile	Thu Jun  3 16:21:47 1999
@@ -7,6 +7,10 @@
 #
 # Note 2! The CFLAGS definitions are now inherited from the
 # parent makes..
+#
+# Note 3! Parport is the Borg.  We have assimilated some other
+# drivers in the `char', `net' and `scsi' directories, but left them
+# there to allay suspicion.
 
 SUB_DIRS     := 
 MOD_SUB_DIRS := $(SUB_DIRS)
diff -u --recursive --new-file v2.3.5/linux/drivers/misc/TODO-parport linux/drivers/misc/TODO-parport
--- v2.3.5/linux/drivers/misc/TODO-parport	Thu May  7 14:56:58 1998
+++ linux/drivers/misc/TODO-parport	Thu Jun  3 16:21:47 1999
@@ -16,3 +16,5 @@
 3. Support more hardware (eg m68k, Sun bpp).
 
 4. A better PLIP (make use of bidirectional/ECP/EPP ports).
+
+See <URL:http://www.cyberelk.demon.co.uk/parport.html>.
diff -u --recursive --new-file v2.3.5/linux/drivers/misc/parport_amiga.c linux/drivers/misc/parport_amiga.c
--- v2.3.5/linux/drivers/misc/parport_amiga.c	Tue May 11 09:55:49 1999
+++ linux/drivers/misc/parport_amiga.c	Mon Jun  7 12:10:10 1999
@@ -155,19 +155,6 @@
 }
 
 
-static void amiga_release_resources(struct parport *p)
-{
-DPRINTK("realease_resources\n");
-	if (p->irq != PARPORT_IRQ_NONE)
-		free_irq(IRQ_AMIGA_CIAA_FLG, p);
-}
-
-static int amiga_claim_resources(struct parport *p)
-{
-DPRINTK("claim_resources\n");
-	return request_irq(IRQ_AMIGA_CIAA_FLG, amiga_interrupt, 0, p->name, p);
-}
-
 static void amiga_init_state(struct parport_state *s)
 {
 	s->u.amiga.data = 0;
@@ -243,10 +230,6 @@
 	amiga_change_mode,
 
 
-	amiga_release_resources,
-	amiga_claim_resources,
-
-
 	NULL, /* epp_write_data */
 	NULL, /* epp_read_data */
 	NULL, /* epp_write_addr */
@@ -289,10 +272,17 @@
 		printk(KERN_INFO "%s: Amiga built-in port using irq\n", p->name);
 		/* XXX: set operating mode */
 		parport_proc_register(p);
-		p->flags |= PARPORT_FLAG_COMA;
+		if (request_irq(IRQ_AMIGA_CIAA_FLG, amiga_interrupt, 0,
+				p->name, p)) {
+			parport_unregister_port (p);
+			return 0;
+		}
 
 		if (parport_probe_hook)
 			(*parport_probe_hook)(p);
+
+		parport_announce_port (p);
+
 		return 1;
 
 	}
@@ -312,8 +302,8 @@
 
 void cleanup_module(void)
 {
-	if (!(this_port->flags & PARPORT_FLAG_COMA))
-		parport_quiesce(this_port);
+	if (p->irq != PARPORT_IRQ_NONE)
+		free_irq(IRQ_AMIGA_CIAA_FLG, p);
 	parport_proc_unregister(this_port);
 	parport_unregister_port(this_port);
 }
diff -u --recursive --new-file v2.3.5/linux/drivers/misc/parport_arc.c linux/drivers/misc/parport_arc.c
--- v2.3.5/linux/drivers/misc/parport_arc.c	Thu Jan  7 09:28:47 1999
+++ linux/drivers/misc/parport_arc.c	Thu Jun  3 16:21:47 1999
@@ -28,11 +28,27 @@
 #include <asm/arch/oldlatches.h>
 #include <asm/arch/irqs.h>
 
-#define DATA_LATCH    0x3350010
+#define DATA_ADDRESS    0x3350010
 
-/* ARC can't read from the data latch, so we must use a soft copy. */
+/* This is equivalent to the above and only used for request_region. */
+#define PORT_BASE       0x80000000 | ((DATA_ADDRESS - IO_BASE) >> 2)
+
+/* The hardware can't read from the data latch, so we must use a soft
+   copy. */
 static unsigned char data_copy;
 
+/* These are pretty simple. We know the irq is never shared and the
+   kernel does all the magic that's required. */
+static void arc_enable_irq(struct parport *p)
+{
+	enable_irq(p->irq);
+}
+
+static void arc_disable_irq(struct parport *p)
+{
+	disable_irq(p->irq);
+}
+
 static void arc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	parport_generic_irq(irq, (struct parport *) dev_id, regs);
@@ -41,7 +57,7 @@
 static void arc_write_data(struct parport *p, unsigned char data)
 {
 	data_copy = data;
-	outb(data, DATA_LATCH);
+	outb_t(data, DATA_LATCH);
 }
 
 static unsigned char arc_read_data(struct parport *p)
@@ -94,9 +110,6 @@
 	
 	NULL, /* change_mode */
 	
-	arc_release_resources,
-	arc_claim_resources,
-	
 	NULL, /* epp_write_data */
 	NULL, /* epp_read_data */
 	NULL, /* epp_write_addr */
@@ -129,23 +142,27 @@
 	/* Archimedes hardware provides only one port, at a fixed address */
 	struct parport *p;
 
-	if (check_region(DATA_LATCH, 4))
+	if (check_region(PORT_BASE, 4))
 		return 0;
 	
-       	if (!(p = parport_register_port(base, IRQ_PRINTERACK, 
-					PARPORT_DMA_NONE, &parport_arc_ops))) 
+	p = parport_register_port(base, IRQ_PRINTERACK, 
+				  PARPORT_DMA_NONE, &parport_arc_ops);
+
+	if (!p)
 		return 0;
 
 	p->modes = PARPORT_MODE_ARCSPP;
-	p->size = 4;
+	p->size = 1;
 
 	printk(KERN_INFO "%s: Archimedes on-board port, using irq %d\n",
 	       p->irq);
 	parport_proc_register(p);
-	p->flags |= PARPORT_FLAG_COMA;
 
 	if (parport_probe_hook)
 		(*parport_probe_hook)(p);
+
+	/* Tell the high-level drivers about the port. */
+	parport_announce_port (p);
 
 	return 1;
 }
diff -u --recursive --new-file v2.3.5/linux/drivers/misc/parport_atari.c linux/drivers/misc/parport_atari.c
--- v2.3.5/linux/drivers/misc/parport_atari.c	Tue May 11 09:55:49 1999
+++ linux/drivers/misc/parport_atari.c	Thu Jun  3 16:21:47 1999
@@ -118,20 +118,6 @@
 }
 
 static void
-parport_atari_release_resources(struct parport *p)
-{
-	if (p->irq != PARPORT_IRQ_NONE)
-		free_irq(IRQ_MFP_BUSY, p);
-}
-
-static int
-parport_atari_claim_resources(struct parport *p)
-{
-	return request_irq(IRQ_MFP_BUSY, parport_atari_interrupt,
-			   IRQ_TYPE_SLOW, p->name, p);
-}
-
-static void
 parport_atari_inc_use_count(void)
 {
 	MOD_INC_USE_COUNT;
@@ -174,9 +160,6 @@
 
 	NULL, /* change_mode */
 
-	parport_atari_release_resources,
-	parport_atari_claim_resources,
-
 	NULL, /* epp_write_data */
 	NULL, /* epp_read_data */
 	NULL, /* epp_write_addr */
@@ -228,13 +211,21 @@
 					  &parport_atari_ops);
 		if (!p)
 			return 0;
+		if (request_irq(IRQ_MFP_BUSY, parport_atari_interrupt,
+				IRQ_TYPE_SLOW, p->name, p)) {
+			parport_unregister_port (p);
+			return 0;
+		}
+
 		this_port = p;
 		printk(KERN_INFO "%s: Atari built-in port using irq\n", p->name);
 		parport_proc_register(p);
-		p->flags |= PARPORT_FLAG_COMA;
 
 		if (parport_probe_hook)
 			(*parport_probe_hook)(p);
+
+		parport_announce_port (p);
+
 		return 1;
 	}
 	return 0;
@@ -255,8 +246,8 @@
 void
 cleanup_module(void)
 {
-	if (!(this_port->flags & PARPORT_FLAG_COMA))
-		parport_quiesce(this_port);
+	if (p->irq != PARPORT_IRQ_NONE)
+		free_irq(IRQ_MFP_BUSY, p);
 	parport_proc_unregister(this_port);
 	parport_unregister_port(this_port);
 }
diff -u --recursive --new-file v2.3.5/linux/drivers/misc/parport_ax.c linux/drivers/misc/parport_ax.c
--- v2.3.5/linux/drivers/misc/parport_ax.c	Wed Jun  2 14:44:39 1999
+++ linux/drivers/misc/parport_ax.c	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-/* $Id: parport_ax.c,v 1.17 1999/01/20 06:18:54 davem Exp $
+/* $Id: parport_ax.c,v 1.19 1999/06/09 08:24:40 davem Exp $
  * Parallel-port routines for Sun Ultra/AX architecture
  * 
  * Author: Eddie C. Dost <ecd@skynet.be>
@@ -201,39 +201,9 @@
 	writel(dcsr, (unsigned long)&dma->dcsr);
 }
 
-void
-parport_ax_release_resources(struct parport *p)
-{
-	if (p->irq != PARPORT_IRQ_NONE) {
-		parport_ax_disable_irq(p);
-		free_irq(p->irq, p);
-	}
-	release_region(p->base, p->size);
-	if (p->modes & PARPORT_MODE_PCECR)
-		release_region(p->base+0x400, 3);
-	release_region((unsigned long)p->private_data,
-		       sizeof(struct linux_ebus_dma));
-}
-
 int
 parport_ax_claim_resources(struct parport *p)
 {
-	/* FIXME check that resources are free */
-	int err;
-
-	if (p->irq != PARPORT_IRQ_NONE) {
-		if ((err = request_irq(p->irq, parport_ax_interrupt,
-				       0, p->name, p)) != 0)
-			return err;
-		else
-			parport_ax_enable_irq(p);
-	}
-	request_region(p->base, p->size, p->name);
-	if (p->modes & PARPORT_MODE_PCECR)
-		request_region(p->base+0x400, 3, p->name);
-	request_region((unsigned long)p->private_data,
-		       sizeof(struct linux_ebus_dma), p->name);
-	return 0;
 }
 
 void
@@ -332,9 +302,6 @@
 	
 	parport_ax_change_mode,
 	
-	parport_ax_release_resources,
-	parport_ax_claim_resources,
-	
 	parport_ax_write_epp,
 	parport_ax_read_epp,
 	parport_ax_write_epp_addr,
@@ -572,6 +539,20 @@
 	if (p->dma == PARPORT_DMA_AUTO)
 		p->dma = (p->modes & PARPORT_MODE_PCECP) ? 0 : PARPORT_DMA_NONE;
 
+	if (p->irq != PARPORT_IRQ_NONE) {
+		int err;
+		if ((err = request_irq(p->irq, parport_ax_interrupt,
+				       0, p->name, p)) != 0)
+			return err;
+		else
+			parport_ax_enable_irq(p);
+	}
+	request_region(p->base, p->size, p->name);
+	if (p->modes & PARPORT_MODE_PCECR)
+		request_region(p->base+0x400, 3, p->name);
+	request_region((unsigned long)p->private_data,
+		       sizeof(struct linux_ebus_dma), p->name);
+
 	printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
 	if (p->irq != PARPORT_IRQ_NONE)
 		printk(", irq %s", __irq_itoa(p->irq));
@@ -587,7 +568,6 @@
 	}
 	printk("]\n");
 	parport_proc_register(p);
-	p->flags |= PARPORT_FLAG_COMA;
 
 	p->ops->write_control(p, 0x0c);
 	p->ops->write_data(p, 0);
@@ -595,6 +575,8 @@
 	if (parport_probe_hook)
 		(*parport_probe_hook)(p);
 
+	parport_announce_port (p);
+
 	return 1;
 }
 
@@ -627,8 +609,15 @@
 	while (p) {
 		tmp = p->next;
 		if (p->modes & PARPORT_MODE_PCSPP) { 
-			if (!(p->flags & PARPORT_FLAG_COMA)) 
-				parport_quiesce(p);
+			if (p->irq != PARPORT_IRQ_NONE) {
+				parport_ax_disable_irq(p);
+				free_irq(p->irq, p);
+			}
+			release_region(p->base, p->size);
+			if (p->modes & PARPORT_MODE_PCECR)
+				release_region(p->base+0x400, 3);
+			release_region((unsigned long)p->private_data,
+				       sizeof(struct linux_ebus_dma));
 			parport_proc_unregister(p);
 			parport_unregister_port(p);
 		}
diff -u --recursive --new-file v2.3.5/linux/drivers/misc/parport_init.c linux/drivers/misc/parport_init.c
--- v2.3.5/linux/drivers/misc/parport_init.c	Mon May 31 22:28:05 1999
+++ linux/drivers/misc/parport_init.c	Thu Jun  3 16:21:47 1999
@@ -99,13 +99,17 @@
 #ifdef MODULE
 int init_module(void)
 {
-	(void)parport_proc_init();	/* We can go on without it. */
+#ifdef CONFIG_SYSCTL
+	parport_default_proc_register ();
+#endif
 	return 0;
 }
 
 void cleanup_module(void)
 {
-	parport_proc_cleanup();
+#ifdef CONFIG_SYSCTL
+	parport_default_proc_unregister ();
+#endif
 }
 
 #else
@@ -118,9 +122,10 @@
 #ifdef CONFIG_PNP_PARPORT
 	parport_probe_hook = &parport_probe_one;
 #endif
-#ifdef	CONFIG_PROC_FS
-	parport_proc_init();
+#ifdef	CONFIG_SYSCTL
+	parport_default_proc_register ();
 #endif
+
 #ifdef CONFIG_PARPORT_PC
 	parport_pc_init(io, io_hi, irq, dma);
 #endif
@@ -136,6 +141,9 @@
 #ifdef CONFIG_PARPORT_ATARI
 	parport_atari_init();
 #endif
+#ifdef CONFIG_PARPORT_ARC
+	parport_arc_init();
+#endif
 	return 0;
 }
 #endif
@@ -148,7 +156,6 @@
 EXPORT_SYMBOL(parport_register_port);
 EXPORT_SYMBOL(parport_announce_port);
 EXPORT_SYMBOL(parport_unregister_port);
-EXPORT_SYMBOL(parport_quiesce);
 EXPORT_SYMBOL(parport_register_driver);
 EXPORT_SYMBOL(parport_unregister_driver);
 EXPORT_SYMBOL(parport_register_device);
diff -u --recursive --new-file v2.3.5/linux/drivers/misc/parport_mfc3.c linux/drivers/misc/parport_mfc3.c
--- v2.3.5/linux/drivers/misc/parport_mfc3.c	Tue May 11 09:55:49 1999
+++ linux/drivers/misc/parport_mfc3.c	Thu Jun  3 16:21:47 1999
@@ -217,22 +217,9 @@
 			}
 }
 
-static void mfc3_release_resources(struct parport *p)
-{
-DPRINTK("realease_resources\n");
-	if (p->irq != PARPORT_IRQ_NONE) 
-		if (--use_cnt == 0) 
-			free_irq(IRQ_AMIGA_PORTS, &pp_mfc3_ops);
-}
-
 static int mfc3_claim_resources(struct parport *p)
 {
 DPRINTK("claim_resources\n");
-	if (p->irq != PARPORT_IRQ_NONE)
-		if (use_cnt++ == 0)
-			if (request_irq(IRQ_AMIGA_PORTS, mfc3_interrupt, 0, p->name, &pp_mfc3_ops))
-				 return use_cnt--;
-	return 0;
 }
 
 static void mfc3_init_state(struct parport_state *s)
@@ -379,11 +366,17 @@
 					printk(KERN_INFO "%s: Multiface III port using irq\n", p->name);
 					/* XXX: set operating mode */
 					parport_proc_register(p);
-					p->flags |= PARPORT_FLAG_COMA;
+
+					if (p->irq != PARPORT_IRQ_NONE)
+						if (use_cnt++ == 0)
+							if (request_irq(IRQ_AMIGA_PORTS, mfc3_interrupt, 0, p->name, &pp_mfc3_ops))
+								use_cnt--;
+
 					if (parport_probe_hook)
 						(*parport_probe_hook)(p);
 					zorro_config_board(key, 0);
 					p->private_data = (void *)key;
+					parport_announce_port (p);
 				}
 			}
 		}
@@ -408,8 +401,9 @@
 
 	for (i = 0; i < MAX_MFC; i++)
 		if (this_port[i] != NULL) {
-			if (!(this_port[i]->flags & PARPORT_FLAG_COMA))
-				parport_quiesce(this_port[i]);
+			if (p->irq != PARPORT_IRQ_NONE) 
+				if (--use_cnt == 0) 
+			free_irq(IRQ_AMIGA_PORTS, &pp_mfc3_ops);
 			parport_proc_unregister(this_port[i]);
 			parport_unregister_port(this_port[i]);
 			zorro_unconfig_board((unsigned int)this_port[i]->private_data, 0);
diff -u --recursive --new-file v2.3.5/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
--- v2.3.5/linux/drivers/misc/parport_pc.c	Mon May 31 22:28:05 1999
+++ linux/drivers/misc/parport_pc.c	Mon Jun  7 13:25:18 1999
@@ -9,6 +9,7 @@
  * based on work by Grant Guenther <grant@torque.net> and Phil Blundell.
  *
  * Cleaned up include files - Russell King <linux@arm.uk.linux.org>
+ * Better EPP probing - Carlos Henrique Bauer <chbauer@acm.org>
  */
 
 /* This driver should work with any hardware that is broadly compatible
@@ -47,6 +48,7 @@
 #include <linux/pci.h>
 
 #include <asm/io.h>
+#include <asm/dma.h>
 
 #include <linux/parport.h>
 #include <linux/parport_pc.h>
@@ -57,6 +59,26 @@
 
 static int user_specified __initdata = 0;
 
+/*
+ * Clear TIMEOUT BIT in EPP MODE
+ */
+int parport_pc_epp_clear_timeout(struct parport *pb)
+{
+	unsigned char r;
+
+	if (!(parport_pc_read_status(pb) & 0x01))
+		return 1;
+
+	/* To clear timeout some chips require double read */
+	parport_pc_read_status(pb);
+	r = parport_pc_read_status(pb);
+	parport_pc_write_status(pb, r | 0x01); /* Some reset by writing 1 */
+	parport_pc_write_status(pb, r & 0xfe); /* Others by writing 0 */
+	r = parport_pc_read_status(pb);
+
+	return !(r & 0x01);
+}
+
 static void parport_pc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	parport_generic_irq(irq, (struct parport *) dev_id, regs);
@@ -179,28 +201,6 @@
 	parport_pc_frob_control(p, 0x10, 0x10);
 }
 
-void parport_pc_release_resources(struct parport *p)
-{
-	if (p->irq != PARPORT_IRQ_NONE)
-		free_irq(p->irq, p);
-	release_region(p->base, p->size);
-	if (p->modes & PARPORT_MODE_PCECR)
-		release_region(p->base_hi, 3);
-}
-
-int parport_pc_claim_resources(struct parport *p)
-{
-	int err;
-	if (p->irq != PARPORT_IRQ_NONE)
-		if ((err = request_irq(p->irq, parport_pc_interrupt,
-				       0, p->name, p)) != 0)
-			return err;
-	request_region(p->base, p->size, p->name);
-	if (p->modes & PARPORT_MODE_PCECR)
-		request_region(p->base_hi, 3, p->name);
-	return 0;
-}
-
 void parport_pc_init_state(struct parport_state *s)
 {
 	s->u.pc.ctr = 0xc;
@@ -298,9 +298,6 @@
 	
 	parport_pc_change_mode,
 	
-	parport_pc_release_resources,
-	parport_pc_claim_resources,
-	
 	parport_pc_write_epp,
 	parport_pc_read_epp,
 	parport_pc_write_epp_addr,
@@ -328,26 +325,6 @@
 
 /* --- Mode detection ------------------------------------- */
 
-/*
- * Clear TIMEOUT BIT in EPP MODE
- */
-int parport_pc_epp_clear_timeout(struct parport *pb)
-{
-	unsigned char r;
-
-	if (!(parport_pc_read_status(pb) & 0x01))
-		return 1;
-
-	/* To clear timeout some chips require double read */
-	parport_pc_read_status(pb);
-	r = parport_pc_read_status(pb);
-	parport_pc_write_status(pb, r | 0x01); /* Some reset by writing 1 */
-	parport_pc_write_status(pb, r & 0xfe); /* Others by writing 0 */
-	r = parport_pc_read_status(pb);
-
-	return !(r & 0x01);
-}
-
 
 /*
  * Checks for port existence, all ports support SPP MODE
@@ -373,12 +350,12 @@
 	 * copy. Some ports _do_ allow reads, so bypass the software
 	 * copy here.  In addition, some bits aren't writable. */
 	r = inb (CONTROL (pb));
-	if ((r & 0x3f) == w) {
+	if ((r & 0xf) == w) {
 		w = 0xe;
 		parport_pc_write_control (pb, w);
 		r = inb (CONTROL(pb));
 		parport_pc_write_control (pb, 0xc);
-		if ((r & 0x3f) == w)
+		if ((r & 0xf) == w)
 			return PARPORT_MODE_PCSPP;
 	}
 
@@ -501,6 +478,19 @@
 	if (!parport_pc_epp_clear_timeout(pb))
 		return 0;  /* No way to clear timeout */
 
+	/*
+	 * Theory:
+	 *	Bit 0 of STR is the EPP timeout bit, this bit is 0
+	 *	when EPP is possible and is set high when an EPP timeout
+	 *	occurs (EPP uses the HALT line to stop the CPU while it does
+	 *	the byte transfer, an EPP timeout occurs if the attached
+	 *	device fails to respond after 10 micro seconds).
+	 *
+	 *	This bit is cleared by either reading it (National Semi)
+	 *	or writing a 1 to the bit (SMC, UMC, WinBond), others ???
+	 *	This bit is always high in non EPP modes.
+	 */
+
 	parport_pc_write_control(pb, parport_pc_read_control(pb) | 0x20);
 	parport_pc_write_control(pb, parport_pc_read_control(pb) | 0x10);
 	parport_pc_epp_clear_timeout(pb);
@@ -513,6 +503,45 @@
 		return PARPORT_MODE_PCEPP;
 	}
 
+	/*
+	 * Theory:
+	 *     Write two values to the EPP address register and
+	 *     read them back. When the transfer times out, the state of
+	 *     the EPP register is undefined in some cases (EPP 1.9?) but
+	 *     in others (EPP 1.7, ECPEPP?) it is possible to read back
+	 *     its value.
+	 */
+	parport_pc_epp_clear_timeout(pb);
+	udelay(30); /* Wait for possible EPP timeout */
+
+	/* Previous test left outputs disabled. */
+	outb (0x55, EPPADDR (pb));
+
+	parport_pc_epp_clear_timeout(pb);
+	udelay(30); /* Wait for possible EPP timeout */
+
+	/* We must enable the outputs to be able to read the address
+           register. */
+	parport_pc_frob_control (pb, 0x20, 0x00);
+
+	if (inb (EPPADDR (pb)) == 0x55) {
+
+		/* wash ... */
+		parport_pc_frob_control (pb, 0x20, 0x20);
+		outb (0xaa, EPPADDR (pb));
+
+		parport_pc_epp_clear_timeout(pb);
+		udelay(30); /* Wait for possible EPP timeout */
+
+		/* ... and repeat */
+		parport_pc_frob_control (pb, 0x20, 0x00);
+
+		if (inb (EPPADDR (pb)) == 0xaa) {
+			parport_pc_epp_clear_timeout (pb);
+			return PARPORT_MODE_PCEPP;
+		}
+	}
+
 	return 0;
 }
 
@@ -638,14 +667,13 @@
  */
 static int __init irq_probe_EPP(struct parport *pb)
 {
+#ifndef ADVANCED_DETECT
+	return PARPORT_IRQ_NONE;
+#else
 	int irqs;
 	unsigned char octr = parport_pc_read_control(pb);
 	unsigned char oecr;
 
-#ifndef ADVANCED_DETECT
-	return PARPORT_IRQ_NONE;
-#endif
-
 	if (pb->modes & PARPORT_MODE_PCECR)
 		oecr = parport_pc_read_econtrol(pb);
 
@@ -675,18 +703,19 @@
 		pb->irq = PARPORT_IRQ_NONE;
 
 	return pb->irq;
+#endif /* Advanced detection. */
 }
 
 static int __init irq_probe_SPP(struct parport *pb)
 {
+#ifndef ADVANCED_DETECT
+	/* Don't even try to do this. */
+	return PARPORT_IRQ_NONE;
+#else
 	int irqs;
 	unsigned char octr = parport_pc_read_control(pb);
 	unsigned char oecr;
 
-#ifndef ADVANCED_DETECT
-	return PARPORT_IRQ_NONE;
-#endif
-
 	if (pb->modes & PARPORT_MODE_PCECR)
 		oecr = parport_pc_read_econtrol(pb);
 	probe_irq_off(probe_irq_on());	/* Clear any interrupts */
@@ -716,6 +745,7 @@
 		parport_pc_write_econtrol(pb, oecr);
 	parport_pc_write_control(pb, octr);
 	return pb->irq;
+#endif /* Advanced detection. */
 }
 
 /* We will attempt to share interrupt requests since other devices
@@ -760,44 +790,59 @@
 				 unsigned long int base_hi,
 				 int irq, int dma)
 {
-	struct parport *p;
+	struct parport_pc_private *priv;
+	struct parport tmp;
+	struct parport *p = &tmp;
 	int probedirq = PARPORT_IRQ_NONE;
 	if (check_region(base, 3)) return 0;
-	if (!(p = parport_register_port(base, irq, dma, &parport_pc_ops)))
-		return 0;
-	p->private_data = kmalloc (sizeof (struct parport_pc_private),
-				   GFP_KERNEL);
-	if (!p->private_data) {
-		/* Not enough memory. */
+	priv = kmalloc (sizeof (struct parport_pc_private), GFP_KERNEL);
+	if (!priv) {
 		printk (KERN_DEBUG "parport (0x%lx): no memory!\n", base);
-		parport_unregister_port (p);
 		return 0;
 	}
-	((struct parport_pc_private *) (p->private_data))->ctr = 0xc;
+	priv->ctr = 0xc;
+	p->base = base;
 	p->base_hi = base_hi;
+	p->irq = irq;
+	p->dma = dma;
+	p->modes = PARPORT_MODE_PCSPP;
+	p->ops = &parport_pc_ops;
+	p->private_data = priv;
+	if (base_hi && !check_region (base_hi, 3)) {
+		p->modes |= parport_ECR_present (p);
+		p->modes |= parport_ECP_supported (p);
+		p->modes |= parport_ECPPS2_supported (p);
+	}
 	if (p->base != 0x3bc) {
-		if (base_hi && !check_region(base_hi,3)) {
-			p->modes |= parport_ECR_present(p);	
-			p->modes |= parport_ECP_supported(p);
-			p->modes |= parport_ECPPS2_supported(p);
-		}
 		if (!check_region(base+0x3, 5)) {
-			p->modes |= parport_EPP_supported(p);
-			p->modes |= parport_ECPEPP_supported(p);
+			p->modes |= parport_EPP_supported (p);
+			p->modes |= parport_ECPEPP_supported (p);
 		}
 	}
 	if (!parport_SPP_supported(p)) {
 		/* No port. */
-		kfree (p->private_data);
-		parport_unregister_port (p);
+		kfree (priv);
+		return 0;
+	}
+
+	p->modes |= parport_PS2_supported(p);
+
+	if (!(p = parport_register_port (base, PARPORT_IRQ_NONE,
+					 PARPORT_DMA_NONE, &parport_pc_ops))) {
+		kfree (priv);
 		return 0;
 	}
-	p->modes |= PARPORT_MODE_PCSPP | parport_PS2_supported(p);
-	p->size = (p->modes & (PARPORT_MODE_PCEPP 
-			       | PARPORT_MODE_PCECPEPP))?8:3;
+
+	p->base_hi = base_hi;
+	p->modes = tmp.modes;
+	p->size = (p->modes & PARPORT_MODE_PCEPP) ? 8 : 3;
+	p->private_data = priv;
+
 	printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
 	if (p->base_hi && (p->modes & PARPORT_MODE_PCECR))
 		printk (" (0x%lx)", p->base_hi);
+	p->irq = irq;
+	p->dma = dma;
 	if (p->irq == PARPORT_IRQ_AUTO) {
 		p->irq = PARPORT_IRQ_NONE;
 		parport_irq_probe(p);
@@ -831,7 +876,30 @@
 		printk("%s: detected irq %d; use procfs to enable interrupt-driven operation.\n", p->name, probedirq);
 #endif
 	parport_proc_register(p);
-	p->flags |= PARPORT_FLAG_COMA;
+
+	request_region (p->base, p->size, p->name);
+	if (p->modes & PARPORT_MODE_PCECR)
+		request_region (p->base_hi, 3, p->name);
+
+	if (p->irq != PARPORT_IRQ_NONE) {
+		if (request_irq (p->irq, parport_pc_interrupt,
+				 0, p->name, p)) {
+			printk (KERN_WARNING "%s: irq %d in use, "
+				"resorting to polled operation\n",
+				p->name, p->irq);
+			p->irq = PARPORT_IRQ_NONE;
+			p->dma = PARPORT_DMA_NONE;
+		}
+
+		if (p->dma != PARPORT_DMA_NONE) {
+			if (request_dma (p->dma, p->name)) {
+				printk (KERN_WARNING "%s: dma %d in use, "
+					"resorting to PIO operation\n",
+					p->name, p->dma);
+				p->dma = PARPORT_DMA_NONE;
+			}
+		}
+	}
 
 	/* Done probing.  Now put the port into a sensible start-up state. */
 	if (p->modes & PARPORT_MODE_PCECR)
@@ -859,6 +927,34 @@
 /* Look for PCI parallel port cards. */
 static int __init parport_pc_init_pci (int irq, int dma)
 {
+/* These need to go in pci.h: */
+#ifndef PCI_VENDOR_ID_SIIG
+#define PCI_VENDOR_ID_SIIG              0x131f
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_550 0x1010
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_650 0x1011
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_850 0x1012
+#define PCI_DEVICE_ID_SIIG_1P_10x       0x1020
+#define PCI_DEVICE_ID_SIIG_2P_10x       0x1021
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_550 0x1034
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_650 0x1035
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_850 0x1036
+#define PCI_DEVICE_ID_SIIG_1P_20x       0x2020
+#define PCI_DEVICE_ID_SIIG_2P_20x       0x2021
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_550 0x2040
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_650 0x2041
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_850 0x2042
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_550 0x2010
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_650 0x2011
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_850 0x2012
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_550 0x2060
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_650 0x2061
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_850 0x2062
+#define PCI_VENDOR_ID_LAVA              0x1407
+#define PCI_DEVICE_ID_LAVA_PARALLEL     0x8000
+#define PCI_DEVICE_ID_LAVA_DUAL_PAR_A   0x8001 /* The Lava Dual Parallel is */
+#define PCI_DEVICE_ID_LAVA_DUAL_PAR_B   0x8002 /* two PCI devices on a card */
+#endif /* IDs not defined */
+
 	int count = 0;
 #ifdef CONFIG_PCI
 	int i;
@@ -871,6 +967,50 @@
 			unsigned int hi; /* -ve if not there */
 		} addr[4];
 	} cards[] = {
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, 1,
+		  { { 3, 4 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, 1,
+		  { { 3, 4 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, 1,
+		  { { 3, 4 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_10x, 1,
+		  { { 2, 3 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_10x, 2,
+		  { { 2, 3 }, { 4, 5 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, 1,
+		  { { 4, 5 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, 1,
+		  { { 4, 5 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, 1,
+		  { { 4, 5 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_20x, 1,
+		  { { 0, 1 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_20x, 2,
+		  { { 0, 1 }, { 2, 3 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, 2,
+		  { { 1, 2 }, { 3, 4 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, 2,
+		  { { 1, 2 }, { 3, 4 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, 2,
+		  { { 1, 2 }, { 3, 4 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, 1,
+		  { { 1, 2 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, 1,
+		  { { 1, 2 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, 1,
+		  { { 1, 2 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, 1,
+		  { { 2, 3 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, 1,
+		  { { 2, 3 }, } },
+		{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, 1,
+		  { { 2, 3 }, } },
+		{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PARALLEL, 1,
+		  { { 0, -1 }, } },
+		{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A, 1,
+		  { { 0, -1 }, } },
+		{ PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B, 1,
+		  { { 0, -1 }, } },
 		{ 0, }
 	};
 
@@ -980,9 +1120,14 @@
 	struct parport *p = parport_enumerate(), *tmp;
 	while (p) {
 		tmp = p->next;
-		if (p->modes & PARPORT_MODE_PCSPP) { 
-			if (!(p->flags & PARPORT_FLAG_COMA)) 
-				parport_quiesce(p);
+		if (p->modes & PARPORT_MODE_PCSPP) {
+			if (p->dma != PARPORT_DMA_NONE)
+				free_dma (p->dma);
+			if (p->irq != PARPORT_IRQ_NONE)
+				free_irq (p->irq, p);
+			release_region (p->base, p->size);
+			if (p->modes & PARPORT_MODE_PCECP)
+				release_region (p->base_hi, 3);
 			parport_proc_unregister(p);
 			kfree (p->private_data);
 			parport_unregister_port(p);
diff -u --recursive --new-file v2.3.5/linux/drivers/misc/parport_procfs.c linux/drivers/misc/parport_procfs.c
--- v2.3.5/linux/drivers/misc/parport_procfs.c	Wed Jun  2 14:44:39 1999
+++ linux/drivers/misc/parport_procfs.c	Thu Jun  3 16:21:47 1999
@@ -1,4 +1,4 @@
-/* Parallel port /proc interface code.
+/* Sysctl interface for parport devices.
  * 
  * Authors: David Campbell <campbell@torque.net>
  *          Tim Waugh <tim@cyberelk.demon.co.uk>
@@ -14,400 +14,425 @@
 
 #include <linux/string.h>
 #include <linux/config.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
 #include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
 #include <linux/kernel.h>
 #include <linux/malloc.h>
-#include <linux/proc_fs.h>
 #include <linux/parport.h>
 #include <linux/ctype.h>
+#include <linux/sysctl.h>
 
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/irq.h>
-
-#ifdef CONFIG_PROC_FS
-
-struct proc_dir_entry *base = NULL;
-
-static int irq_write_proc(struct file *file, const char *buffer,
-			  unsigned long count, void *data)
-{
-	int retval = -EINVAL;
-	int newirq = PARPORT_IRQ_NONE;
-	struct parport *pp = (struct parport *)data;
-	int oldirq = pp->irq;
-
-/*
- * We can have these valid cases:
- * 	"none" (count == 4 || count == 5)
- * 	decimal number (count == 2 || count == 3)
- * 	octal number (count == 3 || count == 4)
- * 	hex number (count == 4 || count == 5)
- * all other cases are -EINVAL
- *
- * Note: newirq is alredy set up to NONE.
- *
- * -RF
- */
-	if (count > 5  || count < 1)
-		goto out;
-
-	if (isdigit(buffer[0]))
-		newirq = simple_strtoul(buffer, NULL, 0);
-	else if (strncmp(buffer, "none", 4) != 0) {
-		if (buffer[0] < 32)
-			/* Things like '\n' are harmless */
-			retval = count;
-
-		goto out;
-	}
+#include <asm/uaccess.h>
 
-	retval = count;
+#ifdef CONFIG_SYSCTL
 
-	if (oldirq == newirq)
-		goto out;
 
-	if (pp->flags & PARPORT_FLAG_COMA)
-		goto out_ok;
-
-	retval = -EBUSY;
+static int do_active_device(ctl_table *table, int write, struct file *filp,
+		      void *result, size_t *lenp)
+{
+	struct parport *port = (struct parport *)table->extra1;
+	char buffer[256];
+	struct pardevice *dev;
+	int len = 0;
 
-	/*
-	 * Here we don' t need the irq version of spinlocks because
-	 * the parport_lowlevel irq handler must not change the cad,
-	 * and so has no one reason to write_lock() the cad_lock spinlock.
-	 *						-arca
-	 */
-	read_lock(&pp->cad_lock);
+	if (write)		/* can't happen anyway */
+		return -EACCES;
 
-	if (pp->cad)
-	{
-		read_unlock(&pp->cad_lock);
-		return retval;
+	if (filp->f_pos) {
+		*lenp = 0;
+		return 0;
 	}
-
-	if (newirq != PARPORT_IRQ_NONE) { 
-		retval = request_irq(newirq, pp->ops->interrupt,
-				     0, pp->name, pp);
-		if (retval)
-		{
-			read_unlock(&pp->cad_lock);
-			return retval;
+	
+	for (dev = port->devices; dev ; dev = dev->next) {
+		if(dev == port->cad) {
+			len += sprintf(buffer, "%s\n", dev->name);
 		}
 	}
 
-	if (oldirq != PARPORT_IRQ_NONE)
-		free_irq(oldirq, pp);
-
-	retval = count;
+	if(!len) {
+		len += sprintf(buffer, "%s\n", "none");
+	}
 
-	read_unlock(&pp->cad_lock);
+	if (len > *lenp)
+		len = *lenp;
+	else
+		*lenp = len;
 
-out_ok:
-	pp->irq = newirq;
+	filp->f_pos += len;
 
-out:
-	return retval;
+	return copy_to_user(result, buffer, len) ? -EFAULT : 0;
 }
 
-static int irq_read_proc(char *page, char **start, off_t off,
-					 int count, int *eof, void *data)
+#if 0 && defined (CONFIG_PARPORT_1284)
+static int do_autoprobe(ctl_table *table, int write, struct file *filp,
+			void *result, size_t *lenp)
 {
-	struct parport *pp = (struct parport *)data;
-	int len;
-	
-	if (pp->irq == PARPORT_IRQ_NONE) {
-		len = sprintf(page, "none\n");
-	} else {
-#ifdef __sparc__
-		len = sprintf(page, "%s\n", __irq_itoa(pp->irq));
-#else
-		len = sprintf(page, "%d\n", pp->irq);
-#endif
+	struct parport_device_info *info = table->extra2;
+	const char *str;
+	char buffer[256];
+	int len = 0;
+
+	if (write) /* permissions stop this */
+		return -EACCES;
+
+	if (filp->f_pos) {
+		*lenp = 0;
+		return 0;
 	}
 	
-	*start = 0;
-	*eof   = 1;
-	return len;
-}
+	if ((str = info->class_name) != NULL)
+		len += sprintf (buffer + len, "CLASS:%s;\n", str);
 
-static int devices_read_proc(char *page, char **start, off_t off,
-					 int count, int *eof, void *data)
-{
-	struct parport *pp = (struct parport *)data;
-	struct pardevice *pd1;
-	int len=0;
+	if ((str = info->model) != NULL)
+		len += sprintf (buffer + len, "MODEL:%s;\n", str);
 
-	for (pd1 = pp->devices; pd1 ; pd1 = pd1->next) {
-		if (pd1 == pp->cad)
-			page[len++] = '+';
-		else
-			page[len++] = ' ';
+	if ((str = info->mfr) != NULL)
+		len += sprintf (buffer + len, "MANUFACTURER:%s;\n", str);
 
-		len += sprintf(page+len, "%s", pd1->name);
+	if ((str = info->description) != NULL)
+		len += sprintf (buffer + len, "DESCRIPTION:%s;\n", str);
 
-		page[len++] = '\n';
-	}
-		
-	*start = 0;
-	*eof   = 1;
-	return len;
+	if ((str = info->cmdset) != NULL)
+		len += sprintf (buffer + len, "COMMAND SET:%s;\n", str);
+
+	if (len > *lenp)
+		len = *lenp;
+	else
+		*lenp = len;
+
+	filp->f_pos += len;
+
+	return copy_to_user (result, buffer, len) ? -EFAULT : 0;
 }
+#endif /* IEEE1284.3 support. */
 
-static int hardware_read_proc(char *page, char **start, off_t off,
-				  int count, int *eof, void *data)
+static int do_hardware(ctl_table *table, int write, struct file *filp,
+		       void *result, size_t *lenp)
 {
-	struct parport *pp = (struct parport *)data;
-	int len=0;
+	struct parport *port = (struct parport *)table->extra1;
+	char buffer[256];
+	int len = 0;
+
+	if (filp->f_pos) {
+		*lenp = 0;
+		return 0;
+	}
+	
+	if (write)		/* can't happen anyway */
+		return -EACCES;
 	
-	len += sprintf(page+len, "base:\t0x%lx\n",pp->base);
+	len += sprintf(buffer+len, "base:\t0x%lx", port->base);
+	if (port->base_hi)
+		len += sprintf(buffer+len, " (0x%lx)", port->base_hi);
+	buffer[len++] = '\n';
 
-	if (pp->irq == PARPORT_IRQ_NONE) {
-		len += sprintf(page+len, "irq:\tnone\n");
+	if (port->irq == PARPORT_IRQ_NONE) {
+		len += sprintf(buffer+len, "irq:\tnone\n");
 	} else {
 #ifdef __sparc__
-		len += sprintf(page+len, "irq:\t%s\n",__irq_itoa(pp->irq));
+		len += sprintf(buffer+len, "irq:\t%s\n", 
+			       __irq_itoa(port->irq));
 #else
-		len += sprintf(page+len, "irq:\t%d\n",pp->irq);
+		len += sprintf(buffer+len, "irq:\t%d\n", port->irq);
 #endif
 	}
 
-	if (pp->dma == PARPORT_DMA_NONE)
-		len += sprintf(page+len, "dma:\tnone\n");
+	if (port->dma == PARPORT_DMA_NONE)
+		len += sprintf(buffer+len, "dma:\tnone\n");
 	else
-		len += sprintf(page+len, "dma:\t%d\n",pp->dma);
+		len += sprintf(buffer+len, "dma:\t%d\n", port->dma);
 
-	len += sprintf(page+len, "modes:\t");
+	len += sprintf(buffer+len, "modes:\t");
 	{
-#define printmode(x) {if(pp->modes&PARPORT_MODE_PC##x){len+=sprintf(page+len,"%s%s",f?",":"",#x);f++;}}
+#define printmode(x) {if(port->modes&PARPORT_MODE_##x){len+=sprintf(buffer+len,"%s%s",f?",":"",#x);f++;}}
 		int f = 0;
-		printmode(SPP);
-		printmode(PS2);
-		printmode(EPP);
-		printmode(ECP);
-		printmode(ECPEPP);
-		printmode(ECPPS2);
+		printmode(PCSPP);
+		printmode(PCPS2);
+		printmode(PCEPP);
+		printmode(PCECP);
 #undef printmode
 	}
-	page[len++] = '\n';
+	buffer[len++] = '\n';
+
+	if (len > *lenp)
+		len = *lenp;
+	else
+		*lenp = len;
+
+	filp->f_pos += len;
 
-	*start = 0;
-	*eof   = 1;
-	return len;
+	return copy_to_user(result, buffer, len) ? -EFAULT : 0;
 }
 
-static int autoprobe_read_proc (char *page, char **start, off_t off,
-				int count, int *eof, void *data)
-{
-	struct parport *pp = (struct parport *) data;
-	int len = 0;
-	const char *str;
+#define PARPORT_PORT_DIR(child) { 0, NULL, NULL, 0, 0555, child }
+#define PARPORT_PARPORT_DIR(child) { DEV_PARPORT, "parport", \
+                                     NULL, 0, 0555, child }
+#define PARPORT_DEV_DIR(child) { CTL_DEV, "dev", NULL, 0, 0555, child }
+#define PARPORT_DEVICES_ROOT_DIR  { DEV_PARPORT_DEVICES, "devices", \
+                                    NULL, 0, 0555, NULL }
+
+
+struct parport_sysctl_table {
+	struct ctl_table_header *sysctl_header;
+	ctl_table vars[9];
+	ctl_table device_dir[2];
+	ctl_table port_dir[2];
+	ctl_table parport_dir[2];
+	ctl_table dev_dir[2];
+};
+
+static const struct parport_sysctl_table parport_sysctl_template = {
+	NULL,
+        {
+		{ DEV_PARPORT_SPINTIME, "spintime",
+		  NULL, sizeof(int), 0644, NULL,
+		  &proc_dointvec },
+		{ DEV_PARPORT_HARDWARE, "hardware",
+		  NULL, 0, 0444, NULL,
+		  &do_hardware },
+		PARPORT_DEVICES_ROOT_DIR,
+#if 0 && defined(CONFIG_PARPORT_1284)
+		{ DEV_PARPORT_AUTOPROBE, "autoprobe",
+		  NULL, 0, 0444, NULL,
+		  &do_autoprobe },
+		{ DEV_PARPORT_AUTOPROBE + 1, "autoprobe0",
+		 NULL, 0, 0444, NULL,
+		 &do_autoprobe },
+		{ DEV_PARPORT_AUTOPROBE + 2, "autoprobe1",
+		  NULL, 0, 0444, NULL,
+		  &do_autoprobe },
+		{ DEV_PARPORT_AUTOPROBE + 3, "autoprobe2",
+		  NULL, 0, 0444, NULL,
+		  &do_autoprobe },
+		{ DEV_PARPORT_AUTOPROBE + 4, "autoprobe3",
+		  NULL, 0, 0444, NULL,
+		  &do_autoprobe },
+#endif /* IEEE 1284 support */
+		{0}
+	},
+	{ {DEV_PARPORT_DEVICES_ACTIVE, "active", NULL, 0, 444, NULL,
+	  &do_active_device }, {0}},
+	{ PARPORT_PORT_DIR(NULL), {0}},
+	{ PARPORT_PARPORT_DIR(NULL), {0}},
+	{ PARPORT_DEV_DIR(NULL), {0}}
+};
+
+struct parport_device_sysctl_table
+{
+	struct ctl_table_header *sysctl_header;
+	ctl_table vars[2];
+	ctl_table device_dir[2];
+	ctl_table devices_root_dir[2];
+	ctl_table port_dir[2];
+	ctl_table parport_dir[2];
+	ctl_table dev_dir[2];
+};
+
+static const struct parport_device_sysctl_table
+parport_device_sysctl_template = {
+	NULL,
+	{
+		{ DEV_PARPORT_DEVICE_TIMESLICE, "timeslice",
+		  NULL, sizeof(int), 0644, NULL,
+		  &proc_dointvec },
+	},
+	{ {0, NULL, NULL, 0, 0555, NULL}, {0}},
+	{ PARPORT_DEVICES_ROOT_DIR, {0}},
+	{ PARPORT_PORT_DIR(NULL), {0}},
+	{ PARPORT_PARPORT_DIR(NULL), {0}},
+	{ PARPORT_DEV_DIR(NULL), {0}}
+};
+
+struct parport_default_sysctl_table
+{
+	struct ctl_table_header *sysctl_header;
+	ctl_table vars[3];
+        ctl_table default_dir[2];
+	ctl_table parport_dir[2];
+	ctl_table dev_dir[2];
+};
+
+extern unsigned long parport_default_timeslice;
+extern int parport_default_spintime;
+
+static struct parport_default_sysctl_table
+parport_default_sysctl_table = {
+	NULL,
+	{
+		{ DEV_PARPORT_DEFAULT_TIMESLICE, "timeslice",
+		  &parport_default_timeslice,
+		  sizeof(parport_default_timeslice), 0644, NULL,
+		  &proc_dointvec },
+		{ DEV_PARPORT_DEFAULT_SPINTIME, "spintime",
+		  &parport_default_spintime,
+		  sizeof(parport_default_timeslice), 0644, NULL,
+		  &proc_dointvec },
+		{0}
+	},
+	{ { DEV_PARPORT_DEFAULT, "default", NULL, 0, 0555,
+	    parport_default_sysctl_table.vars },{0}},
+	{
+	PARPORT_PARPORT_DIR(parport_default_sysctl_table.default_dir), 
+	{0}},
+	{ PARPORT_DEV_DIR(parport_default_sysctl_table.parport_dir), {0}}
+};
 
-	page[0] = '\0';
 
-	if ((str = pp->probe_info.class_name) != NULL)
-		len += sprintf (page+len, "CLASS:%s;\n", str);
+int parport_proc_register(struct parport *port)
+{
+	struct parport_sysctl_table *t;
+	int i;
 
-	if ((str = pp->probe_info.model) != NULL)
-		len += sprintf (page+len, "MODEL:%s;\n", str);
+	t = kmalloc(sizeof(*t), GFP_KERNEL);
+	if (t == NULL)
+		return -ENOMEM;
+	memcpy(t, &parport_sysctl_template, sizeof(*t));
 
-	if ((str = pp->probe_info.mfr) != NULL)
-		len += sprintf (page+len, "MANUFACTURER:%s;\n", str);
+	t->device_dir[0].extra1 = port;
 
-	if ((str = pp->probe_info.description) != NULL)
-		len += sprintf (page+len, "DESCRIPTION:%s;\n", str);
+	for (i = 0; i < 8; i++)
+		t->vars[i].extra1 = port;
 
-	if ((str = pp->probe_info.cmdset) != NULL)
-		len += sprintf (page+len, "COMMAND SET:%s;\n", str);
+#if 0 /* Wait for IEEE 1284 support */
+	t->vars[0].data = &port->spintime;
+#endif
+	t->vars[2].child = t->device_dir;
+	
+	for (i = 0; i < 5; i++)
+#if 0
+		t->vars[3 + i].extra2 = &port->probe_info[i];
+#else
+		t->vars[3 + i].extra2 = &port->probe_info;
+#endif
 
-	*start = 0;
-	*eof   = 1;
-	return len;
-}
+	t->port_dir[0].procname = port->name;
+	t->port_dir[0].ctl_name = port->number + 1; /* nb 0 isn't legal here */
 
-static inline void destroy_proc_entry(struct proc_dir_entry *root, 
-				      struct proc_dir_entry **d)
-{
-	proc_unregister(root, (*d)->low_ino);
-	kfree(*d);
-	*d = NULL;
+	t->port_dir[0].child = t->vars;
+	t->parport_dir[0].child = t->port_dir;
+	t->dev_dir[0].child = t->parport_dir;
+
+	t->sysctl_header = register_sysctl_table(t->dev_dir, 0);
+	if (t->sysctl_header == NULL) {
+		kfree(t);
+		t = NULL;
+	}
+	port->sysctl_table = t;
+	return 0;
 }
 
-static void destroy_proc_tree(struct parport *pp) {
-	if (pp->pdir.entry) {
-		if (pp->pdir.irq) 
-			destroy_proc_entry(pp->pdir.entry, &pp->pdir.irq);
-		if (pp->pdir.devices) 
-			destroy_proc_entry(pp->pdir.entry, &pp->pdir.devices);
-		if (pp->pdir.hardware)
-			destroy_proc_entry(pp->pdir.entry, &pp->pdir.hardware);
-		if (pp->pdir.probe)
-			destroy_proc_entry(pp->pdir.entry, &pp->pdir.probe);
-		destroy_proc_entry(base, &pp->pdir.entry);
+int parport_proc_unregister(struct parport *port)
+{
+	if (port->sysctl_table) {
+		struct parport_sysctl_table *t = port->sysctl_table;
+		port->sysctl_table = NULL;
+		unregister_sysctl_table(t->sysctl_header);
+		kfree(t);
 	}
+	return 0;
 }
 
-static struct proc_dir_entry *new_proc_entry(const char *name, mode_t mode,
-					     struct proc_dir_entry *parent,
-					     unsigned short ino,
-					     struct parport *p)
+int parport_device_proc_register(struct pardevice *device)
 {
-	struct proc_dir_entry *ent;
-
-	ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
-	if (!ent)
-		return NULL;
-
-	memset(ent, 0, sizeof(struct proc_dir_entry));
+	struct parport_device_sysctl_table *t;
+	struct parport * port = device->port;
 	
-	if (mode == S_IFDIR)
-		mode |= S_IRUGO | S_IXUGO;
-	else if (mode == 0)
-		mode = S_IFREG | S_IRUGO;
-
-	ent->low_ino = ino;
-	ent->name = name;
-	ent->namelen = strlen(name);
-	ent->mode = mode;
+	t = kmalloc(sizeof(*t), GFP_KERNEL);
+	if (t == NULL)
+		return -ENOMEM;
+	memcpy(t, &parport_device_sysctl_template, sizeof(*t));
+
+	t->dev_dir[0].child = t->parport_dir;
+	t->parport_dir[0].child = t->port_dir;
+	t->port_dir[0].procname = port->name;
+	t->port_dir[0].ctl_name = port->number + 1; /* nb 0 isn't legal here */
+	t->port_dir[0].child = t->devices_root_dir;
+	t->devices_root_dir[0].child = t->device_dir;
+
+#if 0 && defined(CONFIG_PARPORT_1284)
+
+	t->device_dir[0].ctl_name =
+		parport_device_num(port->number, port->muxport,
+				   device->daisy)
+		+ 1;  /* nb 0 isn't legal here */ 
 
-	if (S_ISDIR(mode))
-	{
-		if (p && p->ops)
-			ent->fill_inode = p->ops->fill_inode;
-		ent->nlink = 2;
-	} else
-		ent->nlink = 1;
+#else /* No IEEE 1284 support */
 
-	proc_register(parent, ent);
+	/* parport_device_num isn't available. */
+	t->device_dir[0].ctl_name = 1;
 	
-	return ent;
-}
+#endif /* IEEE 1284 support or not */
 
-/*
- * This is called as the fill_inode function when an inode
- * is going into (fill = 1) or out of service (fill = 0).
- * We use it here to manage the module use counts.
- *
- * Note: only the top-level directory needs to do this; if
- * a lower level is referenced, the parent will be as well.
- */
-static void parport_modcount(struct inode *inode, int fill)
-{
-#ifdef MODULE
-	if (fill)
-		inc_parport_count();
-	else
-		dec_parport_count();
-#endif
+	t->device_dir[0].procname = device->name;
+	t->device_dir[0].extra1 = device;
+	t->device_dir[0].child = t->vars;
+	t->vars[0].data = &device->timeslice;
+
+	t->sysctl_header = register_sysctl_table(t->dev_dir, 0);
+	if (t->sysctl_header == NULL) {
+		kfree(t);
+		t = NULL;
+	}
+	device->sysctl_table = t;
+	return 0;
 }
 
-int parport_proc_init(void)
+int parport_device_proc_unregister(struct pardevice *device)
 {
-	base = new_proc_entry("parport", S_IFDIR, &proc_root,PROC_PARPORT,
-			      NULL);
-	if (base == NULL) {
-		printk(KERN_ERR "Unable to initialise /proc/parport.\n");
-		return 0;
+	if (device->sysctl_table) {
+		struct parport_device_sysctl_table *t = device->sysctl_table;
+		device->sysctl_table = NULL;
+		unregister_sysctl_table(t->sysctl_header);
+		kfree(t);
 	}
-	base->fill_inode = &parport_modcount;
-
-	return 1;
+	return 0;
 }
 
-void parport_proc_cleanup(void)
+int parport_default_proc_register(void)
 {
-	if (base) {
-		proc_unregister(&proc_root,base->low_ino);
-		kfree(base);
-		base = NULL;
-	}
+	parport_default_sysctl_table.sysctl_header =
+		register_sysctl_table(parport_default_sysctl_table.dev_dir, 0);
+	return 0;
 }
 
-int parport_proc_register(struct parport *pp)
+int parport_default_proc_unregister(void)
 {
-	memset(&pp->pdir, 0, sizeof(struct parport_dir));
-
-	if (base == NULL) {
-		printk(KERN_ERR "parport_proc not initialised yet.\n");
-		return 1;
+	if (parport_default_sysctl_table.sysctl_header) {
+		unregister_sysctl_table(parport_default_sysctl_table.
+					sysctl_header);
+		parport_default_sysctl_table.sysctl_header = NULL;
 	}
-	
-	strncpy(pp->pdir.name, pp->name + strlen("parport"), 
-		sizeof(pp->pdir.name));
-
-	pp->pdir.entry = new_proc_entry(pp->pdir.name, S_IFDIR, base, 0, pp);
-	if (pp->pdir.entry == NULL)
-		goto out_fail;
-
-	pp->pdir.irq = new_proc_entry("irq", S_IFREG | S_IRUGO | S_IWUSR, 
-				      pp->pdir.entry, 0, pp);
-	if (pp->pdir.irq == NULL)
-		goto out_fail;
-
-	pp->pdir.irq->read_proc = irq_read_proc;
-	pp->pdir.irq->write_proc = irq_write_proc;
-	pp->pdir.irq->data = pp;
-	
-	pp->pdir.devices = new_proc_entry("devices", 0, pp->pdir.entry, 0, pp);
-	if (pp->pdir.devices == NULL)
-		goto out_fail;
-
-	pp->pdir.devices->read_proc = devices_read_proc;
-	pp->pdir.devices->data = pp;
-	
-	pp->pdir.hardware = new_proc_entry("hardware", 0, pp->pdir.entry, 0,
-					   pp);
-	if (pp->pdir.hardware == NULL)
-		goto out_fail;
-
-	pp->pdir.hardware->read_proc = hardware_read_proc;
-	pp->pdir.hardware->data = pp;
-
-	pp->pdir.probe = new_proc_entry("autoprobe", 0, pp->pdir.entry, 0, pp);
-	if (pp->pdir.probe == NULL)
-		goto out_fail;
-
-	pp->pdir.probe->read_proc = autoprobe_read_proc;
-	pp->pdir.probe->data = pp;
-
 	return 0;
+}
 
-out_fail:
+#else /* no sysctl */
 
-	printk(KERN_ERR "%s: failure registering /proc/ entry.\n", pp->name);
-	destroy_proc_tree(pp);
-	return 1;
+int parport_proc_register(struct parport *pp)
+{
+	return 0;
 }
 
 int parport_proc_unregister(struct parport *pp)
 {
-	destroy_proc_tree(pp);
 	return 0;
 }
 
-#else
-
-int parport_proc_register(struct parport *p) 
+int parport_device_proc_register(struct pardevice *device)
 {
 	return 0;
 }
 
-int parport_proc_unregister(struct parport *p)
+int parport_device_proc_unregister(struct pardevice *device)
 {
 	return 0;
 }
 
-int parport_proc_init(void)
+int parport_default_proc_register (void)
 {
 	return 0;
 }
 
-void parport_proc_cleanup(void)
+int parport_default_proc_unregister (void)
 {
+	return 0;
 }
-
 #endif
diff -u --recursive --new-file v2.3.5/linux/drivers/misc/parport_share.c linux/drivers/misc/parport_share.c
--- v2.3.5/linux/drivers/misc/parport_share.c	Wed Jun  2 14:44:39 1999
+++ linux/drivers/misc/parport_share.c	Mon Jun  7 14:49:23 1999
@@ -39,6 +39,11 @@
 
 #define PARPORT_DEFAULT_TIMESLICE	(HZ/5)
 
+unsigned long parport_default_timeslice = PARPORT_DEFAULT_TIMESLICE;
+
+/* This doesn't do anything yet. */
+int parport_default_spintime;
+
 static struct parport *portlist = NULL, *portlist_tail = NULL;
 spinlock_t parportlist_lock = SPIN_LOCK_UNLOCKED;
 
@@ -231,25 +236,6 @@
 	kfree(port);
 }
 
-void parport_quiesce(struct parport *port)
-{
-	if (port->devices) {
-		printk(KERN_WARNING "%s: attempt to quiesce active port.\n",
-		       port->name);
-		return;
-	}
-
-	if (port->flags & PARPORT_FLAG_COMA) {
-		printk(KERN_WARNING "%s: attempt to quiesce comatose port.\n",
-		       port->name);
-		return;
-	}
-
-	port->ops->release_resources(port);
-
-	port->flags |= PARPORT_FLAG_COMA; 
-}
-
 struct pardevice *parport_register_device(struct parport *port, const char *name,
 			  int (*pf)(void *), void (*kf)(void *),
 			  void (*irq_func)(int, void *, struct pt_regs *), 
@@ -284,19 +270,6 @@
 		return NULL;
 	}
 
-	/* We may need to claw back the port hardware. */
-	if (port->flags & PARPORT_FLAG_COMA) {
-		if (port->ops->claim_resources(port)) {
-			printk(KERN_WARNING
-			       "%s: unable to get hardware to register %s.\n",
-			       port->name, name);
-			kfree (tmp->state);
-			kfree (tmp);
-			return NULL;
-		}
-		port->flags &= ~PARPORT_FLAG_COMA;
-	}
-
 	tmp->name = name;
 	tmp->port = port;
 	tmp->preempt = pf;
@@ -338,7 +311,7 @@
 	port->ops->inc_use_count();
 
 	init_waitqueue_head(&tmp->wait_q);
-	tmp->timeslice = PARPORT_DEFAULT_TIMESLICE;
+	tmp->timeslice = parport_default_timeslice;
 	tmp->waitnext = tmp->waitprev = NULL;
 
 	return tmp;
@@ -358,9 +331,9 @@
 	port = dev->port;
 
 	if (port->cad == dev) {
-		printk(KERN_WARNING "%s: refused to unregister "
-		       "currently active device %s.\n", port->name, dev->name);
-		return;
+		printk(KERN_DEBUG "%s: %s forgot to release port\n",
+		       port->name, dev->name);
+		parport_release (dev);
 	}
 
 	spin_lock(&port->pardevice_lock);
@@ -381,10 +354,6 @@
 
 	dec_parport_count();
 	port->ops->dec_use_count();
-
-	/* If there are no more devices, put the port to sleep. */
-	if (!port->devices)
-		parport_quiesce(port);
 
 	return;
 }
diff -u --recursive --new-file v2.3.5/linux/drivers/net/3c515.c linux/drivers/net/3c515.c
--- v2.3.5/linux/drivers/net/3c515.c	Thu Jan  7 08:46:59 1999
+++ linux/drivers/net/3c515.c	Mon Jun  7 16:18:58 1999
@@ -1009,6 +1009,7 @@
 			outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
 		}
 	}
+	vp->stats.tx_bytes+=skb->len;
 	return 0;
 }
 
@@ -1209,6 +1210,7 @@
 				netif_rx(skb);
 				dev->last_rx = jiffies;
 				vp->stats.rx_packets++;
+				vp->stats.rx_bytes+=skb->len;
 				/* Wait a limited time to go to next packet. */
 				for (i = 200; i >= 0; i--)
 					if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
@@ -1256,6 +1258,7 @@
 			short pkt_len = rx_status & 0x1fff;
 			struct sk_buff *skb;
 
+			vp->stats.rx_bytes+=pkt_len;
 			if (vortex_debug > 4)
 				printk("Receiving packet size %d status %4.4x.\n",
 					   pkt_len, rx_status);
diff -u --recursive --new-file v2.3.5/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.3.5/linux/drivers/net/Config.in	Mon Mar 22 08:08:11 1999
+++ linux/drivers/net/Config.in	Mon Jun  7 14:35:09 1999
@@ -109,6 +109,7 @@
   fi
   if [ "$CONFIG_MCA" = "y" ]; then
     tristate 'NE/2 (ne2000 MCA version) support' CONFIG_NE2_MCA
+    tristate 'SKnet MCA support' CONFIG_SKMC
   fi
   bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA
   if [ "$CONFIG_NET_EISA" = "y" ]; then
@@ -237,6 +238,12 @@
       bool '  WANPIPE X.25 support' CONFIG_WANPIPE_X25
       bool '  WANPIPE Frame Relay support' CONFIG_WANPIPE_FR
       bool '  WANPIPE PPP support' CONFIG_WANPIPE_PPP
+    fi
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      dep_tristate 'Cyclom 2X(tm) multiprotocol cards (EXPERIMENTAL)' CONFIG_CYCLADES_SYNC $CONFIG_WAN_DRIVERS
+      if [ "$CONFIG_CYCLADES_SYNC" != "n" ]; then
+        bool '  Cyclom 2X X.25 support (EXPERIMENTAL)' CONFIG_CYCLOMX_X25
+      fi
     fi
   fi
 fi
diff -u --recursive --new-file v2.3.5/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.3.5/linux/drivers/net/Makefile	Thu Apr 15 05:42:40 1999
+++ linux/drivers/net/Makefile	Mon Jun  7 14:35:09 1999
@@ -486,6 +486,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SKMC),y)
+L_OBJS += sk_mca.o
+else
+  ifeq ($(CONFIG_SKMC),m)
+  M_OBJS += sk_mca.o
+  endif
+endif
+
 ifeq ($(CONFIG_ELMC_II),y)
 L_OBJS += 3c527.o
 else
@@ -1048,6 +1056,23 @@
   endif
 endif
 
+ifeq ($(CONFIG_CYCLADES_SYNC),y)
+  LX_OBJS += cycx_drv.o
+  L_OBJS += cycx_main.o
+  ifeq ($(CONFIG_CYCLOMX_X25),y)
+    L_OBJS += cycx_x25.o
+  endif
+endif
+
+ifeq ($(CONFIG_CYCLADES_SYNC),m)
+  MX_OBJS += cycx_drv.o
+  M_OBJS += cyclomx.o
+  CYCLOMX_OBJS = cycx_main.o
+  ifeq ($(CONFIG_CYCLOMX_X25),y)
+    CYCLOMX_OBJS += cycx_x25.o
+  endif
+endif
+
 ifeq ($(CONFIG_X25_ASY),y)
 L_OBJS += x25_asy.o
 else
@@ -1084,6 +1109,9 @@
 
 wanpipe.o: $(WANPIPE_OBJS)
 	ld -r -o $@ $(WANPIPE_OBJS)
+
+cyclomx.o: $(CYCLOMX_OBJS)
+	ld -r -o $@ $(CYCLOMX_OBJS)
 
 rcpci.o: rcpci45.o rclanmtl.o
 	$(LD) -r -o rcpci.o rcpci45.o rclanmtl.o
diff -u --recursive --new-file v2.3.5/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.3.5/linux/drivers/net/Space.c	Mon May 31 22:28:05 1999
+++ linux/drivers/net/Space.c	Mon Jun  7 14:35:09 1999
@@ -69,6 +69,7 @@
 extern int wavelan_probe(struct device *);
 extern int el16_probe(struct device *);
 extern int elmc_probe(struct device *);
+extern int skmca_probe(struct device *);
 extern int elplus_probe(struct device *);
 extern int ac3200_probe(struct device *);
 extern int es_probe(struct device *);
@@ -266,6 +267,9 @@
 #endif
 #ifdef CONFIG_ELMC		/* 3c523 */
 	{elmc_probe, 0},
+#endif
+#ifdef CONFIG_SKMC              /* SKnet Microchannel */
+        {skmca_probe, 0},
 #endif
 	{NULL, 0},
 };
diff -u --recursive --new-file v2.3.5/linux/drivers/net/cosa.c linux/drivers/net/cosa.c
--- v2.3.5/linux/drivers/net/cosa.c	Mon May 31 22:28:05 1999
+++ linux/drivers/net/cosa.c	Mon Jun  7 16:18:58 1999
@@ -1,4 +1,4 @@
-/* $Id: cosa.c,v 1.21 1999/02/06 19:49:18 kas Exp $ */
+/* $Id: cosa.c,v 1.24 1999/05/28 17:28:34 kas Exp $ */
 
 /*
  *  Copyright (C) 1995-1997  Jan "Yenya" Kasprzak <kas@fi.muni.cz>
@@ -75,6 +75,10 @@
  * The Comtrol Hostess SV11 driver by Alan Cox
  * The Sync PPP/Cisco HDLC layer (syncppp.c) ported to Linux by Alan Cox
  */
+/*
+ *     5/25/1999 : Marcelo Tosatti <marcelo@conectiva.com.br>
+ *             fixed a deadlock in cosa_sppp_open
+ */
 
 /* ---------- Headers, macros, data structures ---------- */
 
@@ -1265,8 +1269,10 @@
 			debug_status_out(cosa, 0);
 #endif
 		}
+		cosa_putdata8(cosa, 0);
 		cosa_putdata8(cosa, status);
 #ifdef DEBUG_IO
+		debug_data_cmd(cosa, 0);
 		debug_data_cmd(cosa, status);
 #endif
 	}
@@ -1659,6 +1665,7 @@
 				printk(KERN_WARNING
 					"%s: No channel wants data in TX IRQ\n",
 					cosa->name);
+				put_driver_status_nolock(cosa);
 				clear_bit(TXBIT, &cosa->rxtx);
 				spin_unlock_irqrestore(&cosa->lock, flags);
 				return;
diff -u --recursive --new-file v2.3.5/linux/drivers/net/cs89x0.c linux/drivers/net/cs89x0.c
--- v2.3.5/linux/drivers/net/cs89x0.c	Wed Mar 10 16:51:35 1999
+++ linux/drivers/net/cs89x0.c	Mon Jun  7 16:18:58 1999
@@ -739,7 +739,7 @@
 		if (tickssofar < 5)
 			return 1;
 		if (net_debug > 0) printk("%s: transmit timed out, %s?\n", dev->name,
-			   tx_done(dev) ? "IRQ conflict" : "network cable problem");
+			   tx_done(dev) ? "IRQ conflict ?" : "network cable problem");
 		/* Try to restart the adaptor. */
 		dev->tbusy=0;
 		dev->trans_start = jiffies;
diff -u --recursive --new-file v2.3.5/linux/drivers/net/cycx_drv.c linux/drivers/net/cycx_drv.c
--- v2.3.5/linux/drivers/net/cycx_drv.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/cycx_drv.c	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,663 @@
+/*
+* cycx_drv.c	cycx Support Module.
+*
+*		This module is a library of common hardware-specific
+*		functions used by all Cyclades sync and some async (8x & 16x)
+*		drivers.
+*
+* Copyright:	(c) 1998, 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+*
+* Author:	Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+*
+* Based on sdladrv.c by Gene Kozin <genek@compuserve.com>
+*
+*		This program is free software; you can redistribute it and/or
+*		modify it under the terms of the GNU General Public License
+*		as published by the Free Software Foundation; either version
+*		2 of the License, or (at your option) any later version.
+* ============================================================================
+* 1999/05/28    acme		cycx_intack & cycx_intde gone for good
+* 1999/05/18	acme		lots of unlogged work, submitting to Linus...
+* 1999/01/03	acme		more judicious use of data types
+* 1999/01/03	acme		judicious use of data types :>
+*				cycx_inten trying to reset pending interrupts
+*				from cyclom 2x - I think this isn't the way to
+*				go, but for now...
+* 1999/01/02	acme		cycx_intack ok, I think there's nothing to do
+*				to ack an int in cycx_drv.c, only handle it in
+*				cyx_isr (or in the other protocols: cyp_isr,
+*				cyf_isr, when they get implemented.
+* Dec 31, 1998	Arnaldo         cycx_data_boot & cycx_code_boot fixed, crossing
+*				fingers to see x25_configure in cycx_x25.c
+*				work... :)
+* Dec 26, 1998	Arnaldo         load implementation fixed, seems to work! :)
+*				cycx_2x_dpmbase_options with all the possible
+*				DPM addresses (20).
+*				cycx_intr implemented (test this!)
+*				general code cleanup
+* Dec  8, 1998	Ivan Passos	Cyclom-2X firmware load implementation.
+* Aug  8, 1998	Arnaldo		Initial version.
+*/
+
+#include <linux/config.h>
+#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+#include <linux/module.h>
+#else
+#define EXPORT_SYMBOL(function)
+#endif
+#include <linux/kernel.h>	/* printk(), and other useful stuff */
+#include <linux/stddef.h>	/* offsetof(), etc. */
+#include <linux/errno.h>	/* return codes */
+#include <linux/sched.h>	/* for jiffies, HZ, etc. */
+#include <linux/cycx_drv.h>	/* API definitions */
+#include <linux/cycx_cfm.h>	/* CYCX firmware module definitions */
+#include <linux/delay.h>	/* udelay */
+#include <asm/io.h>		/* for inb(), outb(), etc. */
+
+#define	MOD_VERSION	0
+#define	MOD_RELEASE	1
+
+#ifdef MODULE
+MODULE_AUTHOR("Arnaldo Carvalho de Melo");
+MODULE_DESCRIPTION("Cyclades Sync Cards Driver.");
+#endif
+
+/* Function Prototypes */
+/* Module entry points. These are called by the OS and must be public. */
+int init_module (void);
+void cleanup_module (void);
+
+/* Hardware-specific functions */
+static int cycx_detect (cycxhw_t *hw);
+static int cycx_load (cycxhw_t *hw, cfm_t *cfm, u32 len);
+static int cycx_init (cycxhw_t *hw);
+static int cycx_reset (cycxhw_t *hw);
+static void cycx_bootcfg (cycxhw_t *hw);
+
+static int init_cycx_2x (cycxhw_t *hw);
+static int reset_cycx_2x (u32 addr);
+static int detect_cycx_2x (u32 addr);
+
+/* Miscellaneous functions */
+static void delay_cycx (int sec);
+static int get_option_index (u32 *optlist, u32 optval);
+static u16 checksum (u8 *buf, u32 len);
+
+#define wait_cyc(addr) cycx_exec(addr + CMD_OFFSET)
+
+/* Global Data
+ * Note: All data must be explicitly initialized!!! */
+
+/* private data */
+static char modname[] = "cycx_drv";
+static char fullname[] = "Cyclom X Support Module";
+static char copyright[]	= "(c) 1998, 1999 Arnaldo Carvalho de Melo";
+
+/* Hardware configuration options.
+ * These are arrays of configuration options used by verification routines.
+ * The first element of each array is its size (i.e. number of options).
+ */
+static u32 cycx_2x_dpmbase_options[] =
+{
+	20,
+	0xA0000, 0xA4000, 0xA8000, 0xAC000, 0xB0000, 0xB4000, 0xB8000,
+	0xBC000, 0xC0000, 0xC4000, 0xC8000, 0xCC000, 0xD0000, 0xD4000,
+	0xD8000, 0xDC000, 0xE0000, 0xE4000, 0xE8000, 0xEC000
+};
+
+static u32 cycx_2x_irq_options[]  = { 7, 3, 5, 9, 10, 11, 12, 15 };
+
+/* Kernel Loadable Module Entry Points */
+/* Module 'insert' entry point.
+ * o print announcement
+ * o initialize static data
+ *
+ * Return:	0	Ok
+ *		< 0	error.
+ * Context:	process */
+#ifdef MODULE
+int init_module (void)
+{
+	printk(KERN_INFO "%s v%u.%u %s\n",
+		fullname, MOD_VERSION, MOD_RELEASE, copyright);
+	printk(KERN_INFO "version=0x%X\n", LINUX_VERSION_CODE);
+	return 0;
+}
+/* Module 'remove' entry point.
+ * o release all remaining system resources */
+void cleanup_module (void)
+{
+}
+#endif
+/* Kernel APIs */
+/* Set up adapter.
+ * o detect adapter type
+ * o verify hardware configuration options
+ * o check for hardware conflicts
+ * o set up adapter shared memory
+ * o test adapter memory
+ * o load firmware
+ * Return:	0	ok.
+ *		< 0	error */
+EXPORT_SYMBOL(cycx_setup);
+int cycx_setup (cycxhw_t *hw, void *cfm, u32 len)
+{
+	u32 *irq_opt = NULL;	/* IRQ options */
+	u32 *dpmbase_opt = NULL;/* DPM window base options */
+	int err = 0;
+
+	if (cycx_detect(hw)) {
+		printk(KERN_ERR "%s: adapter Cyclom %uX not found at "
+				"address 0x%lX!\n",
+			modname, hw->type, (unsigned long) hw->dpmbase);
+		return -EINVAL;
+	}
+
+	printk(KERN_INFO "%s: found Cyclom %uX card at address 0x%lx.\n",
+			 modname, hw->type, (unsigned long) hw->dpmbase);
+
+	switch (hw->type) {
+		case CYCX_2X:
+			irq_opt	= cycx_2x_irq_options;
+			dpmbase_opt = cycx_2x_dpmbase_options;
+			break;
+		default:
+			printk(KERN_ERR "%s: unknown card.\n", modname);
+			return -EINVAL;
+	}
+
+	/* Verify IRQ configuration options */
+	if (!get_option_index(irq_opt, hw->irq)) {
+		printk (KERN_ERR "%s: IRQ %d is illegal!\n", modname, hw->irq);
+		return -EINVAL;
+	} 
+
+	/* Setup adapter dual-port memory window and test memory */
+	if (!hw->dpmbase) {
+		printk(KERN_ERR "%s: you must specify the dpm address!\n",
+				modname);
+ 		return -EINVAL;
+	}
+	else if (!get_option_index(dpmbase_opt, hw->dpmbase)) {
+		printk(KERN_ERR "%s: memory address 0x%lX is illegal!\n",
+				modname, (unsigned long) hw->dpmbase);
+		return -EINVAL;
+	}
+
+	hw->dpmsize = CYCX_WINDOWSIZE;
+	/* FIXME! Is this the only amount ever available? */
+	hw->memory = 0x40000;
+
+	cycx_init(hw);
+
+	printk(KERN_INFO "%s: dual-port memory window is set at 0x%lX.\n",
+			 modname, (unsigned long) hw->dpmbase);
+	printk(KERN_INFO "%s: found %luK bytes of on-board memory.\n",
+			 modname, (unsigned long) hw->memory / 1024);
+
+	/* Load firmware. If loader fails then shut down adapter */
+	err = cycx_load(hw, cfm, len);
+	if (err) cycx_down(hw);		/* shutdown adapter */
+	return err;
+} 
+
+/* Shut down CYCX: disable shared memory access and interrupts, stop CPU,etc.*/ 
+EXPORT_SYMBOL(cycx_down);
+int cycx_down (cycxhw_t *hw)
+{
+	return 0; /* FIXME: anything needed here? */
+}
+
+/* Enable interrupt generation.  */
+EXPORT_SYMBOL(cycx_inten);
+int cycx_inten (cycxhw_t *hw)
+{
+	switch (hw->type) {
+		case CYCX_2X: writeb (0, hw->dpmbase); break;
+		default: return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Generate an interrupt to adapter's CPU. */
+EXPORT_SYMBOL(cycx_intr);
+int cycx_intr (cycxhw_t *hw)
+{
+	switch (hw->type) {
+		case CYCX_2X:
+			writew(0, hw->dpmbase + GEN_CYCX_INTR);
+			return 0;
+		default: return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Execute Adapter Command.
+ * o Set exec flag.
+ * o Busy-wait until flag is reset. */
+EXPORT_SYMBOL(cycx_exec);
+int cycx_exec (u32 addr)
+{
+	u16 i = 0;
+	/* wait till addr content is zeroed */
+
+	while (readw(addr) != 0) {
+		udelay(1000);
+		if (++i > 50) return -1;
+	}
+
+	return 0;
+}
+
+/* Read absolute adapter memory.
+ * Transfer data from adapter's memory to data buffer. */
+EXPORT_SYMBOL(cycx_peek);
+int cycx_peek (cycxhw_t *hw, u32 addr, void *buf, u32 len)
+{
+	if (len == 1)	*(u8*)buf = readb (hw->dpmbase + addr);
+	else 		memcpy_fromio(buf, hw->dpmbase + addr, len);
+
+	return 0;
+}
+
+/* Write Absolute Adapter Memory.
+ * Transfer data from data buffer to adapter's memory. */
+EXPORT_SYMBOL(cycx_poke);
+int cycx_poke (cycxhw_t *hw, u32 addr, void *buf, u32 len)
+{
+	if (len == 1) writeb (*(u8*)buf, hw->dpmbase + addr);
+	else	      memcpy_toio(hw->dpmbase + addr, buf, len);
+
+	return 0;
+}
+
+/* Hardware-Specific Functions */
+/* Detect adapter type.
+ * o if adapter type is specified then call detection routine for that adapter
+ *   type.  Otherwise call detection routines for every adapter types until
+ *   adapter is detected.
+ *
+ * Notes:
+ * 1) Detection tests are destructive! Adapter will be left in shutdown state
+ *    after the test. */
+static int cycx_detect (cycxhw_t *hw)
+{
+	int err = 0;
+
+	if (!hw->dpmbase) return -EFAULT;
+
+	switch (hw->type) {
+		case CYCX_2X:
+			if (!detect_cycx_2x(hw->dpmbase)) err = -ENODEV;
+			break;
+		default:
+			if (detect_cycx_2x(hw->dpmbase)) hw->type = CYCX_2X;
+			else err = -ENODEV;
+	}
+
+	return err;
+}
+
+/* Load Aux Routines */
+/* Reset board hardware.
+   return 1 if memory exists at addr and 0 if not. */
+static int memory_exists(u32 addr)
+{
+	int timeout = 0;
+
+	for (; timeout < 3 ; timeout++) {
+		writew (TEST_PATTERN, addr + 0x10);
+
+		if (readw (addr + 0x10) == TEST_PATTERN)
+			if (readw (addr + 0x10) == TEST_PATTERN) return 1;
+
+		delay_cycx(1);
+	}
+
+	return 0;
+}
+
+/* Reset board hardware. */
+static int cycx_reset(cycxhw_t *hw)
+{
+	/* Reset board */
+	switch (hw->type) {
+		case CYCX_2X: return reset_cycx_2x(hw->dpmbase);
+	}
+
+	return -EINVAL;
+}
+
+/* Load reset code. */
+static void reset_load(u32 addr, u8 *buffer, u32 cnt)
+{
+	u32 pt_code = addr + RESET_OFFSET;
+	u16 i, j;
+
+	for ( i = 0 ; i < cnt ; i++) {
+		for (j = 0 ; j < 50 ; j++); /* Delay - FIXME busy waiting... */
+		writeb(*buffer++, pt_code++);
+	}
+}
+
+/* Load buffer using boot interface.
+ * o copy data from buffer to Cyclom-X memory
+ * o wait for reset code to copy it to right portion of memory */
+static int buffer_load(u32 addr, u8 *buffer, u32 cnt)
+{
+	memcpy_toio(addr + DATA_OFFSET, buffer, cnt);
+	writew(GEN_BOOT_DAT, addr + CMD_OFFSET);
+	return wait_cyc(addr);
+}
+
+/* Set up entry point and kick start Cyclom-X CPU. */
+static void cycx_start (u32 addr)
+{
+	/* put in 0x30 offset the jump instruction to the code entry point */
+	writeb(0xea, addr + 0x30);
+	writeb(0x00, addr + 0x31);
+	writeb(0xc4, addr + 0x32);
+	writeb(0x00, addr + 0x33);
+	writeb(0x00, addr + 0x34);
+
+	/* cmd to start executing code */
+	writew(GEN_START, addr + CMD_OFFSET);
+}         
+
+/* Load and boot reset code. */
+static void cycx_reset_boot(u32 addr, u8 *code, u32 len)
+{
+	u32 pt_start = addr + START_OFFSET;
+
+        writeb(0xea, pt_start++); /* jmp to f000:3f00 */
+        writeb(0x00, pt_start++);
+        writeb(0xfc, pt_start++);
+        writeb(0x00, pt_start++);
+        writeb(0xf0, pt_start);
+	reset_load(addr, code, len);
+
+	/* 80186 was in hold, go */
+	writeb(0, addr + START_CPU);
+	delay_cycx(1);
+}
+
+/* Load data.bin file through boot (reset) interface. */
+static int cycx_data_boot(u32 addr, u8 *code, u32 len)
+{
+	u32 pt_boot_cmd = addr + CMD_OFFSET;
+	u32 i;
+
+	/* boot buffer lenght */
+	writew(CFM_LOAD_BUFSZ, pt_boot_cmd + sizeof(u16));
+	writew(GEN_DEFPAR, pt_boot_cmd);
+
+	if (wait_cyc(addr) < 0) return 2;
+
+	writew(0, pt_boot_cmd + sizeof(u16));
+	writew(0x4000, pt_boot_cmd + 2 * sizeof(u16));
+	writew(GEN_SET_SEG, pt_boot_cmd);
+
+	if (wait_cyc(addr) < 0) return 2;
+
+	for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ)
+		if (buffer_load(addr, code + i,
+				MIN(CFM_LOAD_BUFSZ, (len - i))) < 0) {
+			printk(KERN_ERR "%s: Error !!\n", modname);
+			return 4;
+		}
+
+	return 0;
+}
+
+
+/* Load code.bin file through boot (reset) interface. */
+static int cycx_code_boot(u32 addr, u8 *code, u32 len)
+{
+	u32 pt_boot_cmd = addr + CMD_OFFSET;
+	u32 i;
+
+	/* boot buffer lenght */
+	writew(CFM_LOAD_BUFSZ, pt_boot_cmd + sizeof(u16));
+	writew(GEN_DEFPAR, pt_boot_cmd);
+
+	if (wait_cyc(addr) == -1) return 2;
+
+	writew(0x0000, pt_boot_cmd + sizeof(u16));
+	writew(0xc400, pt_boot_cmd + 2 * sizeof(u16));
+	writew(GEN_SET_SEG, pt_boot_cmd);
+
+	if (wait_cyc(addr) == -1) return 1;
+
+	for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ)
+		if (buffer_load(addr, code + i,MIN(CFM_LOAD_BUFSZ,(len - i)))) {
+			printk(KERN_ERR "%s: Error !!\n", modname);
+			return 4;
+		}
+
+	return 0;
+}
+
+/* Initialize CYCX hardware: setup memory window, IRQ, etc. */
+static int cycx_init (cycxhw_t *hw)
+{
+	switch (hw->type) {
+		case CYCX_2X: return init_cycx_2x(hw);
+	}
+
+	return -EINVAL;
+}
+
+/* Load adapter from the memory image of the CYCX firmware module. 
+ * o verify firmware integrity and compatibility
+ * o start adapter up */
+static int cycx_load (cycxhw_t *hw, cfm_t *cfm, u32 len)
+{
+	int i, j, status;
+	cycx_header_t *img_hdr;
+	u8 *reset_image,
+	   *data_image,
+	   *code_image;
+	u32 pt_cycld = hw->dpmbase + 0x400;
+	u16 cksum;
+
+	/* Announce */
+	printk(KERN_INFO "%s: firmware signature=\"%s\"\n",
+			 modname, cfm->signature); 
+
+	/* Verify firmware signature */
+	if (strcmp(cfm->signature, CFM_SIGNATURE)) {
+		printk(KERN_ERR "%s:cycx_load: not Cyclom-2X firmware!\n",
+				modname);
+		return -EINVAL;
+	}
+
+	printk(KERN_INFO "%s: firmware version=%u\n", modname, cfm->version);
+
+	/* Verify firmware module format version */
+	if (cfm->version != CFM_VERSION) {
+		printk(KERN_ERR "%s:cycx_load: firmware format %u rejected! "
+				"Expecting %u.\n",
+				modname, cfm->version, CFM_VERSION);
+		return -EINVAL;
+	}
+
+	/* Verify firmware module length and checksum */
+	cksum = checksum((u8*)&cfm->info, sizeof(cfm_info_t) +
+					      cfm->info.codesize);
+/*
+        FIXME cfm->info.codesize is off by 2
+	if (((len - sizeof(cfm_t) - 1) != cfm->info.codesize) ||
+*/
+	if (cksum != cfm->checksum) {
+		printk(KERN_ERR "%s:cycx_load: firmware corrupted!\n", modname);
+		printk(KERN_ERR " cdsize = 0x%x (expected 0x%lx)\n",
+                       len - sizeof(cfm_t) - 1, cfm->info.codesize);
+                printk(KERN_ERR " chksum = 0x%x (expected 0x%x)\n",
+                       cksum, cfm->checksum);
+		return -EINVAL;
+	}
+
+	/* If everything is ok, set reset, data and code pointers */
+
+	img_hdr = (cycx_header_t*)(((u8*) cfm) + sizeof(cfm_t) - 1);
+#ifdef FIRMWARE_DEBUG
+	printk(KERN_INFO "%s:cycx_load: image sizes\n", modname);
+	printk(KERN_INFO " reset=%lu\n", img_hdr->reset_size);
+	printk(KERN_INFO "  data=%lu\n", img_hdr->data_size);
+	printk(KERN_INFO "  code=%lu\n", img_hdr->code_size);
+#endif
+	reset_image = ((u8 *) img_hdr) + sizeof(cycx_header_t);
+	data_image = reset_image + img_hdr->reset_size;
+	code_image = data_image + img_hdr->data_size;
+
+	/*---- Start load ----*/
+        /* Announce */
+	printk(KERN_INFO "%s: loading firmware %s (ID=%u)...\n", modname,
+		(cfm->descr[0] != '\0') ? cfm->descr : "unknown firmware",
+		cfm->info.codeid);
+
+	for (i = 0 ; i < 5 ; i++) {
+		/* Reset Cyclom hardware */
+		if ((status = cycx_reset(hw)) != 0) {
+			printk(KERN_ERR "%s: dpm problem or board not "
+					"found (%d).\n", modname, status);
+			return -EINVAL;
+		}
+
+		/* Load reset.bin */
+                cycx_reset_boot(hw->dpmbase, reset_image, img_hdr->reset_size);
+		/* reset is waiting for boot */
+		writew(GEN_POWER_ON, pt_cycld);
+		delay_cycx(1);
+
+		for (j = 0 ; j < 3 ; j++)
+			if (!readw(pt_cycld)) goto reset_loaded;
+			else delay_cycx(1);
+	}
+
+	printk(KERN_ERR "%s: reset not started.\n", modname);
+	return -EINVAL;
+reset_loaded:
+	/* Load data.bin */
+	if((status = cycx_data_boot(hw->dpmbase, data_image, 
+		                    img_hdr->data_size)) != 0) {
+		printk(KERN_ERR "%s: cannot load data file (%d).\n",
+				modname, status);
+		return -EINVAL;
+	}
+
+	/* Load code.bin */
+	if((status = cycx_code_boot(hw->dpmbase, code_image, 
+		                    img_hdr->code_size)) != 0) {
+		printk(KERN_ERR "%s: cannot load code file (%d).\n",
+				modname, status);
+		return -EINVAL;
+	}
+
+	/* Prepare boot-time configuration data */
+	cycx_bootcfg(hw);
+
+	/* kick-off CPU */
+	cycx_start(hw->dpmbase);
+
+	/* Arthur Ganzert's tip: wait a while after the firmware loading...
+	   seg abr 26 17:17:12 EST 1999 - acme */
+	delay_cycx(7);
+	printk(KERN_INFO "%s: firmware loaded!\n", modname);
+
+	/* enable interrupts */
+        if (cycx_inten(hw)) {
+		printk(KERN_ERR "%s: adapter hardware failure!\n", modname);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/* Prepare boot-time firmware configuration data.
+ * o initialize configuration data area
+   From async.doc - V_3.4.0 - 07/18/1994
+   - As of now, only static buffers are available to the user.
+     So, the bit VD_RXDIRC must be set in 'valid'. That means that user
+     wants to use the static transmission and reception buffers. */
+static void cycx_bootcfg (cycxhw_t *hw)
+{
+	/* use fixed buffers */
+	writeb(FIXED_BUFFERS, hw->dpmbase + CONF_OFFSET); 
+}
+
+/* Initialize CYCX_2X adapter. */
+static int init_cycx_2x (cycxhw_t *hw)
+{
+	if (!detect_cycx_2x(hw->dpmbase)) return -ENODEV;
+	return 0;
+}
+
+/* Detect Cyclom 2x adapter.
+ *	Following tests are used to detect Cyclom 2x adapter:
+ *       to be completed based on the tests done below
+ *	Return 1 if detected o.k. or 0 if failed.
+ *	Note:	This test is destructive! Adapter will be left in shutdown
+ *		state after the test. */
+static int detect_cycx_2x (u32 addr)
+{
+	printk(KERN_INFO "%s: looking for a cyclom 2x at 0x%lX...\n",
+			 modname, (unsigned long) addr);
+
+	reset_cycx_2x(addr);
+	return memory_exists(addr);
+}
+
+/* Miscellaneous */
+/* Get option's index into the options list.
+ *	Return option's index (1 .. N) or zero if option is invalid. */
+static int get_option_index (u32 *optlist, u32 optval)
+{
+	int i = 1;
+	for (; i <= optlist[0]; ++i) if (optlist[i] == optval) return i;
+	return 0;
+}
+
+/* Reset adapter's CPU. */
+static int reset_cycx_2x (u32 addr)
+{
+	writeb (0, addr + RST_ENABLE);  delay_cycx (2);
+	writeb (0, addr + RST_DISABLE); delay_cycx (2);
+	return memory_exists(addr) ? 0 : 1;
+}
+
+/* Delay */
+static void delay_cycx (int sec)
+{
+/* acme
+   Thu dez 31 21:45:16 EDT 1998
+   FIXME I'll keep this comment here just in case, as of now I don't
+   know it all the contexts where this routine is used are interruptible... */
+
+	current->state = TASK_INTERRUPTIBLE;
+	current->counter = 0;       /* make us low-priority */
+	schedule_timeout(sec*HZ);
+}
+
+/* Calculate 16-bit CRC using CCITT polynomial. */
+static u16 checksum (u8 *buf, u32 len)
+{
+	u16 crc = 0;
+	u16 mask, flag;
+
+	for (; len; --len, ++buf)
+		for (mask = 0x80; mask; mask >>= 1) {
+			flag = (crc & 0x8000);
+			crc <<= 1;
+			crc |= ((*buf & mask) ? 1 : 0);
+			if (flag) crc ^= 0x1021;
+		}
+
+	return crc;
+}
+/* End */
diff -u --recursive --new-file v2.3.5/linux/drivers/net/cycx_main.c linux/drivers/net/cycx_main.c
--- v2.3.5/linux/drivers/net/cycx_main.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/cycx_main.c	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,377 @@
+/*
+* cycx_main.c	Cyclades Cyclom X Multiprotocol WAN Link Driver. Main module.
+*
+* Author:	Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+*
+* Copyright:	(c) 1998, 1999 Arnaldo Carvalho de Melo
+*
+* Based on sdlamain.c by Gene Kozin <genek@compuserve.com> &
+*			 Jaspreet Singh	<jaspreet@sangoma.com>
+*
+*		This program is free software; you can redistribute it and/or
+*		modify it under the terms of the GNU General Public License
+*		as published by the Free Software Foundation; either version
+*		2 of the License, or (at your option) any later version.
+* ============================================================================
+* 1999/05/19	acme		works directly linked into the kernel
+*				init_waitqueue_head for 2.3.* kernel
+* 1999/05/18	acme		major cleanup (polling not needed), etc
+* Aug 28, 1998	Arnaldo		minor cleanup (ioctls for firmware deleted)
+*				queue_task activated
+* Aug 08, 1998	Arnaldo		Initial version.
+*/
+
+#include <linux/config.h>	/* OS configuration options */
+#include <linux/stddef.h>	/* offsetof(), etc. */
+#include <linux/errno.h>	/* return codes */
+#include <linux/string.h>	/* inline memset(), etc. */
+#include <linux/malloc.h>	/* kmalloc(), kfree() */
+#include <linux/kernel.h>	/* printk(), and other useful stuff */
+#include <linux/module.h>	/* support for loadable modules */
+#include <linux/ioport.h>	/* request_region(), release_region() */
+#include <linux/tqueue.h>	/* for kernel task queues */
+#include <linux/wanrouter.h>	/* WAN router definitions */
+#include <linux/cyclomx.h>	/* cyclomx common user API definitions */
+#include <asm/uaccess.h>	/* kernel <-> user copy */
+#include <linux/init.h>         /* __initfunc (when not using as a module) */
+
+#ifdef MODULE
+MODULE_AUTHOR("Arnaldo Carvalho de Melo");
+MODULE_DESCRIPTION("Cyclades Sync Cards Driver.");
+#endif
+
+/* Defines & Macros */
+
+#define	DRV_VERSION	0		/* version number */
+#define	DRV_RELEASE	3		/* release (minor version) number */
+#define	MAX_CARDS	1		/* max number of adapters */
+
+#ifndef	CONFIG_CYCLOMX_CARDS		/* configurable option */
+#define	CONFIG_CYCLOMX_CARDS 1
+#endif
+
+/* Function Prototypes */
+
+/* Module entry points */
+int init_module (void);
+void cleanup_module (void);
+
+/* WAN link driver entry points */
+static int setup (wan_device_t *wandev, wandev_conf_t *conf);
+static int shutdown (wan_device_t *wandev);
+static int ioctl (wan_device_t *wandev, unsigned cmd, unsigned long arg);
+
+/* Miscellaneous functions */
+static void cycx_isr (int irq, void *dev_id, struct pt_regs *regs);
+
+/* Global Data
+ * Note: All data must be explicitly initialized!!!
+ */
+
+/* private data */
+static char drvname[]	= "cyclomx";
+static char fullname[]	= "CYCLOM X(tm) Multiprotocol Driver";
+static char copyright[]	= "(c) 1998, 1999 Arnaldo Carvalho de Melo";
+static int ncards = CONFIG_CYCLOMX_CARDS;
+static cycx_t *card_array = NULL;	/* adapter data space */
+
+/* Kernel Loadable Module Entry Points */
+
+/*
+ * Module 'insert' entry point.
+ * o print announcement
+ * o allocate adapter data space
+ * o initialize static data
+ * o register all cards with WAN router
+ * o calibrate CYCX shared memory access delay.
+ *
+ * Return:	0	Ok
+ *		< 0	error.
+ * Context:	process
+ */
+#ifdef MODULE
+int init_module (void)
+#else
+__initfunc(int cyclomx_init (void))
+#endif
+{
+	int cnt, err = 0;
+
+	printk(KERN_INFO "%s v%u.%u %s\n",
+		fullname, DRV_VERSION, DRV_RELEASE, copyright);
+
+	/* Verify number of cards and allocate adapter data space */
+	ncards = min(ncards, MAX_CARDS);
+	ncards = max(ncards, 1);
+	card_array = kmalloc(sizeof(cycx_t) * ncards, GFP_KERNEL);
+
+	if (card_array == NULL) return -ENOMEM;
+
+	memset(card_array, 0, sizeof(cycx_t) * ncards);
+
+	/* Register adapters with WAN router */
+	for (cnt = 0; cnt < ncards; ++cnt) {
+		cycx_t *card = &card_array[cnt];
+		wan_device_t *wandev = &card->wandev;
+
+		sprintf(card->devname, "%s%d", drvname, cnt + 1);
+		wandev->magic    = ROUTER_MAGIC;
+		wandev->name     = card->devname;
+		wandev->private  = card;
+		wandev->enable_tx_int = 0;
+		wandev->setup    = &setup;
+		wandev->shutdown = &shutdown;
+		wandev->ioctl    = &ioctl;
+		err = register_wan_device(wandev);
+
+		if (err) {
+			printk(KERN_ERR
+				"%s: %s registration failed with error %d!\n",
+				drvname, card->devname, err);
+			break;
+		}
+	}
+
+	if (cnt) ncards = cnt;	/* adjust actual number of cards */
+	else {
+		kfree(card_array);
+		err = -ENODEV;
+	}
+
+	return err;
+}
+
+/*
+ * Module 'remove' entry point.
+ * o unregister all adapters from the WAN router
+ * o release all remaining system resources
+ */
+#ifdef MODULE
+void cleanup_module (void)
+{
+	int i = 0;
+
+	for (; i < ncards; ++i) {
+		cycx_t *card = &card_array[i];
+		unregister_wan_device(card->devname);
+	}
+
+	kfree(card_array);
+}
+#endif
+/* WAN Device Driver Entry Points */
+/*
+ * Setup/confugure WAN link driver.
+ * o check adapter state
+ * o make sure firmware is present in configuration
+ * o allocate interrupt vector
+ * o setup CYCLOM X hardware
+ * o call appropriate routine to perform protocol-specific initialization
+ * o mark I/O region as used
+ *
+ * This function is called when router handles ROUTER_SETUP IOCTL. The
+ * configuration structure is in kernel memory (including extended data, if
+ * any).
+ */
+static int setup (wan_device_t *wandev, wandev_conf_t *conf)
+{
+	cycx_t *card;
+	int err = 0;
+	int irq;
+
+	/* Sanity checks */
+	if (!wandev || !wandev->private || !conf) return -EFAULT;
+
+	card = wandev->private;
+
+	if (wandev->state != WAN_UNCONFIGURED) return -EBUSY;
+
+	if (!conf->data_size || (conf->data == NULL)) {
+		printk(KERN_ERR "%s: firmware not found in configuration "
+				"data!\n", wandev->name);
+		return -EINVAL;
+	}
+
+	if (conf->irq <= 0) {
+		printk(KERN_ERR "%s: can't configure without IRQ!\n",
+				wandev->name);
+		return -EINVAL;
+	}
+
+	/* Allocate IRQ */
+	irq = conf->irq == 2 ? 9 : conf->irq;	/* IRQ2 -> IRQ9 */
+
+	if (request_irq(irq, cycx_isr, 0, wandev->name, card)) {
+		printk(KERN_ERR "%s: can't reserve IRQ %d!\n",
+				wandev->name, irq);
+		return -EINVAL;
+	}
+
+	/* Configure hardware, load firmware, etc. */
+	memset(&card->hw, 0, sizeof(cycxhw_t));
+	card->hw.irq = (conf->irq == 9) ? 2 : conf->irq;
+	card->hw.dpmbase = conf->maddr;
+	card->hw.dpmsize = CYCX_WINDOWSIZE;
+	card->hw.type = conf->hw_opt[0];
+	card->hw.fwid = CFID_X25_2X;
+	card->lock = SPIN_LOCK_UNLOCKED;
+#if LINUX_VERSION_CODE >= 0x020300
+	init_waitqueue_head(&card->wait_stats);
+#else
+	card->wait_stats = NULL;
+#endif
+	err = cycx_setup(&card->hw, conf->data, conf->data_size);
+
+	if (err) {
+		free_irq(irq, card);
+		return err;
+	}
+
+	/* Intialize WAN device data space */
+	wandev->irq       = irq;
+	wandev->dma       = wandev->ioport = 0;
+	wandev->maddr     = (unsigned long*)card->hw.dpmbase;
+	wandev->msize     = card->hw.dpmsize;
+	wandev->hw_opt[0] = card->hw.type;
+	wandev->hw_opt[1] = card->hw.pclk;
+	wandev->hw_opt[2] = card->hw.memory;
+	wandev->hw_opt[3] = card->hw.fwid;
+
+	/* Protocol-specific initialization */
+	switch (card->hw.fwid) {
+#ifdef	CONFIG_CYCLOMX_X25
+		case CFID_X25_2X: err = cyx_init(card, conf); break;
+#endif
+		default:
+			printk(KERN_ERR "%s: this firmware is not supported!\n",
+					wandev->name);
+			err = -EINVAL;
+	}
+
+	if (err) {
+		cycx_down(&card->hw);
+		free_irq(irq, card);
+		return err;
+	}
+
+	wandev->critical = 0;
+	return 0;
+}
+
+/*
+ * Shut down WAN link driver. 
+ * o shut down adapter hardware
+ * o release system resources.
+ *
+ * This function is called by the router when device is being unregistered or
+ * when it handles ROUTER_DOWN IOCTL.
+ */
+static int shutdown (wan_device_t *wandev)
+{
+	cycx_t *card;
+
+	/* sanity checks */
+	if (!wandev || !wandev->private) return -EFAULT;
+
+	if (wandev->state == WAN_UNCONFIGURED) return 0;
+
+	card = wandev->private;
+	wandev->state = WAN_UNCONFIGURED;
+	cycx_down(&card->hw);
+	printk(KERN_INFO "%s: irq %d being freed!\n", wandev->name,wandev->irq);
+	free_irq(wandev->irq, card);
+	wandev->critical = 0;
+	return 0;
+}
+
+/*
+ * Driver I/O control. 
+ * o verify arguments
+ * o perform requested action
+ *
+ * This function is called when router handles one of the reserved user
+ * IOCTLs.  Note that 'arg' stil points to user address space.
+ */
+static int ioctl (wan_device_t *wandev, unsigned cmd, unsigned long arg)
+{
+	return -EINVAL;
+}
+
+/* Miscellaneous */
+/*
+ * CYCX Interrupt Service Routine.
+ * o acknowledge CYCX hardware interrupt.
+ * o call protocol-specific interrupt service routine, if any.
+ */
+static void cycx_isr (int irq, void *dev_id, struct pt_regs *regs)
+{
+#define	card	((cycx_t*)dev_id)
+	if (!card || card->wandev.state == WAN_UNCONFIGURED) return;
+
+	if (card->in_isr) {
+		printk(KERN_WARNING "%s: interrupt re-entrancy on IRQ %d!\n",
+				    card->devname, card->wandev.irq);
+		return;
+	}
+
+	if (card->isr) card->isr(card);
+#undef	card
+}
+
+/*
+ * This routine is called by the protocol-specific modules when network
+ * interface is being open.  The only reason we need this, is because we
+ * have to call MOD_INC_USE_COUNT, but cannot include 'module.h' where it's
+ * defined more than once into the same kernel module.
+ */
+void cyclomx_open (cycx_t *card)
+{
+	++card->open_cnt;
+	MOD_INC_USE_COUNT;
+}
+
+/*
+ * This routine is called by the protocol-specific modules when network
+ * interface is being closed.  The only reason we need this, is because we
+ * have to call MOD_DEC_USE_COUNT, but cannot include 'module.h' where it's
+ * defined more than once into the same kernel module.
+ */
+void cyclomx_close (cycx_t *card)
+{
+	--card->open_cnt;
+	MOD_DEC_USE_COUNT;
+}
+
+/* Set WAN device state.  */
+void cyclomx_set_state (cycx_t *card, int state)
+{
+	unsigned long flags;
+
+	save_flags(flags); cli();
+
+	if (card->wandev.state != state) {
+		switch (state) {
+			case WAN_CONNECTED:
+				printk (KERN_INFO "%s: link connected!\n",
+						  card->devname);
+				break;
+
+			case WAN_CONNECTING:
+				printk (KERN_INFO "%s: link connecting...\n",
+						  card->devname);
+				break;
+
+			case WAN_DISCONNECTED:
+				printk (KERN_INFO "%s: link disconnected!\n",
+						  card->devname);
+				break;
+		}
+
+		card->wandev.state = state;
+	}
+
+	card->state_tick = jiffies;
+	restore_flags(flags);
+}
+
+/* End */
diff -u --recursive --new-file v2.3.5/linux/drivers/net/cycx_x25.c linux/drivers/net/cycx_x25.c
--- v2.3.5/linux/drivers/net/cycx_x25.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/cycx_x25.c	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,1538 @@
+/*
+* cycx_x25.c	CYCLOM X Multiprotocol WAN Link Driver.  X.25 module.
+*
+* Author:	Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+* Copyright:	(c) 1998, 1999 Arnaldo Carvalho de Melo
+*
+* Based on sdla_x25.c by Gene Kozin <genek@compuserve.com>
+*
+*		This program is free software; you can redistribute it and/or
+*		modify it under the terms of the GNU General Public License
+*		as published by the Free Software Foundation; either version
+*		2 of the License, or (at your option) any later version.
+* ============================================================================
+* 1999/05/28	acme		fixed nibble_to_byte, ackvc now properly treated
+*				if_send simplified
+* 1999/05/25	acme		fixed t1, t2, t21 & t23 configuration
+*				use spinlocks instead of cli/sti in some points
+* 1999/05/24	acme		finished the x25_get_stat function
+* 1999/05/23	acme		dev->type = ARPHRD_X25 (tcpdump only works,
+*				AFAIT, with ARPHRD_ETHER). This seems to be
+*				needed to use socket(AF_X25)...
+*				Now the config file must specify a peer media
+*				address for svc channes over a crossover cable.
+*				Removed hold_timeout from x25_channel_t,
+*				not used.
+*				A little enhancement in the DEBUG processing
+* 1999/05/22	acme		go to DISCONNECTED in disconnect_confirm_intr,
+*				instead of chan_disc.
+* 1999/05/16	marcelo		fixed timer initialization in SVCs
+* 1999/01/05	acme		x25_configure now get (most of) all
+*				parameters...
+* 1999/01/05	acme		pktlen now (correctly) uses log2 (value
+*				configured)
+* 1999/01/03	acme		judicious use of data types (u8, u16, u32, etc)
+* 1999/01/03	acme		cyx_isr: reset dpmbase to acknowledge
+*				indication (interrupt from cyclom 2x)
+* 1999/01/02	acme		cyx_isr: first hackings...
+* 1999/01/0203  acme 		when initializing an array don't give less
+*				elements than declared...
+* 				example: char send_cmd[6] = "?\xFF\x10";
+*          			you'll gonna lose a couple hours, 'cause your
+*				brain won't admit that there's an error in the
+*				above declaration...  the side effect is that
+*				memset is put into the unresolved symbols
+*				instead of using the inline memset functions...
+* 1999/01/02    acme 		began chan_connect, chan_send, x25_send
+* Dec 31, 1998	Arnaldo		x25_configure
+*				this code can be compiled as non module
+* Dec 27, 1998	Arnaldo		code cleanup
+*				IPX code wiped out! let's decrease code
+*				complexity for now, remember: I'm learning! :)
+*                               bps_to_speed_code OK
+* Dec 26, 1998	Arnaldo		Minimal debug code cleanup
+* Aug 08, 1998	Arnaldo		Initial version.
+*/
+#define CYCLOMX_X25_DEBUG 1
+
+#include <linux/version.h>
+#include <linux/kernel.h>	/* printk(), and other useful stuff */
+#include <linux/stddef.h>	/* offsetof(), etc. */
+#include <linux/errno.h>	/* return codes */
+#include <linux/string.h>	/* inline memset(), etc. */
+#include <linux/malloc.h>	/* kmalloc(), kfree() */
+#include <linux/wanrouter.h>	/* WAN router definitions */
+#include <asm/byteorder.h>	/* htons(), etc. */
+#include <linux/if_arp.h>       /* ARPHRD_X25 */
+#include <linux/cyclomx.h>	/* CYCLOM X common user API definitions */
+#include <linux/cycx_x25.h>	/* X.25 firmware API definitions */
+
+/* Defines & Macros */
+#define MAX_CMD_RETRY	5
+#define X25_CHAN_MTU	2048	/* unfragmented logical channel MTU */
+#define OUT_INTR	1
+#define IN_INTR		0
+
+/* Data Structures */
+/* This is an extention of the 'struct device' we create for each network
+   interface to keep the rest of X.25 channel-specific data. */
+typedef struct x25_channel {
+	char name[WAN_IFNAME_SZ+1];	/* interface name, ASCIIZ */
+	char addr[WAN_ADDRESS_SZ+1];	/* media address, ASCIIZ */
+	char *local_addr;		/* local media address, ASCIIZ -
+					   svc thru crossover cable */
+	s16 lcn;			/* logical channel number/conn.req.key*/
+	u8 link;
+	struct timer_list timer;	/* timer used for svc channel disc. */
+	spinlock_t lock;
+	u16 protocol;			/* ethertype, 0 - multiplexed */
+	u8 svc;				/* 0 - permanent, 1 - switched */
+	u8 state;			/* channel state */
+	u8 drop_sequence;		/* mark sequence for dropping */
+	u32 idle_tmout;			/* sec, before disconnecting */
+	struct sk_buff *rx_skb;		/* receive socket buffer */
+	cycx_t *card;			/* -> owner */
+	struct enet_statistics ifstats;	/* interface statistics */
+} x25_channel_t;
+
+/* Function Prototypes */
+/* WAN link driver entry points. These are called by the WAN router module. */
+static int update (wan_device_t *wandev),
+	   new_if (wan_device_t *wandev, struct device *dev,wanif_conf_t *conf),
+	   del_if (wan_device_t *wandev, struct device *dev);
+
+/* Network device interface */
+static int if_init (struct device *dev),
+	   if_open (struct device *dev),
+	   if_close (struct device *dev),
+	   if_header (struct sk_buff *skb, struct device *dev,
+		      u16 type, void *daddr, void *saddr, unsigned len),
+	   if_rebuild_hdr (struct sk_buff *skb),
+	   if_send (struct sk_buff *skb, struct device *dev);
+
+static struct net_device_stats * if_stats (struct device *dev);
+
+/* Interrupt handlers */
+static void cyx_isr (cycx_t *card),
+	    tx_intr (cycx_t *card, TX25Cmd *cmd),
+	    rx_intr (cycx_t *card, TX25Cmd *cmd),
+	    log_intr (cycx_t *card, TX25Cmd *cmd),
+	    stat_intr (cycx_t *card, TX25Cmd *cmd),
+	    connect_confirm_intr (cycx_t *card, TX25Cmd *cmd),
+	    disconnect_confirm_intr (cycx_t *card, TX25Cmd *cmd),
+	    connect_intr (cycx_t *card, TX25Cmd *cmd),
+	    disconnect_intr (cycx_t *card, TX25Cmd *cmd),
+	    spur_intr (cycx_t *card, TX25Cmd *cmd);
+
+/* X.25 firmware interface functions */
+static int x25_configure (cycx_t *card, TX25Config *conf),
+	   x25_get_stats (cycx_t *card),
+	   x25_send (cycx_t *card, u8 link, u8 lcn, u8 bitm, int len,void *buf),
+	   x25_connect_response (cycx_t *card, x25_channel_t *chan),
+	   x25_disconnect_response (cycx_t *card, u8 link, u8 lcn);
+
+/* Miscellaneous functions */
+static int chan_connect (struct device *dev),
+	   chan_send (struct device *dev, struct sk_buff *skb);
+
+static void set_chan_state (struct device *dev, u8 state, u8 outside_intr),
+	    nibble_to_byte (u8 *s, u8 *d, u8 len, u8 nibble),
+	    reset_timer (struct device *dev),
+	    chan_disc (struct device *dev),
+	    chan_timer (unsigned long data);
+
+static u8 bps_to_speed_code (u32 bps);
+static u8 log2 (u32 n);
+
+static unsigned dec_to_uint (u8 *str, int len);
+
+static struct device *get_dev_by_lcn (wan_device_t *wandev, s16 lcn);
+static struct device *get_dev_by_dte_addr (wan_device_t *wandev, char *dte);
+
+#ifdef CYCLOMX_X25_DEBUG
+static void hex_dump(char *msg, unsigned char *p, int len);
+static void x25_dump_config(TX25Config *conf);
+static void x25_dump_stats(TX25Stats *stats);
+static void x25_dump_devs(wan_device_t *wandev);
+#define dprintk(format, a...) printk(format, ##a)
+#else
+#define hex_dump(msg, p, len)
+#define x25_dump_config(conf)
+#define x25_dump_stats(stats)
+#define x25_dump_devs(wandev)
+#define dprintk(format, a...)
+#endif
+/* Public Functions */
+
+/* X.25 Protocol Initialization routine.
+ *
+ * This routine is called by the main CYCLOM X module during setup.  At this
+ * point adapter is completely initialized and X.25 firmware is running.
+ *  o read firmware version (to make sure it's alive)
+ *  o configure adapter
+ *  o initialize protocol-specific fields of the adapter data space.
+ *
+ * Return:	0	o.k.
+ *		< 0	failure.  */
+int cyx_init (cycx_t *card, wandev_conf_t *conf)
+{
+	TX25Config cfg;
+
+	/* Verify configuration ID */
+	if (conf->config_id != WANCONFIG_X25) {
+		printk(KERN_INFO "%s: invalid configuration ID %u!\n",
+				 card->devname, conf->config_id);
+		return -EINVAL;
+	}
+
+	/* Initialize protocol-specific fields */
+	card->mbox  = card->hw.dpmbase + X25_MBOX_OFFS;
+	card->u.x.critical = 0; /* critical section flag */
+	card->u.x.connection_keys = 0;
+
+	/* Configure adapter. Here we set resonable defaults, then parse
+	 * device configuration structure and set configuration options.
+	 * Most configuration options are verified and corrected (if
+	 * necessary) since we can't rely on the adapter to do so and don't
+	 * want it to fail either. */
+	memset(&cfg, 0, sizeof(cfg));
+	cfg.link = 0;
+	cfg.clock = conf->clocking == WANOPT_EXTERNAL ? 8 : 55;
+	cfg.speed = bps_to_speed_code(conf->bps);
+	cfg.n3win = 7;
+	cfg.n2win = 2;
+	cfg.n2 = 5;
+	cfg.nvc = 1;
+	cfg.npvc = 1;
+	cfg.flags = 0x02; /* default = V35 */
+	cfg.t1 = 10;   /* line carrier timeout */
+	cfg.t2 = 29;   /* tx timeout */
+	cfg.t21 = 180; /* CALL timeout */
+	cfg.t23 = 180; /* CLEAR timeout */
+
+	/* adjust MTU */
+	if (!conf->mtu || conf->mtu >= 512)
+		card->wandev.mtu = 512;
+	else if (conf->mtu >= 256)
+		card->wandev.mtu = 256;
+	else if (conf->mtu >= 128)
+		card->wandev.mtu = 128;
+	else
+		card->wandev.mtu = 64;
+
+	cfg.pktlen = log2(card->wandev.mtu);
+
+	if (conf->station == WANOPT_DTE) {
+		cfg.locaddr = 3; /* DTE */
+		cfg.remaddr = 1; /* DCE */
+	} else {
+		cfg.locaddr = 1; /* DCE */
+		cfg.remaddr = 3; /* DTE */
+	}
+
+        if (conf->interface == WANOPT_RS232)
+	        cfg.flags = 0;      /* FIXME just reset the 2nd bit */
+
+	if (conf->u.x25.hi_pvc) {
+		card->u.x.hi_pvc = min(conf->u.x25.hi_pvc, 4095);
+		card->u.x.lo_pvc = min(conf->u.x25.lo_pvc, card->u.x.hi_pvc);
+	}
+
+	if (conf->u.x25.hi_svc) {
+		card->u.x.hi_svc = min(conf->u.x25.hi_svc, 4095);
+		card->u.x.lo_svc = min(conf->u.x25.lo_svc, card->u.x.hi_svc);
+	}
+
+	if (card->u.x.lo_pvc == 255)
+		cfg.npvc = 0;
+	else
+		cfg.npvc = card->u.x.hi_pvc - card->u.x.lo_pvc + 1;
+
+	cfg.nvc = card->u.x.hi_svc - card->u.x.lo_svc + 1 + cfg.npvc;
+
+	if (conf->u.x25.hdlc_window)
+		cfg.n2win = min(conf->u.x25.hdlc_window, 7);
+
+	if (conf->u.x25.pkt_window)
+		cfg.n3win = min(conf->u.x25.pkt_window, 7);
+
+	if (conf->u.x25.t1)
+		cfg.t1 = min(conf->u.x25.t1, 30);
+
+	if (conf->u.x25.t2)
+		cfg.t2 = min(conf->u.x25.t2, 30);
+
+	if (conf->u.x25.t11_t21)
+		cfg.t21 = min(conf->u.x25.t11_t21, 30);
+
+	if (conf->u.x25.t13_t23)
+		cfg.t23 = min(conf->u.x25.t13_t23, 30);
+
+	if (conf->u.x25.n2)
+		cfg.n2 = min(conf->u.x25.n2, 30);
+
+	/* initialize adapter */
+	if (x25_configure(card, &cfg))
+		return -EIO;
+
+	/* Initialize protocol-specific fields of adapter data space */
+	card->wandev.bps	= conf->bps;
+	card->wandev.interface	= conf->interface;
+	card->wandev.clocking	= conf->clocking;
+	card->wandev.station	= conf->station;
+	card->isr		= &cyx_isr;
+	card->exec		= NULL;
+	card->wandev.update	= &update;
+	card->wandev.new_if	= &new_if;
+	card->wandev.del_if	= &del_if;
+	card->wandev.state	= WAN_DISCONNECTED;
+	card->wandev.enable_tx_int = card->irq_dis_if_send_count = 0;
+	return 0;
+}
+
+/* WAN Device Driver Entry Points */
+/* Update device status & statistics. */
+static int update (wan_device_t *wandev)
+{
+	/* sanity checks */
+	if (!wandev || !wandev->private)
+		return -EFAULT;
+
+	if (wandev->state == WAN_UNCONFIGURED)
+		return -ENODEV;
+
+	x25_get_stats(wandev->private);
+	return 0;
+}
+
+/* Create new logical channel.
+ * This routine is called by the router when ROUTER_IFNEW IOCTL is being
+ * handled.
+ * o parse media- and hardware-specific configuration
+ * o make sure that a new channel can be created
+ * o allocate resources, if necessary
+ * o prepare network device structure for registaration.
+ *
+ * Return:	0	o.k.
+ *		< 0	failure (channel will not be created) */
+static int new_if (wan_device_t *wandev, struct device *dev, wanif_conf_t *conf)
+{
+	cycx_t *card = wandev->private;
+	x25_channel_t *chan;
+	int err = 0;
+
+	if (conf->name[0] == '\0' || strlen(conf->name) > WAN_IFNAME_SZ) {
+		printk(KERN_INFO "%s: invalid interface name!\n",card->devname);
+		return -EINVAL;
+	}
+
+	/* allocate and initialize private data */
+	if ((chan = kmalloc(sizeof(x25_channel_t), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	memset(chan, 0, sizeof(x25_channel_t));
+	strcpy(chan->name, conf->name);
+	chan->card = card;
+	chan->link = conf->port;
+	chan->protocol = ETH_P_IP;
+	chan->rx_skb = NULL;
+	/* only used in svc connected thru crossover cable */
+	chan->local_addr = NULL;
+	chan->lock = SPIN_LOCK_UNLOCKED;
+
+	if (conf->addr[0] == '@') {	/* SVC */
+		int local_len = strlen(conf->local_addr);
+
+		if (local_len) {
+			if (local_len > WAN_ADDRESS_SZ) {
+				printk(KERN_ERR "%s: %s local addr too long!\n",
+						wandev->name, chan->name);
+				kfree(chan);
+				return -EINVAL;
+			} else if ((chan->local_addr = kmalloc(local_len + 1,
+							 GFP_KERNEL)) == NULL) {
+				kfree(chan);
+				return ENOMEM;
+			}
+
+                	strncpy(chan->local_addr, conf->local_addr,
+				WAN_ADDRESS_SZ);
+		}
+
+                chan->svc = 1;
+                strncpy(chan->addr, &conf->addr[1], WAN_ADDRESS_SZ);
+		init_timer(&chan->timer);
+		chan->timer.function = chan_timer;
+		chan->timer.data = (unsigned long) dev;
+
+                /* Set channel timeouts (default if not specified) */
+                chan->idle_tmout = conf->idle_timeout ? conf->idle_timeout : 90;
+        } else if (is_digit(conf->addr[0])) {	/* PVC */
+		s16 lcn = dec_to_uint(conf->addr, 0);
+
+		if (lcn >= card->u.x.lo_pvc && lcn <= card->u.x.hi_pvc)
+			chan->lcn = lcn;
+		else {
+			printk(KERN_ERR
+				"%s: PVC %u is out of range on interface %s!\n",
+				wandev->name, lcn, chan->name);
+			err = -EINVAL;
+		}
+	} else {
+		printk(KERN_ERR "%s: invalid media address on interface %s!\n",
+				wandev->name, chan->name);
+		err = -EINVAL;
+	}
+
+	if (err) {
+		if (chan->local_addr)
+			kfree(chan->local_addr);
+		kfree(chan);
+		return err;
+	}
+
+	/* prepare network device data space for registration */
+	dev->name = chan->name;
+	dev->init = &if_init;
+	dev->priv = chan;
+	return 0;
+}
+
+/* Delete logical channel. */
+static int del_if (wan_device_t *wandev, struct device *dev)
+{
+	if (!dev) {
+		printk(KERN_ERR "cycx_x25:del_if:dev == NULL!\n");
+		return 0;
+	}
+
+	if (dev->priv) {
+		x25_channel_t *chan = dev->priv;
+		if (chan->svc) {
+			if (chan->local_addr)
+				kfree(chan->local_addr);
+
+			if (chan->state == WAN_CONNECTED)
+				del_timer(&chan->timer);
+		}
+		kfree(chan);
+		dev->priv = NULL;
+	}
+
+	return 0;
+}
+
+/* Network Device Interface */
+/* Initialize Linux network interface.
+ *
+ * This routine is called only once for each interface, during Linux network
+ * interface registration.  Returning anything but zero will fail interface
+ * registration. */
+static int if_init (struct device *dev)
+{
+	x25_channel_t *chan = dev->priv;
+	cycx_t *card = chan->card;
+	wan_device_t *wandev = &card->wandev;
+
+	/* Initialize device driver entry points */
+	dev->open = &if_open;
+	dev->stop = &if_close;
+	dev->hard_header = &if_header;
+	dev->rebuild_header = &if_rebuild_hdr;
+	dev->hard_start_xmit = &if_send;
+	dev->get_stats = &if_stats;
+
+	/* Initialize media-specific parameters */
+	dev->mtu = X25_CHAN_MTU;
+	dev->type = ARPHRD_X25;		/* ARP h/w type */
+	dev->hard_header_len = 0;	/* media header length */
+	dev->addr_len = 0;		/* hardware address length */
+
+	if (!chan->svc)
+		*(u16*)dev->dev_addr = htons(chan->lcn);
+
+	/* Initialize hardware parameters (just for reference) */
+	dev->irq = wandev->irq;
+	dev->dma = wandev->dma;
+	dev->base_addr = wandev->ioport;
+	dev->mem_start = (unsigned long)wandev->maddr;
+	dev->mem_end = (unsigned long)(wandev->maddr + wandev->msize - 1);
+	dev->flags |= IFF_NOARP;
+
+        /* Set transmit buffer queue length */
+        dev->tx_queue_len = 10;
+
+	/* Initialize socket buffers */
+	dev_init_buffers(dev);
+	set_chan_state(dev, WAN_DISCONNECTED, OUT_INTR);
+	return 0;
+}
+
+/* Open network interface.
+ * o prevent module from unloading by incrementing use count
+ * o if link is disconnected then initiate connection
+ *
+ * Return 0 if O.k. or errno.  */
+static int if_open (struct device *dev)
+{
+	x25_channel_t *chan = dev->priv;
+	cycx_t *card = chan->card;
+
+	if (dev->start)
+		return -EBUSY; /* only one open is allowed */ 
+
+	if (test_and_set_bit(0, (void*)&card->wandev.critical))
+		return -EAGAIN;
+
+	dev->interrupt = 0;
+	dev->tbusy = 0;
+	dev->start = 1;
+	cyclomx_open(card);
+
+	card->wandev.critical = 0;
+	return 0;
+}
+
+/* Close network interface.
+ * o reset flags.
+ * o if there's no more open channels then disconnect physical link. */
+static int if_close (struct device *dev)
+{
+	x25_channel_t *chan = dev->priv;
+	cycx_t *card = chan->card;
+
+	if (test_and_set_bit(0, (void*)&card->wandev.critical))
+		return -EAGAIN;
+
+	dev->start = 0;
+
+	if (chan->state == WAN_CONNECTED || chan->state == WAN_CONNECTING)
+		chan_disc(dev);
+		
+	cyclomx_close(card);
+
+	card->wandev.critical = 0;
+	return 0;
+}
+
+/* Build media header.
+ * o encapsulate packet according to encapsulation type.
+ *
+ * The trick here is to put packet type (Ethertype) into 'protocol' field of
+ * the socket buffer, so that we don't forget it.  If encapsulation fails,
+ * set skb->protocol to 0 and discard packet later.
+ *
+ * Return:	media header length. */
+static int if_header (struct sk_buff *skb, struct device *dev,
+		      u16 type, void *daddr, void *saddr, unsigned len)
+{
+	skb->protocol = type;
+	return dev->hard_header_len;
+}
+
+/* * Re-build media header.
+ * Return:	1	physical address resolved.
+ *		0	physical address not resolved */
+static int if_rebuild_hdr (struct sk_buff *skb)
+{
+	return 1;
+}
+
+/* Send a packet on a network interface.
+ * o set tbusy flag (marks start of the transmission).
+ * o check link state. If link is not up, then drop the packet.
+ * o check channel status. If it's down then initiate a call.
+ * o pass a packet to corresponding WAN device.
+ * o free socket buffer
+ *
+ * Return:	0	complete (socket buffer must be freed)
+ *		non-0	packet may be re-transmitted (tbusy must be set)
+ *
+ * Notes:
+ * 1. This routine is called either by the protocol stack or by the "net
+ *    bottom half" (with interrupts enabled).
+ * 2. Setting tbusy flag will inhibit further transmit requests from the
+ *    protocol stack and can be used for flow control with protocol layer. */
+static int if_send (struct sk_buff *skb, struct device *dev)
+{
+	x25_channel_t *chan = dev->priv;
+	cycx_t *card = chan->card;
+
+	if (dev->tbusy) {
+		++chan->ifstats.rx_dropped;	
+		return -EBUSY;	
+	}
+
+	dev->tbusy = 1;
+
+	reset_timer(dev);
+
+	if (!chan->svc)
+		chan->protocol = skb->protocol;
+
+	if (card->wandev.state != WAN_CONNECTED)
+		++chan->ifstats.tx_dropped;
+        else if (chan->svc && chan->protocol &&
+		 chan->protocol != skb->protocol) {
+                printk(KERN_INFO
+                        "%s: unsupported Ethertype 0x%04X on interface %s!\n",
+                        card->devname, skb->protocol, dev->name);
+                ++chan->ifstats.tx_errors;
+        } else switch (chan->state) {
+		case WAN_DISCONNECTED:
+			if (chan_connect(dev))
+				return -EBUSY;
+			/* fall thru */
+		case WAN_CONNECTED:
+			dev->trans_start = jiffies;
+			if (chan_send(dev, skb)) {
+				dev->tbusy = 1;
+				return -EBUSY;
+			}
+			break;
+		default:
+			++chan->ifstats.tx_dropped;	
+			++card->wandev.stats.tx_dropped;
+	}
+
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+/* Get Ethernet-style interface statistics.
+ * Return a pointer to struct net_device_stats */
+static struct net_device_stats *if_stats (struct device *dev)
+{
+        x25_channel_t *chan = dev->priv;
+
+	return chan ? &chan->ifstats : NULL;
+}
+
+/* Interrupt Handlers */
+/* X.25 Interrupt Service Routine. */
+static void cyx_isr (cycx_t *card)
+{
+	unsigned long host_cpu_flags;
+	TX25Cmd cmd;
+	u16 z = 0;
+
+	card->in_isr = 1;
+	card->buff_int_mode_unbusy = 0;
+
+	if (test_and_set_bit(0, (void*)&card->wandev.critical)) {
+ 		printk(KERN_INFO "cyx_isr: %s, wandev.critical set to 0x%02X\n",
+				 card->devname, card->wandev.critical);
+		card->in_isr = 0;
+		return;
+	}
+
+	/* For all interrupts set the critical flag to CRITICAL_RX_INTR.
+         * If the if_send routine is called with this flag set it will set
+         * the enable transmit flag to 1. (for a delayed interrupt) */
+	card->wandev.critical = CRITICAL_IN_ISR;
+	cycx_peek(&card->hw, X25_RXMBOX_OFFS, &cmd, sizeof(cmd));
+	switch (cmd.command) {
+		case X25_DATA_INDICATION:
+			rx_intr(card, &cmd);
+			break;
+		case X25_ACK_FROM_VC:
+			tx_intr(card, &cmd);
+			break;
+		case X25_LOG: 
+			log_intr(card, &cmd);
+			break;
+		case X25_STATISTIC: 
+			stat_intr(card, &cmd);
+			break;
+		case X25_CONNECT_CONFIRM:
+			connect_confirm_intr(card, &cmd);
+			break;
+		case X25_CONNECT_INDICATION:
+			connect_intr(card, &cmd);
+			break;
+		case X25_DISCONNECT_INDICATION:
+			disconnect_intr(card, &cmd);
+			break;
+		case X25_DISCONNECT_CONFIRM:
+			disconnect_confirm_intr(card, &cmd);
+			break;
+		case X25_LINE_ON:
+			cyclomx_set_state(card, WAN_CONNECTED);
+			break;
+		case X25_LINE_OFF:
+			cyclomx_set_state(card, WAN_DISCONNECTED);
+			break;
+		default: 
+			spur_intr(card, &cmd); /* unwanted interrupt */
+	}
+
+	cycx_poke(&card->hw, 0, &z, sizeof(z));
+	cycx_poke(&card->hw, X25_RXMBOX_OFFS, &z, sizeof(z));
+ 
+	card->wandev.critical = CRITICAL_INTR_HANDLED;
+
+	if (card->wandev.enable_tx_int)
+		card->wandev.enable_tx_int = 0;
+
+	spin_lock_irqsave(&card->lock, host_cpu_flags);
+	card->in_isr = 0;
+	card->wandev.critical = 0;
+	spin_unlock_irqrestore(&card->lock, host_cpu_flags);
+
+	if (card->buff_int_mode_unbusy)
+		mark_bh(NET_BH);
+}
+
+/* Transmit interrupt handler.
+ *	o Release socket buffer
+ *	o Clear 'tbusy' flag */
+static void tx_intr (cycx_t *card, TX25Cmd *cmd)
+{
+	struct device *dev;
+	wan_device_t *wandev = &card->wandev;
+	u8 lcn;
+
+	cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn));
+
+	/* unbusy device and then dev_tint(); */
+	if ((dev = get_dev_by_lcn (wandev, lcn)) != NULL) {
+		card->buff_int_mode_unbusy = 1;
+		dev->tbusy = 0;
+	} else
+		printk(KERN_ERR "%s:ackvc for inexistent lcn %d\n",
+				 card->devname, lcn);
+}
+
+/* Receive interrupt handler.
+ * This routine handles fragmented IP packets using M-bit according to the
+ * RFC1356.
+ * o map ligical channel number to network interface.
+ * o allocate socket buffer or append received packet to the existing one.
+ * o if M-bit is reset (i.e. it's the last packet in a sequence) then 
+ *   decapsulate packet and pass socket buffer to the protocol stack.
+ *
+ * Notes:
+ * 1. When allocating a socket buffer, if M-bit is set then more data is
+ *    comming and we have to allocate buffer for the maximum IP packet size
+ *    expected on this channel.
+ * 2. If something goes wrong and X.25 packet has to be dropped (e.g. no
+ *    socket buffers available) the whole packet sequence must be discarded. */
+static void rx_intr (cycx_t *card, TX25Cmd *cmd)
+{
+	wan_device_t *wandev = &card->wandev;
+	struct device *dev;
+	x25_channel_t *chan;
+	struct sk_buff *skb;
+	u8 bitm, lcn;
+	int pktlen = cmd->len - 5;
+
+	cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn));
+	cycx_peek(&card->hw, cmd->buf + 4, &bitm, sizeof(bitm));
+	bitm &= 0x10;
+
+	if ((dev = get_dev_by_lcn(wandev, lcn)) == NULL) {
+		/* Invalid channel, discard packet */
+		printk(KERN_INFO "%s: receiving on orphaned LCN %d!\n",
+				 card->devname, lcn);
+		return;
+	}
+
+	chan = dev->priv;
+	reset_timer(dev);
+
+	if (chan->drop_sequence)
+		if (!bitm)
+			chan->drop_sequence = 0;
+		else
+			return;
+
+	if ((skb = chan->rx_skb) == NULL) {
+		/* Allocate new socket buffer */
+		int bufsize = bitm ? dev->mtu : pktlen;
+
+		if ((skb = dev_alloc_skb(bufsize +
+					 dev->hard_header_len)) == NULL) {
+			printk(KERN_INFO "%s: no socket buffers available!\n",
+					 card->devname);
+			chan->drop_sequence = 1;
+			++chan->ifstats.rx_dropped;
+			return;
+		}
+
+		skb->dev = dev;
+		skb->protocol = htons(chan->protocol);
+		chan->rx_skb = skb;
+	}
+
+	if (skb_tailroom(skb) < pktlen) {
+		/* No room for the packet. Call off the whole thing! */
+		dev_kfree_skb(skb);
+		chan->rx_skb = NULL;
+
+		if (bitm)
+			chan->drop_sequence = 1;
+
+		printk(KERN_INFO "%s: unexpectedly long packet sequence "
+			"on interface %s!\n", card->devname, dev->name);
+		++chan->ifstats.rx_length_errors;
+		return;
+	}
+
+	/* Append packet to the socket buffer  */
+	cycx_peek(&card->hw, cmd->buf + 5, skb_put(skb, pktlen), pktlen);
+
+	if (bitm)
+		return; /* more data is coming */
+
+	dev->last_rx = jiffies;		/* timestamp */
+	chan->rx_skb = NULL;		/* dequeue packet */
+
+	skb->protocol = htons(ETH_P_IP);
+	skb->dev = dev;
+	skb->mac.raw = skb->data;
+	netif_rx(skb);
+	++chan->ifstats.rx_packets;
+	chan->ifstats.rx_bytes += skb->len;
+}
+
+/* Connect interrupt handler. */
+static void connect_intr (cycx_t *card, TX25Cmd *cmd)
+{
+	wan_device_t *wandev = &card->wandev;
+	struct device *dev = NULL;
+	x25_channel_t *chan;
+	u8 data[32],
+	   local[24],
+	   rem[24];
+	u8 lcn, sizelocal, sizerem;
+
+	cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn));
+	cycx_peek(&card->hw, cmd->buf + 5, &sizelocal, sizeof(sizelocal));
+	cycx_peek(&card->hw, cmd->buf + 6, data, cmd->len - 6);
+
+	sizerem = sizelocal >> 4;
+	sizelocal &= 0x0F;
+
+	local[0] = rem[0] = '\0';
+
+	if (sizelocal)
+		nibble_to_byte(data, local, sizelocal, 0);
+
+	if (sizerem)
+		nibble_to_byte(data + (sizelocal >> 1), rem, sizerem, sizelocal & 1);
+	dprintk(KERN_INFO "connect_intr:lcn=%d, local=%s, remote=%s\n",
+			  lcn, local, rem);
+	if ((dev = get_dev_by_dte_addr(wandev, rem)) == NULL) {
+		/* Invalid channel, discard packet */
+		printk(KERN_INFO "%s: connect not expected: remote %s!\n",
+				 card->devname, rem);
+		return;
+	}
+
+	chan = dev->priv;
+	chan->lcn = lcn;
+	x25_connect_response(card, chan);
+	set_chan_state(dev, WAN_CONNECTED, IN_INTR);
+}
+
+/* Connect confirm interrupt handler. */
+static void connect_confirm_intr (cycx_t *card, TX25Cmd *cmd)
+{
+	wan_device_t *wandev = &card->wandev;
+	struct device *dev;
+	x25_channel_t *chan;
+	u8 lcn, key;
+
+	cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn));
+	cycx_peek(&card->hw, cmd->buf + 1, &key, sizeof(key));
+	dprintk(KERN_INFO "%s: connect_confirm_intr:lcn=%d, key=%d\n",
+			  card->devname, lcn, key);
+	if ((dev = get_dev_by_lcn(wandev, -key)) == NULL) {
+		/* Invalid channel, discard packet */
+		clear_bit(--key, (void*)&card->u.x.connection_keys);
+		printk(KERN_INFO "%s: connect confirm not expected: lcn %d, "
+				 "key=%d!\n", card->devname, lcn, key);
+		return;
+	}
+
+	clear_bit(--key, (void*)&card->u.x.connection_keys);
+	chan = dev->priv;
+	chan->lcn = lcn;
+	set_chan_state(dev, WAN_CONNECTED, IN_INTR);
+}
+
+/* Disonnect confirm interrupt handler. */
+static void disconnect_confirm_intr (cycx_t *card, TX25Cmd *cmd)
+{
+	wan_device_t *wandev = &card->wandev;
+	struct device *dev;
+	u8 lcn;
+
+	cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn));
+	dprintk(KERN_INFO "%s: disconnect_confirm_intr:lcn=%d\n",
+			  card->devname, lcn);
+	if ((dev = get_dev_by_lcn(wandev, lcn)) == NULL) {
+		/* Invalid channel, discard packet */
+		printk(KERN_INFO "%s:disconnect confirm not expected!:lcn %d\n",
+				 card->devname, lcn);
+		return;
+	}
+
+	set_chan_state(dev, WAN_DISCONNECTED, IN_INTR);
+}
+
+/* disconnect interrupt handler. */
+static void disconnect_intr (cycx_t *card, TX25Cmd *cmd)
+{
+	wan_device_t *wandev = &card->wandev;
+	struct device *dev;
+	u8 lcn;
+
+	cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn));
+	dprintk(KERN_INFO "disconnect_intr:lcn=%d\n", lcn);
+	x25_disconnect_response(card, 0, lcn);
+
+	if ((dev = get_dev_by_lcn(wandev, lcn)) != NULL)
+		set_chan_state(dev, WAN_DISCONNECTED, IN_INTR);
+}
+
+/* LOG interrupt handler. */
+static void log_intr (cycx_t *card, TX25Cmd *cmd)
+{
+#if CYCLOMX_X25_DEBUG
+	char bf[20];
+	u16 size, toread, link, msg_code;
+	u8 code, routine;
+
+	cycx_peek(&card->hw, cmd->buf, &msg_code, sizeof(msg_code));
+	cycx_peek(&card->hw, cmd->buf + 2, &link, sizeof(link));
+	cycx_peek(&card->hw, cmd->buf + 4, &size, sizeof(size));
+	/* at most 20 bytes are available... thanx to Daniela :) */
+	toread = size < 20 ? size : 20;
+	cycx_peek(&card->hw, cmd->buf + 10, &bf, toread);
+	cycx_peek(&card->hw, cmd->buf + 10 + toread, &code, 1);
+	cycx_peek(&card->hw, cmd->buf + 10 + toread + 1, &routine, 1);
+
+	printk(KERN_INFO "cyx_isr: X25_LOG (0x4500) indic.:\n");
+	printk(KERN_INFO "cmd->buf=0x%X\n", cmd->buf);
+	printk(KERN_INFO "Log message code=0x%X\n", msg_code);
+	printk(KERN_INFO "Link=%d\n", link);
+	printk(KERN_INFO "log code=0x%X\n", code);
+	printk(KERN_INFO "log routine=0x%X\n", routine);
+	printk(KERN_INFO "Message size=%d\n", size);
+	hex_dump("Message", bf, toread);
+#endif
+}
+
+/* STATISTIC interrupt handler. */
+static void stat_intr (cycx_t *card, TX25Cmd *cmd)
+{
+	cycx_peek(&card->hw, cmd->buf, &card->u.x.stats,
+		  sizeof(card->u.x.stats));
+	hex_dump("stat_intr", (unsigned char*)&card->u.x.stats,
+		 sizeof(card->u.x.stats));
+	x25_dump_stats(&card->u.x.stats);
+	wake_up_interruptible(&card->wait_stats);
+}
+
+/* Spurious interrupt handler.
+ * o print a warning
+ * If number of spurious interrupts exceeded some limit, then ??? */
+static void spur_intr (cycx_t *card, TX25Cmd *cmd)
+{
+	printk(KERN_INFO "%s: spurious interrupt (0x%X)!\n",
+			 card->devname, cmd->command);
+}
+#ifdef CYCLOMX_X25_DEBUG
+static void hex_dump(char *msg, unsigned char *p, int len)
+{
+	unsigned char hex[1024],
+	    	* phex = hex;
+
+	if (len >= (sizeof(hex) / 2))
+		len = (sizeof(hex) / 2) - 1;
+
+	while (len--) {
+		sprintf(phex, "%02x", *p++);
+		phex += 2;
+	}
+
+	printk(KERN_INFO "%s: %s\n", msg, hex);
+}
+#endif
+/* CYCLOM X Firmware-Specific Functions
+ *
+ * Almost all X.25 commands can unexpetedly fail due to so called 'X.25
+ * asynchronous events' such as restart, interrupt, incoming call request,
+ * call clear request, etc.  They can't be ignored and have to be dealt with
+ * immediately.  To tackle with this problem we execute each interface command
+ * in a loop until good return code is received or maximum number of retries
+ * is reached.  Each interface command returns non-zero return code, an
+ * asynchronous event/error handler x25_error() is called.
+ */
+/* Exec x25 command. */
+static int x25_exec (cycx_t *card, int command, int link,
+		     void *data1, int len1, void *data2, int len2)
+{
+	TX25Cmd c;
+	u32 addr = 0x1200 + 0x2E0 * link + 0x1E2;
+	int err = 0;
+
+	c.command = command;
+	c.link = link;
+	c.len = len1 + len2;
+
+	if (test_and_set_bit(0, (void*)&card->u.x.critical))
+		return -EAGAIN;
+
+	/* write command */
+	cycx_poke(&card->hw, X25_MBOX_OFFS, &c, sizeof(c) - sizeof(c.buf));
+
+	/* write x25 data */
+	if (data1) {
+		cycx_poke(&card->hw, addr, data1, len1);
+
+		if (data2)
+			if (len2 > 254) {
+				u32 addr1 = 0xA00 + 0x400 * link;
+
+				cycx_poke(&card->hw, addr + len1, data2, 249);
+				cycx_poke(&card->hw, addr1, ((u8*) data2) + 249,
+					  len2 - 249);
+			} else
+				cycx_poke(&card->hw, addr + len1, data2, len2);
+	}
+
+	/* generate interruption, executing command */
+	cycx_intr(&card->hw);
+
+	/* wait till card->mbox == 0 */
+	err = cycx_exec(card->mbox);
+	card->u.x.critical = 0;
+
+	return err;
+}
+
+/* Configure adapter. */
+static int x25_configure (cycx_t *card, TX25Config *conf)
+{
+	struct {
+		u16 nlinks;
+		TX25Config conf[2];
+	} x25_cmd_conf;
+
+	memset (&x25_cmd_conf, 0, sizeof(x25_cmd_conf));
+	x25_cmd_conf.nlinks = 2;
+	x25_cmd_conf.conf[0] = *conf;
+	/* FIXME: we need to find a way in the wanrouter framework
+		  to configure the second link, for now lets use it
+		  with the same config from the first link, fixing
+		  the interface type to RS232, the speed in 38400 and
+		  the clock to external */
+	x25_cmd_conf.conf[1] = *conf;
+	x25_cmd_conf.conf[1].link = 1;
+	x25_cmd_conf.conf[1].speed = 5; /* 38400 */
+	x25_cmd_conf.conf[1].clock = 8;
+	x25_cmd_conf.conf[1].flags = 0; /* default = RS232 */
+
+	x25_dump_config(&x25_cmd_conf.conf[0]);
+	x25_dump_config(&x25_cmd_conf.conf[1]);
+
+	return x25_exec(card, X25_CONFIG, 0,
+			&x25_cmd_conf, sizeof(x25_cmd_conf), NULL, 0);
+}
+
+/* Get protocol statistics. */
+static int x25_get_stats (cycx_t *card)
+{
+	/* the firmware expects 20 in the size field!!!
+	   thanx to Daniela */
+	int err = x25_exec(card, X25_STATISTIC, 0, NULL, 20, NULL, 0);
+
+	if (err)
+		return err;
+
+	interruptible_sleep_on(&card->wait_stats);
+
+	if (signal_pending(current))
+		return -EINTR;
+
+	card->wandev.stats.rx_packets = card->u.x.stats.n2_rx_frames;
+	card->wandev.stats.rx_over_errors = card->u.x.stats.rx_over_errors;
+	card->wandev.stats.rx_crc_errors = card->u.x.stats.rx_crc_errors;
+	card->wandev.stats.rx_length_errors = 0; /* not available from fw */
+	card->wandev.stats.rx_frame_errors = 0; /* not available from fw */
+	card->wandev.stats.rx_missed_errors = card->u.x.stats.rx_aborts;
+	card->wandev.stats.rx_dropped = 0; /* not available from fw */
+	card->wandev.stats.rx_errors = 0; /* not available from fw */
+	card->wandev.stats.tx_packets = card->u.x.stats.n2_tx_frames;
+	card->wandev.stats.tx_aborted_errors = card->u.x.stats.tx_aborts;
+	card->wandev.stats.tx_dropped = 0; /* not available from fw */
+	card->wandev.stats.collisions = 0; /* not available from fw */
+	card->wandev.stats.tx_errors = 0; /* not available from fw */
+
+	x25_dump_devs(&card->wandev);
+	return 0;
+}
+
+/* return the number of nibbles */
+static int byte_to_nibble(u8 *s, u8 *d, char *nibble)
+{
+        int i = 0;
+
+        if (*nibble && *s) {
+                d[i] |= *s++ - '0';
+                *nibble = 0;
+                ++i;
+        }
+
+        while (*s) {
+                d[i] = (*s - '0') << 4;
+                if (*(s + 1))
+			d[i] |= *(s + 1) - '0';
+                else {
+                        *nibble = 1;
+                        break;
+                }
+                ++i;
+                s += 2;
+        }
+
+        return i;
+}
+
+static void nibble_to_byte(u8 *s, u8 *d, u8 len, u8 nibble)
+{
+	if (nibble) {
+		*d++ = '0' + (*s++ & 0x0F);
+		--len;
+	}
+
+	while (len) {
+		*d++ = '0' + (*s >> 4);
+		if (--len) {
+			*d++ = '0' + (*s & 0x0F);
+			--len;
+		} else break;
+		
+		++s;
+	}
+
+	*d = '\0';
+}
+
+/* Place X.25 call. */
+static int x25_place_call (cycx_t *card, x25_channel_t *chan)
+{
+	int err = 0,
+  	    retry = MAX_CMD_RETRY,
+	    len;
+	char data[64],
+	     nibble = 0,
+	     mylen = chan->local_addr ? strlen(chan->local_addr) : 0,
+	     remotelen = strlen(chan->addr);
+	u8 key;
+
+	if (card->u.x.connection_keys == ~0UL) {
+		printk(KERN_INFO "%s: too many simultaneous connection "
+				 "requests!\n", card->devname);
+		return -EAGAIN;
+	}
+
+	key = ffz(card->u.x.connection_keys);
+	set_bit(key, (void*)&card->u.x.connection_keys);
+	++key;
+	dprintk(KERN_INFO "%s:x25_place_call:key=%d\n", card->devname, key);
+	memset(data, 0, sizeof(data));
+	data[1] = key; /* user key */
+	data[2] = 0x10;
+	data[4] = 0x0B;
+
+	len = byte_to_nibble(chan->addr, data + 6, &nibble);
+	len += chan->local_addr ? byte_to_nibble(chan->local_addr,
+						 data + 6 + len, &nibble) : 0;
+	if (nibble)
+		++len;
+	data[5] = mylen << 4 | remotelen;
+	data[6 + len + 1] = 0xCC; /* TCP/IP over X.25, thanx to Daniela :) */
+	
+	do err = x25_exec(card, X25_CONNECT_REQUEST, chan->link,
+			  &data, 7 + len + 1, NULL, 0);
+	while (err && retry--);
+
+        if (err)
+		clear_bit(--key, (void*)&card->u.x.connection_keys);
+	else {
+                chan->lcn = -key;
+                chan->protocol = ETH_P_IP;
+        }
+
+        return err;
+}
+
+/* Place X.25 CONNECT RESPONSE. */
+static int x25_connect_response (cycx_t *card, x25_channel_t *chan)
+{
+	int err = 0,
+  	    retry = MAX_CMD_RETRY;
+	char data[32];
+
+	memset(data, 0, sizeof(data));
+	data[0] = data[3] = chan->lcn;
+	data[2] = 0x10;
+	data[4] = 0x0F;
+	data[7] = 0xCC; /* TCP/IP over X.25, thanx Daniela */
+
+	do err = x25_exec(card, X25_CONNECT_RESPONSE, chan->link,
+			  &data, 8, NULL, 0);
+	while (err && retry--);
+
+        return err;
+}
+
+/* Place X.25 DISCONNECT RESPONSE.  */
+static int x25_disconnect_response (cycx_t *card, u8 link, u8 lcn)
+{
+	int err = 0,
+  	    retry = MAX_CMD_RETRY;
+	char data[5];
+
+	memset(data, 0, sizeof(data));
+	data[0] = data[3] = lcn;
+	data[2] = 0x10;
+	data[4] = 0x17;
+	do err = x25_exec(card, X25_DISCONNECT_RESPONSE, link,
+			  &data, 5, NULL, 0);
+	while (err && retry--);
+
+        return err;
+}
+
+/* Clear X.25 call.  */
+static int x25_clear_call (cycx_t *card, u8 link, u8 lcn, u8 cause, u8 diagn)
+{
+        int retry = MAX_CMD_RETRY,
+            err;
+	u8 data[7];
+
+	memset(data, 0, sizeof(data));
+	data[0] = data[3] = lcn;
+	data[2] = 0x10;
+	data[4] = 0x13;
+	data[5] = cause;
+	data[6] = diagn;
+
+	do err = x25_exec(card, X25_DISCONNECT_REQUEST, link, data, 7, NULL, 0);
+	while (err && retry--);
+
+        return err;
+}
+
+/* Send X.25 data packet. */
+static int x25_send (cycx_t *card, u8 link, u8 lcn, u8 bitm, int len, void *buf)
+{
+	int err = 0,
+  	    retry = MAX_CMD_RETRY;
+	u8 data[] = "?\xFF\x10??"; 
+
+	data[0] = data[3] = lcn;
+	data[4] = bitm;
+
+	do err = x25_exec(card, X25_DATA_REQUEST, link, &data, 5, buf, len);
+	while (err && retry--);
+
+	return err;
+}
+
+/* Miscellaneous */
+/* Find network device by its channel number.  */
+static struct device *get_dev_by_lcn (wan_device_t *wandev, s16 lcn)
+{
+	struct device *dev = wandev->dev;
+
+	for (; dev; dev = dev->slave)
+		if (((x25_channel_t*)dev->priv)->lcn == lcn)
+			break;
+	return dev;
+}
+
+/* Find network device by its remote dte address. */
+static struct device *get_dev_by_dte_addr (wan_device_t *wandev, char *dte)
+{
+	struct device *dev = wandev->dev;
+
+	for (; dev; dev = dev->slave)
+		if (!strcmp (((x25_channel_t*)dev->priv)->addr, dte))
+			break;
+	return dev;
+}
+
+/* Initiate connection on the logical channel.
+ * o for PVC we just get channel configuration
+ * o for SVCs place an X.25 call
+ *
+ * Return:	0	connected
+ *		>0	connection in progress
+ *		<0	failure */
+static int chan_connect (struct device *dev)
+{
+	x25_channel_t *chan = dev->priv;
+	cycx_t *card = chan->card;
+
+	if (chan->svc) {
+                if (!chan->addr[0])
+			return -EINVAL; /* no destination address */
+                dprintk(KERN_INFO "%s: placing X.25 call to %s...\n",
+				  card->devname, chan->addr);
+                if (x25_place_call(card, chan))
+			return -EIO;
+                set_chan_state(dev, WAN_CONNECTING, OUT_INTR);
+                return 1;
+        } else 
+		set_chan_state(dev, WAN_CONNECTED, OUT_INTR);
+
+	return 0;
+}
+
+/* Disconnect logical channel.
+ * o if SVC then clear X.25 call */
+static void chan_disc (struct device *dev)
+{
+	x25_channel_t *chan = dev->priv;
+
+	if (chan->svc) {
+		x25_clear_call(chan->card, chan->link, chan->lcn, 0, 0);
+		set_chan_state(dev, WAN_DISCONNECTING, OUT_INTR);
+	} else
+		set_chan_state(dev, WAN_DISCONNECTED, OUT_INTR);
+}
+
+/* Called by kernel timer */
+static void chan_timer (unsigned long data)
+{
+	struct device *dev = (struct device*) data;
+	x25_channel_t *chan = dev->priv;
+	
+	switch (chan->state) {
+		case WAN_CONNECTED:
+			chan_disc(dev);
+			break;
+		default:
+			printk (KERN_ERR "%s: chan_timer for svc (%s) not "
+					 "connected!\n",
+					 chan->card->devname, dev->name);
+	}
+}
+
+/* Set logical channel state. */
+static void set_chan_state (struct device *dev, u8 state, u8 outside_intr)
+{
+	x25_channel_t *chan = dev->priv;
+	cycx_t *card = chan->card;
+	u32 flags = 0;
+
+	if (outside_intr)
+		spin_lock(&card->lock);
+	else
+		spin_lock_irqsave(&card->lock, flags);
+
+	if (chan->state != state) {
+		if (chan->svc && chan->state == WAN_CONNECTED)
+			del_timer(&chan->timer);
+	
+		switch (state) {
+			case WAN_CONNECTED:
+				printk (KERN_INFO "%s: interface %s "
+						  "connected!\n",
+						  card->devname, dev->name);
+				*(u16*)dev->dev_addr = htons(chan->lcn);
+				dev->tbusy = 0;
+				reset_timer(dev);
+				break;
+
+			case WAN_CONNECTING:
+				printk (KERN_INFO "%s: interface %s "
+						  "connecting...\n",
+						  card->devname, dev->name);
+				break;
+
+			case WAN_DISCONNECTING:
+				printk (KERN_INFO "%s: interface %s "
+						  "disconnecting...\n",
+						  card->devname, dev->name);
+				break;
+
+			case WAN_DISCONNECTED:
+				printk (KERN_INFO "%s: interface %s "
+						  "disconnected!\n",
+						  card->devname, dev->name);
+				if (chan->svc) {
+					*(unsigned short*)dev->dev_addr = 0;
+					chan->lcn = 0;
+                                }
+				break;
+		}
+
+		chan->state = state;
+	}
+
+	if (outside_intr)
+		spin_unlock(&card->lock);
+	else
+		spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/* Send packet on a logical channel.
+ *	When this function is called, tx_skb field of the channel data space
+ *	points to the transmit socket buffer.  When transmission is complete,
+ *	release socket buffer and reset 'tbusy' flag.
+ *
+ * Return:	0	- transmission complete
+ *		1	- busy
+ *
+ * Notes:
+ * 1. If packet length is greater than MTU for this channel, we'll fragment
+ *    the packet into 'complete sequence' using M-bit.
+ * 2. When transmission is complete, an event notification should be issued
+ *    to the router.  */
+static int chan_send (struct device *dev, struct sk_buff *skb)
+{
+	x25_channel_t *chan = dev->priv;
+	cycx_t *card = chan->card;
+	int bitm = 0;		/* final packet */
+	unsigned len = skb->len;
+
+	if (skb->len > card->wandev.mtu) {
+		len = card->wandev.mtu;
+		bitm = 0x10;		/* set M-bit (more data) */
+	}
+
+	if (x25_send(card, chan->link, chan->lcn, bitm, len, skb->data))
+		return 1;
+		
+	if (bitm) {
+		skb_pull(skb, len);
+		return 1;
+	}
+
+	++chan->ifstats.tx_packets;
+	chan->ifstats.tx_bytes += len;
+	return 0;
+}
+
+/* Convert line speed in bps to a number used by cyclom 2x code. */
+static u8 bps_to_speed_code (u32 bps)
+{
+	u8 number = 0; /* defaults to the lowest (1200) speed ;> */
+
+	     if (bps >= 512000) number = 8;
+	else if (bps >= 256000) number = 7;
+	else if (bps >= 64000)  number = 6;
+	else if (bps >= 38400)  number = 5;
+	else if (bps >= 19200)  number = 4;
+	else if (bps >= 9600)   number = 3;
+	else if (bps >= 4800)   number = 2;
+	else if (bps >= 2400)   number = 1;
+
+	return number;
+}
+
+/* log base 2 */
+static u8 log2 (u32 n)
+{
+        u8 log = 0;
+
+        if (!n)
+		return 0;
+
+        while (n > 1) {
+                n >>= 1;
+                ++log;
+        }
+
+        return log;
+}
+
+/* Convert decimal string to unsigned integer.
+ * If len != 0 then only 'len' characters of the string are converted. */
+static unsigned dec_to_uint (u8 *str, int len)
+{
+	unsigned val = 0;
+
+	if (!len)
+		len = strlen(str);
+
+	for (; len && is_digit(*str); ++str, --len)
+		val = (val * 10) + (*str - (unsigned)'0');
+
+	return val;
+}
+
+static void reset_timer(struct device *dev)
+{
+	x25_channel_t *chan = dev->priv;
+
+	if (!chan->svc)
+		return;
+
+	del_timer(&chan->timer);
+	chan->timer.expires = jiffies + chan->idle_tmout * HZ;
+	add_timer(&chan->timer);
+}
+#ifdef CYCLOMX_X25_DEBUG
+static void x25_dump_config(TX25Config *conf)
+{
+	printk (KERN_INFO "x25 configuration\n");
+	printk (KERN_INFO "-----------------\n");
+        printk (KERN_INFO "link number=%d\n", conf->link);
+        printk (KERN_INFO "line speed=%d\n", conf->speed);
+        printk (KERN_INFO "clock=%sternal\n", conf->clock == 8 ? "Ex" : "In");
+        printk (KERN_INFO "# level 2 retransm.=%d\n", conf->n2);
+        printk (KERN_INFO "level 2 window=%d\n", conf->n2win);
+        printk (KERN_INFO "level 3 window=%d\n", conf->n3win);
+        printk (KERN_INFO "# logical channels=%d\n", conf->nvc);
+        printk (KERN_INFO "level 3 pkt len=%d\n", conf->pktlen);
+	printk (KERN_INFO "my address=%d\n", conf->locaddr);
+        printk (KERN_INFO "remote address=%d\n", conf->remaddr);
+        printk (KERN_INFO "t1=%d seconds\n", conf->t1);
+        printk (KERN_INFO "t2=%d seconds\n", conf->t2);
+        printk (KERN_INFO "t21=%d seconds\n", conf->t21);
+        printk (KERN_INFO "# PVCs=%d\n", conf->npvc);
+	printk (KERN_INFO "t23=%d seconds\n", conf->t23);
+        printk (KERN_INFO "flags=0x%x\n", conf->flags);
+}
+
+static void x25_dump_stats(TX25Stats *stats)
+{
+	printk (KERN_INFO "x25 statistics\n");
+	printk (KERN_INFO "--------------\n");
+        printk (KERN_INFO "rx_crc_errors=%d\n", stats->rx_crc_errors);
+        printk (KERN_INFO "rx_over_errors=%d\n", stats->rx_over_errors);
+        printk (KERN_INFO "n2_tx_frames=%d\n", stats->n2_tx_frames);
+        printk (KERN_INFO "n2_rx_frames=%d\n", stats->n2_rx_frames);
+        printk (KERN_INFO "tx_timeouts=%d\n", stats->tx_timeouts);
+        printk (KERN_INFO "rx_timeouts=%d\n", stats->rx_timeouts);
+        printk (KERN_INFO "n3_tx_packets=%d\n", stats->n3_tx_packets);
+        printk (KERN_INFO "n3_rx_packets=%d\n", stats->n3_rx_packets);
+        printk (KERN_INFO "tx_aborts=%d\n", stats->tx_aborts);
+        printk (KERN_INFO "rx_aborts=%d\n", stats->rx_aborts);
+}
+
+static void x25_dump_devs(wan_device_t *wandev)
+{
+	struct device *dev = wandev->dev;
+
+	printk (KERN_INFO "x25 dev states\n");
+	printk (KERN_INFO "name: addr:           tbusy:\n");
+	printk (KERN_INFO "----------------------------\n");
+
+	for (; dev; dev = dev->slave) {
+		x25_channel_t *chan = dev->priv;
+
+		printk (KERN_INFO "%-5.5s %-15.15s   %ld\n",
+				  chan->name, chan->addr, dev->tbusy);
+	}
+}
+
+#endif /* CYCLOMX_X25_DEBUG */
+/* End */
diff -u --recursive --new-file v2.3.5/linux/drivers/net/irda/Config.in linux/drivers/net/irda/Config.in
--- v2.3.5/linux/drivers/net/irda/Config.in	Mon May 31 22:28:05 1999
+++ linux/drivers/net/irda/Config.in	Mon Jun  7 16:18:58 1999
@@ -10,6 +10,7 @@
 dep_tristate 'Winbond W83977AF (IR)' CONFIG_WINBOND_FIR $CONFIG_IRDA
 dep_tristate 'Sharp UIRCC' CONFIG_SHARP_FIR $CONFIG_IRDA
 dep_tristate 'Toshiba Type-O IR Port' CONFIG_TOSHIBA_FIR $CONFIG_IRDA
+dep_tristate 'SMC IrCC' CONFIG_SMC_IRCC_FIR $CONFIG_IRDA
 
 comment 'Dongle support' 
 bool 'Serial dongle support' CONFIG_DONGLE
diff -u --recursive --new-file v2.3.5/linux/drivers/net/irda/Makefile linux/drivers/net/irda/Makefile
--- v2.3.5/linux/drivers/net/irda/Makefile	Mon May 31 22:28:05 1999
+++ linux/drivers/net/irda/Makefile	Mon Jun  7 16:18:58 1999
@@ -28,6 +28,22 @@
   endif
 endif
 
+ifeq ($(CONFIG_IRPORT_SIR),y)
+L_OBJS += irport.o
+else
+  ifeq ($(CONFIG_IRPORT_SIR),m)
+  M_OBJS += irport.o
+  endif
+endif
+
+ifeq ($(CONFIG_IRPORT_SIR),y)
+L_OBJS += irport.o
+else
+  ifeq ($(CONFIG_IRPORT_SIR),m)
+  M_OBJS += irport.o
+  endif
+endif
+
 ifeq ($(CONFIG_NSC_FIR),y)
 L_OBJS += pc87108.o
 else
@@ -60,6 +76,30 @@
   endif
 endif
 
+ifeq ($(CONFIG_TOSHIBA_FIR),y)
+L_OBJS += toshoboe.o
+else
+  ifeq ($(CONFIG_TOSHIBA_FIR),m)
+  M_OBJS += toshoboe.o
+  endif
+endif
+
+ifeq ($(CONFIG_TOSHIBA_FIR),y)
+L_OBJS += toshoboe.o
+else
+  ifeq ($(CONFIG_TOSHIBA_FIR),m)
+  M_OBJS += toshoboe.o
+  endif
+endif
+
+ifeq ($(CONFIG_SMC_IRCC_FIR),y)
+L_OBJS += irport.o smc-ircc.o
+else
+  ifeq ($(CONFIG_SMC_IRCC_FIR),m)
+  M_OBJS += irport.o smc-ircc.o
+  endif
+endif
+
 ifeq ($(CONFIG_ESI_DONGLE),y)
 L_OBJS += esi.o
 else
@@ -89,6 +129,22 @@
 else
   ifeq ($(CONFIG_GIRBIL_DONGLE),m)
   M_OBJS += girbil.o
+  endif
+endif
+
+ifeq ($(CONFIG_LITELINK_DONGLE),y)
+L_OBJS += litelink.o
+else
+  ifeq ($(CONFIG_LITELINK_DONGLE),m)
+  M_OBJS += litelink.o
+  endif
+endif
+
+ifeq ($(CONFIG_LITELINK_DONGLE),y)
+L_OBJS += litelink.o
+else
+  ifeq ($(CONFIG_LITELINK_DONGLE),m)
+  M_OBJS += litelink.o
   endif
 endif
 
diff -u --recursive --new-file v2.3.5/linux/drivers/net/irda/actisys.c linux/drivers/net/irda/actisys.c
--- v2.3.5/linux/drivers/net/irda/actisys.c	Mon May 31 22:28:05 1999
+++ linux/drivers/net/irda/actisys.c	Mon Jun  7 16:18:58 1999
@@ -7,7 +7,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Wed Oct 21 20:02:35 1998
- * Modified at:   Mon May 10 15:12:54 1999
+ * Modified at:   Sun May 16 14:35:11 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
@@ -29,20 +29,15 @@
 #include <linux/sched.h>
 #include <linux/init.h>
 
-#include <asm/ioctls.h>
-#include <asm/segment.h>
-#include <asm/uaccess.h>
-
 #include <net/irda/irda.h>
 #include <net/irda/irmod.h>
 #include <net/irda/irda_device.h>
 #include <net/irda/dongle.h>
 
-static void actisys_reset(struct irda_device *dev, int unused);
+static void actisys_reset(struct irda_device *dev);
 static void actisys_open(struct irda_device *idev, int type);
 static void actisys_close(struct irda_device *dev);
 static void actisys_change_speed( struct irda_device *dev, int baudrate);
-static void actisys_reset(struct irda_device *dev, int unused);
 static void actisys_init_qos(struct irda_device *idev, struct qos_info *qos);
 
 /* These are the baudrates supported */
@@ -169,7 +164,7 @@
  *    	1. Clear DTR for a few ms.
  *
  */
-static void actisys_reset(struct irda_device *idev, int unused)
+static void actisys_reset(struct irda_device *idev)
 {
 	ASSERT(idev != NULL, return;);
 	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
diff -u --recursive --new-file v2.3.5/linux/drivers/net/irda/esi.c linux/drivers/net/irda/esi.c
--- v2.3.5/linux/drivers/net/irda/esi.c	Mon May 31 22:28:05 1999
+++ linux/drivers/net/irda/esi.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Thomas Davis, <ratbert@radiks.net>
  * Created at:    Sat Feb 21 18:54:38 1998
- * Modified at:   Mon May 10 15:13:12 1999
+ * Modified at:   Sun May 16 14:35:21 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:	  esi.c
  *
@@ -31,10 +31,6 @@
 #include <linux/sched.h>
 #include <linux/init.h>
 
-#include <asm/ioctls.h>
-#include <asm/segment.h>
-#include <asm/uaccess.h>
-
 #include <net/irda/irda.h>
 #include <net/irda/irmod.h>
 #include <net/irda/irda_device.h>
@@ -44,7 +40,7 @@
 static void esi_open(struct irda_device *idev, int type);
 static void esi_close(struct irda_device *driver);
 static void esi_change_speed(struct irda_device *idev, int baud);
-static void esi_reset(struct irda_device *idev, int unused);
+static void esi_reset(struct irda_device *idev);
 static void esi_qos_init(struct irda_device *idev, struct qos_info *qos);
 
 static struct dongle dongle = {
@@ -116,7 +112,7 @@
 	irda_device_set_dtr_rts(idev, dtr, rts);
 }
 
-static void esi_reset( struct irda_device *idev, int unused)
+static void esi_reset( struct irda_device *idev)
 {
 	/* Empty */
 }
diff -u --recursive --new-file v2.3.5/linux/drivers/net/irda/girbil.c linux/drivers/net/irda/girbil.c
--- v2.3.5/linux/drivers/net/irda/girbil.c	Mon May 31 22:28:05 1999
+++ linux/drivers/net/irda/girbil.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sat Feb  6 21:02:33 1999
- * Modified at:   Mon May 10 16:01:33 1999
+ * Modified at:   Tue Jun  1 08:47:41 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
@@ -28,17 +28,13 @@
 #include <linux/sched.h>
 #include <linux/init.h>
 
-#include <asm/ioctls.h>
-#include <asm/segment.h>
-#include <asm/uaccess.h>
-
 #include <net/irda/irda.h>
 #include <net/irda/irmod.h>
 #include <net/irda/irda_device.h>
 #include <net/irda/irtty.h>
 #include <net/irda/dongle.h>
 
-static void girbil_reset(struct irda_device *dev, int unused);
+static void girbil_reset(struct irda_device *dev);
 static void girbil_open(struct irda_device *dev, int type);
 static void girbil_close(struct irda_device *dev);
 static void girbil_change_speed(struct irda_device *dev, int baud);
@@ -165,7 +161,7 @@
  *    	  0. set RTS, and wait at least 5 ms 
  *        1. clear RTS 
  */
-void girbil_reset(struct irda_device *idev, int unused)
+void girbil_reset(struct irda_device *idev)
 {
 	__u8 control = GIRBIL_TXEN | GIRBIL_RXEN;
 
@@ -177,22 +173,26 @@
 
 	/* Sleep at least 5 ms */
 	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout(2);
+	schedule_timeout(MSECS_TO_JIFFIES(20));
 	
 	/* Set DTR and clear RTS to enter command mode */
 	irda_device_set_dtr_rts(idev, FALSE, TRUE);
 
 	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout(2);
+	schedule_timeout(MSECS_TO_JIFFIES(20));
 
 	/* Write control byte */
 	irda_device_raw_write(idev, &control, 1);
 
 	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout(2);
+	schedule_timeout(MSECS_TO_JIFFIES(20));
 
 	/* Go back to normal mode */
 	irda_device_set_dtr_rts(idev, TRUE, TRUE);
+
+       	/* Make sure the IrDA chip also goes to defalt speed */
+	if (idev->change_speed)
+		idev->change_speed(idev, 9600);
 }
 
 /*
@@ -204,7 +204,7 @@
 static void girbil_init_qos(struct irda_device *idev, struct qos_info *qos)
 {
 	qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
-	qos->min_turn_time.bits &= 0xfe; /* All except 0 ms */
+	qos->min_turn_time.bits &= 0x03;
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.3.5/linux/drivers/net/irda/irport.c linux/drivers/net/irda/irport.c
--- v2.3.5/linux/drivers/net/irda/irport.c	Mon May 31 22:28:05 1999
+++ linux/drivers/net/irda/irport.c	Mon Jun  7 16:18:58 1999
@@ -1,43 +1,38 @@
 /*********************************************************************
- *		  
+ * 
  * Filename:	  irport.c
- * Version:	  0.9
- * Description:   Serial driver for IrDA. 
+ * Version:	  1.0
+ * Description:   Half duplex serial port SIR driver for IrDA. 
  * Status:	  Experimental.
  * Author:	  Dag Brattli <dagb@cs.uit.no>
  * Created at:	  Sun Aug  3 13:49:59 1997
- * Modified at:   Sat May 23 23:15:20 1998
+ * Modified at:   Tue Jun  1 10:02:42 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:	  serial.c by Linus Torvalds 
  * 
- *     Copyright (c) 1997,1998 Dag Brattli <dagb@cs.uit.no>
- *     All Rights Reserved.
+ *     Copyright (c) 1997, 1998, 1999 Dag Brattli, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
  *     published by the Free Software Foundation; either version 2 of 
  *     the License, or (at your option) any later version.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is 
- *     provided "AS-IS" and at no charge.
- *
- *     NOTICE:
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License 
+ *     along with this program; if not, write to the Free Software 
+ *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
+ *     MA 02111-1307 USA
  *
  *     This driver is ment to be a small half duplex serial driver to be
- *     used for IR-chipsets that has a UART (16550) compatibility mode. If
- *     your chipset is is UART only, you should probably use IrTTY instead
- *     since the Linux serial driver is probably more robust and optimized.
- *
- *     The functions in this file may be used by FIR drivers, but this
- *     driver knows nothing about FIR drivers so don't ever insert such
- *     code into this file. Instead you should code your FIR driver in a
- *     separate file, and then call the functions in this file if
- *     necessary. This is becase it is difficult to use the Linux serial
- *     driver with a FIR driver becase they must share interrupts etc. Most
- *     FIR chipsets can function in advanced SIR mode, and you should
- *     probably use that mode instead of the UART compatibility mode (and
- *     then just forget about this file)
+ *     used for IR-chipsets that has a UART (16550) compatibility mode. 
+ *     Eventually it will replace irtty, because of irtty has some 
+ *     problems that is hard to get around when we don't have control
+ *     over the serial driver. This driver may also be used by FIR 
+ *     drivers to handle SIR mode for them.
  *
  ********************************************************************/
 
@@ -48,14 +43,15 @@
 #include <linux/ioport.h>
 #include <linux/malloc.h>
 #include <linux/string.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
+#include <linux/skbuff.h>
+#include <linux/serial_reg.h>
 #include <linux/errno.h>
 #include <linux/init.h>
 
-#include <linux/skbuff.h>
-#include <linux/serial_reg.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/spinlock.h>
 
 #include <net/irda/irda.h>
 #include <net/irda/irmod.h>
@@ -86,7 +82,6 @@
 static int  irport_net_init(struct device *dev);
 static int  irport_net_open(struct device *dev);
 static int  irport_net_close(struct device *dev);
-static void irport_wait_until_sent(struct irda_device *idev);
 static int  irport_is_receiving(struct irda_device *idev);
 static void irport_set_dtr_rts(struct irda_device *idev, int dtr, int rts);
 static int  irport_raw_write(struct irda_device *idev, __u8 *buf, int len);
@@ -158,12 +153,15 @@
         idev->io.io_ext    = IO_EXTENT;
         idev->io.fifo_size = 16;
 
+	idev->netdev.base_addr = iobase;
+	idev->netdev.irq = irq;
+
 	/* Lock the port that we need */
 	ret = check_region(idev->io.iobase2, idev->io.io_ext);
 	if (ret < 0) { 
-		DEBUG( 0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
-		       idev->io.iobase2);
-		/* w83977af_cleanup( self->idev);  */
+		DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
+		      idev->io.iobase2);
+		/* irport_cleanup(self->idev);  */
 		return -ENODEV;
 	}
 	request_region(idev->io.iobase2, idev->io.io_ext, idev->name);
@@ -207,12 +205,10 @@
 
 static int irport_close(struct irda_device *idev)
 {
-	DEBUG(0, __FUNCTION__ "()\n");
-
 	ASSERT(idev != NULL, return -1;);
 	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);
 
-	/* Release the PORT that this driver is using */
+	/* Release the IO-port that this driver is using */
 	DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n", 
 	      idev->io.iobase2);
 	release_region(idev->io.iobase2, idev->io.io_ext);
@@ -224,24 +220,37 @@
 	return 0;
 }
 
-void irport_start(int iobase)
+void irport_start(struct irda_device *idev, int iobase)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&idev->lock, flags);
+
+	irport_stop(idev, iobase);
+
 	/* Initialize UART */
 	outb(UART_LCR_WLEN8, iobase+UART_LCR);  /* Reset DLAB */
 	outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase+UART_MCR);
 	
 	/* Turn on interrups */
-	outb((UART_IER_RLSI | UART_IER_RDI), iobase+UART_IER);
+	outb(UART_IER_RLSI | UART_IER_RDI |UART_IER_THRI, iobase+UART_IER);
 
+	spin_unlock_irqrestore(&idev->lock, flags);
 }
 
-void irport_stop(int iobase)
+void irport_stop(struct irda_device *idev, int iobase)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&idev->lock, flags);
+
 	/* Reset UART */
 	outb(0, iobase+UART_MCR);
 	
 	/* Turn off interrupts */
 	outb(0, iobase+UART_IER);
+
+	spin_unlock_irqrestore(&idev->lock, flags);
 }
 
 /*
@@ -254,24 +263,24 @@
 {
 	DEBUG(4, __FUNCTION__ "(), iobase=%#x\n", iobase);
 
-
 	return 0;
 }
 
 /*
  * Function irport_change_speed (idev, speed)
  *
- *    Set speed of port to specified baudrate
+ *    Set speed of IrDA port to specified baudrate
  *
  */
 void irport_change_speed(struct irda_device *idev, int speed)
 {
+	unsigned long flags;
 	int iobase; 
 	int fcr;    /* FIFO control reg */
 	int lcr;    /* Line control reg */
 	int divisor;
 
-	DEBUG( 0, __FUNCTION__ "(), Setting speed to: %d\n", speed);
+	DEBUG(0, __FUNCTION__ "(), Setting speed to: %d\n", speed);
 
 	ASSERT(idev != NULL, return;);
 	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
@@ -281,12 +290,24 @@
 	/* Update accounting for new speed */
 	idev->io.baudrate = speed;
 
+	spin_lock_irqsave(&idev->lock, flags);
+
 	/* Turn off interrupts */
 	outb(0, iobase+UART_IER); 
 
 	divisor = SPEED_MAX/speed;
 	
-	fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14;
+	fcr = UART_FCR_ENABLE_FIFO;
+
+	/* 
+	 * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
+	 * almost 1,7 ms at 19200 bps. At speeds above that we can just forget
+	 * about this timeout since it will always be fast enough. 
+	 */
+	if (idev->io.baudrate < 38400)
+		fcr |= UART_FCR_TRIGGER_1;
+	else 
+		fcr |= UART_FCR_TRIGGER_14;
         
 	/* IrDA ports use 8N1 */
 	lcr = UART_LCR_WLEN8;
@@ -297,8 +318,10 @@
 	outb(lcr,		  iobase+UART_LCR); /* Set 8N1	*/
 	outb(fcr,		  iobase+UART_FCR); /* Enable FIFO's */
 
-	/* Turn on receive interrups */
-	outb(UART_IER_RLSI|UART_IER_RDI, iobase+UART_IER); 
+	/* Turn on interrups */
+	outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, iobase+UART_IER);
+
+	spin_unlock_irqrestore(&self->lock, flags);
 }
 
 /*
@@ -312,10 +335,13 @@
 {
 	int actual = 0;
 	int iobase;
+	int fcr;
 
 	ASSERT(idev != NULL, return;);
 	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
 
+	DEBUG(4, __FUNCTION__ "()\n");
+
 	/* Finished with frame?  */
 	if (idev->tx_buff.len > 0)  {
 		/* Write data left in transmit buffer */
@@ -335,9 +361,18 @@
 		/* Schedule network layer, so we can get some more frames */
 		mark_bh(NET_BH);
 
-		outb(UART_FCR_ENABLE_FIFO | 
-		     UART_FCR_TRIGGER_14  |
-		     UART_FCR_CLEAR_RCVR, iobase+UART_FCR); /* Enable FIFO's */
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR;
+
+		if (idev->io.baudrate < 38400)
+			fcr |= UART_FCR_TRIGGER_1;
+		else 
+			fcr |= UART_FCR_TRIGGER_14;
+
+		/* 
+		 * Reset Rx FIFO to make sure that all reflected transmit data
+		 * will be discarded
+		 */
+		outb(fcr, iobase+UART_FCR);
 
 		/* Turn on receive interrupts */
 		outb(UART_IER_RLSI|UART_IER_RDI, iobase+UART_IER); 
@@ -347,7 +382,7 @@
 /*
  * Function irport_write (driver)
  *
- *    
+ *    Fill Tx FIFO with transmit data
  *
  */
 static int irport_write(int iobase, int fifo_size, __u8 *buf, int len)
@@ -356,21 +391,18 @@
 
 	/* Tx FIFO should be empty! */
 	if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
-		DEBUG( 0, __FUNCTION__ "(), failed, fifo not empty!\n");
+		DEBUG(0, __FUNCTION__ "(), failed, fifo not empty!\n");
 		return -1;
 	}
         
 	/* Fill FIFO with current frame */
-	while (( fifo_size-- > 0) && (actual < len)) {
+	while ((fifo_size-- > 0) && (actual < len)) {
 		/* Transmit next byte */
-		outb( buf[actual], iobase+UART_TX);
+		outb(buf[actual], iobase+UART_TX);
 
 		actual++;
 	}
         
-	DEBUG(4, __FUNCTION__ "(), fifo_size %d ; %d sent of %d\n", 
-	      fifo_size, actual, len);
-
 	return actual;
 }
 
@@ -384,11 +416,10 @@
 int irport_hard_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct irda_device *idev;
+	unsigned long flags;
 	int actual = 0;
 	int iobase;
 
-	DEBUG(5, __FUNCTION__ "(), dev=%p\n", dev);
-
 	ASSERT(dev != NULL, return 0;);
 	
 	idev = (struct irda_device *) dev->priv;
@@ -399,8 +430,19 @@
 	iobase = idev->io.iobase2;
 
 	/* Lock transmit buffer */
-	if (irda_lock((void *) &dev->tbusy) == FALSE)
-		return -EBUSY;
+	if (irda_lock((void *) &dev->tbusy) == FALSE) {
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 5)
+			return -EBUSY;
+
+		WARNING("%s: transmit timed out\n", dev->name);
+		irport_start(idev, iobase);
+		irport_change_speed(idev, idev->io.baudrate);
+
+		dev->trans_start = jiffies;
+	}
+
+	spin_lock_irqsave(&idev->lock, flags);
 	
 	/* Init tx buffer */
 	idev->tx_buff.data = idev->tx_buff.head;
@@ -415,6 +457,8 @@
 	/* Turn on transmit finished interrupt. Will fire immediately!  */
 	outb(UART_IER_THRI, iobase+UART_IER); 
 
+	spin_unlock_irqrestore(&idev->lock, flags);
+
 	dev_kfree_skb(skb);
 	
 	return 0;
@@ -431,10 +475,7 @@
 	int iobase;
 	int boguscount = 0;
 
-	if (!idev)
-		return;
-
-	DEBUG(4, __FUNCTION__ "()\n");
+	ASSERT(idev != NULL, return;);
 
 	iobase = idev->io.iobase2;
 
@@ -466,27 +507,42 @@
 	int boguscount = 0;
 
 	if (!idev) {
-		printk(KERN_WARNING __FUNCTION__ 
-		       "() irq %d for unknown device.\n", irq);
+		WARNING(__FUNCTION__ "() irq %d for unknown device.\n", irq);
 		return;
 	}
 
+	spin_lock(&idev->lock);
+
 	idev->netdev.interrupt = 1;
 
 	iobase = idev->io.iobase2;
 
-	iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+	iir = inb(iobase+UART_IIR) & UART_IIR_ID;
 	while (iir) {
 		/* Clear interrupt */
 		lsr = inb(iobase+UART_LSR);
 
-		if ((iir & UART_IIR_THRI) && (lsr & UART_LSR_THRE)) {
-			/* Transmitter ready for data */
-			irport_write_wakeup(idev);
-		} else if ((iir & UART_IIR_RDI) && (lsr & UART_LSR_DR)) {
-	       		/* Receive interrupt */
-			irport_receive(idev);
-		}
+		DEBUG(4, __FUNCTION__ "(), iir=%02x, lsr=%02x, iobase=%#x\n", 
+		      iir, lsr, iobase);
+
+		switch (iir) {
+		case UART_IIR_RLSI:
+			DEBUG(0, __FUNCTION__ "(), RLSI\n");
+			break;
+		case UART_IIR_RDI:
+			if (lsr & UART_LSR_DR)
+				/* Receive interrupt */
+				irport_receive(idev);
+			break;
+		case UART_IIR_THRI:
+			if (lsr & UART_LSR_THRE)
+				/* Transmitter ready for data */
+				irport_write_wakeup(idev);
+			break;
+		default:
+			DEBUG(0, __FUNCTION__ "(), unhandled IIR=%#x\n", iir);
+			break;
+		} 
 		
 		/* Make sure we don't stay here to long */
 		if (boguscount++ > 32)
@@ -495,6 +551,8 @@
  	        iir = inb(iobase + UART_IIR) & UART_IIR_ID;
 	}
 	idev->netdev.interrupt = 0;
+
+	spin_unlock(&idev->lock);
 }
 
 static int irport_net_init(struct device *dev)
@@ -524,18 +582,21 @@
 	iobase = idev->io.iobase2;
 
 	if (request_irq(idev->io.irq2, irport_interrupt, 0, idev->name, 
-			(void *) idev)) {
+			(void *) idev))
 		return -EAGAIN;
-	}
+
+	irport_start(idev, iobase);
+
+	MOD_INC_USE_COUNT;
 
 	/* Ready to play! */
 	dev->tbusy = 0;
 	dev->interrupt = 0;
 	dev->start = 1;
 
-	MOD_INC_USE_COUNT;
-
-	irport_start(iobase);
+	/* Change speed to make sure dongles follow us again */
+	if (idev->change_speed)
+		idev->change_speed(idev, 9600);
 
 	return 0;
 }
@@ -558,12 +619,12 @@
 
 	iobase = idev->io.iobase2;
 
-	irport_stop(iobase);
-
 	/* Stop device */
 	dev->tbusy = 1;
 	dev->start = 0;
 
+	irport_stop(idev, iobase);
+
 	free_irq(idev->io.irq2, idev);
 
 	MOD_DEC_USE_COUNT;
@@ -571,12 +632,32 @@
 	return 0;
 }
 
-static void irport_wait_until_sent(struct irda_device *idev)
+/*
+ * Function irport_wait_until_sent (idev)
+ *
+ *    Delay exectution until finished transmitting
+ *
+ */
+void irport_wait_until_sent(struct irda_device *idev)
 {
-	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout(60*HZ/1000);
+	int iobase;
+
+	iobase = idev->io.iobase2;
+
+	/* Wait until Tx FIFO is empty */
+	while (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
+		DEBUG(2, __FUNCTION__ "(), waiting!\n");
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(MSECS_TO_JIFFIES(60));
+	}
 }
 
+/*
+ * Function irport_is_receiving (idev)
+ *
+ *    Returns true is we are currently receiving data
+ *
+ */
 static int irport_is_receiving(struct irda_device *idev)
 {
 	return (idev->rx_buff.state != OUTSIDE_FRAME);
@@ -635,6 +716,9 @@
 
 MODULE_PARM(io, "1-4i");
 MODULE_PARM(irq, "1-4i");
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Half duplex serial driver for IrDA SIR mode");
 
 /*
  * Function cleanup_module (void)
diff -u --recursive --new-file v2.3.5/linux/drivers/net/irda/litelink.c linux/drivers/net/irda/litelink.c
--- v2.3.5/linux/drivers/net/irda/litelink.c	Mon May 31 22:28:05 1999
+++ linux/drivers/net/irda/litelink.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Stable
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Fri May  7 12:50:33 1999
- * Modified at:   Mon May 10 15:12:18 1999
+ * Modified at:   Wed May 19 07:25:15 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
@@ -33,19 +33,19 @@
 #include <linux/tty.h>
 #include <linux/sched.h>
 #include <linux/init.h>
-#include <asm/ioctls.h>
-#include <asm/uaccess.h>
 
 #include <net/irda/irda.h>
 #include <net/irda/irmod.h>
 #include <net/irda/irda_device.h>
 #include <net/irda/dongle.h>
 
-static void litelink_reset(struct irda_device *dev, int unused);
+#define MIN_DELAY 25      /* 15 us, but wait a little more to be sure */
+#define MAX_DELAY 10000   /* 1 ms */
+
 static void litelink_open(struct irda_device *idev, int type);
 static void litelink_close(struct irda_device *dev);
-static void litelink_change_speed( struct irda_device *dev, int baudrate);
-static void litelink_reset(struct irda_device *dev, int unused);
+static void litelink_change_speed(struct irda_device *dev, int baudrate);
+static void litelink_reset(struct irda_device *dev);
 static void litelink_init_qos(struct irda_device *idev, struct qos_info *qos);
 
 /* These are the baudrates supported */
@@ -105,13 +105,13 @@
 	irda_device_set_dtr_rts(idev, TRUE, FALSE);
 
 	/* Sleep a minimum of 15 us */
-	udelay(15);
+	udelay(MIN_DELAY);
 
 	/* Go back to normal mode */
 	irda_device_set_dtr_rts(idev, TRUE, TRUE);
 	
 	/* Sleep a minimum of 15 us */
-	udelay(15);
+	udelay(MIN_DELAY);
 	
 	/* Cycle through avaiable baudrates until we reach the correct one */
 	for (i=0; i<5 && baud_rates[i] != baudrate; i++) {
@@ -120,13 +120,13 @@
 		irda_device_set_dtr_rts(idev, FALSE, TRUE);
 		
 		/* Sleep a minimum of 15 us */
-		udelay(15);
+		udelay(MIN_DELAY);
 		
 		/* Set DTR, Set RTS */
 		irda_device_set_dtr_rts(idev, TRUE, TRUE);
 		
 		/* Sleep a minimum of 15 us */
-		udelay(15);
+		udelay(MIN_DELAY);
         }
 }
 
@@ -137,11 +137,8 @@
  *      called with a process context!
  *
  */
-static void litelink_reset(struct irda_device *idev, int unused)
+static void litelink_reset(struct irda_device *idev)
 {
-	struct irtty_cb *self;
-        struct tty_struct *tty;
-
 	ASSERT(idev != NULL, return;);
 	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
 	
@@ -149,19 +146,19 @@
 	irda_device_set_dtr_rts(idev, TRUE, TRUE);
 
 	/* Sleep a minimum of 15 us */
-	udelay(15);
+	udelay(MIN_DELAY);
 
 	/* Clear RTS to reset dongle */
 	irda_device_set_dtr_rts(idev, TRUE, FALSE);
 
 	/* Sleep a minimum of 15 us */
-	udelay(15);
+	udelay(MIN_DELAY);
 
 	/* Go back to normal mode */
 	irda_device_set_dtr_rts(idev, TRUE, TRUE);
 	
 	/* Sleep a minimum of 15 us */
-	udelay(15);
+	udelay(MIN_DELAY);
 
 	/* This dongles speed defaults to 115200 bps */
 	idev->qos.baud_rate.value = 115200;
@@ -173,7 +170,7 @@
  *    Initialize QoS capabilities
  *
  */
-static void litelink_init_qos( struct irda_device *idev, struct qos_info *qos)
+static void litelink_init_qos(struct irda_device *idev, struct qos_info *qos)
 {
 	qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
 	qos->min_turn_time.bits &= 0x40; /* Needs 0.01 ms */
diff -u --recursive --new-file v2.3.5/linux/drivers/net/irda/pc87108.c linux/drivers/net/irda/pc87108.c
--- v2.3.5/linux/drivers/net/irda/pc87108.c	Mon May 31 22:28:05 1999
+++ linux/drivers/net/irda/pc87108.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sat Nov  7 21:43:15 1998
- * Modified at:   Sun May  9 12:57:46 1999
+ * Modified at:   Mon May 24 15:19:21 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>
@@ -72,7 +72,7 @@
 #define CHIP_IO_EXTENT 8
 
 static unsigned int io[]  = { 0x2f8, ~0, ~0, ~0 };
-static unsigned int io2[] = { 0x150, 0, 0, 0};
+static unsigned int io2[] = { 0x150, 0, 0, 0 };
 static unsigned int irq[] = { 3, 0, 0, 0 };
 static unsigned int dma[] = { 0, 0, 0, 0 };
 
@@ -98,28 +98,28 @@
 };
 
 /* Some prototypes */
-static int  pc87108_open( int i, unsigned int iobase, unsigned int board_addr, 
-			  unsigned int irq, unsigned int dma);
+static int  pc87108_open(int i, unsigned int iobase, unsigned int board_addr, 
+			 unsigned int irq, unsigned int dma);
 #ifdef MODULE
-static int  pc87108_close( struct irda_device *idev);
+static int  pc87108_close(struct irda_device *idev);
 #endif /* MODULE */
-static int  pc87108_probe( int iobase, int board_addr, int irq, int dma);
-static void pc87108_pio_receive( struct irda_device *idev);
-static int  pc87108_dma_receive( struct irda_device *idev); 
+static int  pc87108_probe(int iobase, int board_addr, int irq, int dma);
+static void pc87108_pio_receive(struct irda_device *idev);
+static int  pc87108_dma_receive(struct irda_device *idev); 
 static int  pc87108_dma_receive_complete(struct irda_device *idev, int iobase);
-static int  pc87108_hard_xmit( struct sk_buff *skb, struct device *dev);
-static int  pc87108_pio_write( int iobase, __u8 *buf, int len, int fifo_size);
-static void pc87108_dma_write( struct irda_device *idev, int iobase);
-static void pc87108_change_speed( struct irda_device *idev, int baud);
+static int  pc87108_hard_xmit(struct sk_buff *skb, struct device *dev);
+static int  pc87108_pio_write(int iobase, __u8 *buf, int len, int fifo_size);
+static void pc87108_dma_write(struct irda_device *idev, int iobase);
+static void pc87108_change_speed(struct irda_device *idev, int baud);
 static void pc87108_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static void pc87108_wait_until_sent( struct irda_device *idev);
-static int  pc87108_is_receiving( struct irda_device *idev);
-static int  pc87108_read_dongle_id ( int iobase);
-static void pc87108_init_dongle_interface ( int iobase, int dongle_id);
-
-static int  pc87108_net_init( struct device *dev);
-static int  pc87108_net_open( struct device *dev);
-static int  pc87108_net_close( struct device *dev);
+static void pc87108_wait_until_sent(struct irda_device *idev);
+static int  pc87108_is_receiving(struct irda_device *idev);
+static int  pc87108_read_dongle_id (int iobase);
+static void pc87108_init_dongle_interface (int iobase, int dongle_id);
+
+static int  pc87108_net_init(struct device *dev);
+static int  pc87108_net_open(struct device *dev);
+static int  pc87108_net_close(struct device *dev);
 
 /*
  * Function pc87108_init ()
@@ -131,11 +131,11 @@
 {
 	int i;
 
-	for ( i=0; (io[i] < 2000) && (i < 4); i++) {
+	for (i=0; (io[i] < 2000) && (i < 4); i++) {
 		int ioaddr = io[i];
 		if (check_region(ioaddr, CHIP_IO_EXTENT) < 0)
 			continue;
-		if (pc87108_open( i, io[i], io2[i], irq[i], dma[i]) == 0)
+		if (pc87108_open(i, io[i], io2[i], irq[i], dma[i]) == 0)
 			return 0;
 	}
 	return -ENODEV;
@@ -167,29 +167,29 @@
  *    Open driver instance
  *
  */
-static int pc87108_open( int i, unsigned int iobase, unsigned int board_addr, 
-			 unsigned int irq, unsigned int dma)
+static int pc87108_open(int i, unsigned int iobase, unsigned int board_addr, 
+			unsigned int irq, unsigned int dma)
 {
 	struct pc87108 *self;
 	struct irda_device *idev;
 	int ret;
 	int dongle_id;
 
-	DEBUG( 0, __FUNCTION__ "()\n");
+	DEBUG(0, __FUNCTION__ "()\n");
 
-	if (( dongle_id = pc87108_probe( iobase, board_addr, irq, dma)) == -1)
+	if ((dongle_id = pc87108_probe(iobase, board_addr, irq, dma)) == -1)
 		return -1;
 
 	/*
 	 *  Allocate new instance of the driver
 	 */
-	self = kmalloc( sizeof(struct pc87108), GFP_KERNEL);
-	if ( self == NULL) {
-		printk( KERN_ERR "IrDA: Can't allocate memory for "
-			"IrDA control block!\n");
+	self = kmalloc(sizeof(struct pc87108), GFP_KERNEL);
+	if (self == NULL) {
+		printk(KERN_ERR "IrDA: Can't allocate memory for "
+		       "IrDA control block!\n");
 		return -ENOMEM;
 	}
-	memset( self, 0, sizeof(struct pc87108));
+	memset(self, 0, sizeof(struct pc87108));
    
 	/* Need to store self somewhere */
 	dev_self[i] = self;
@@ -204,24 +204,24 @@
         idev->io.fifo_size = 32;
 
 	/* Lock the port that we need */
-	ret = check_region( idev->io.iobase, idev->io.io_ext);
-	if ( ret < 0) { 
-		DEBUG( 0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
-		       idev->io.iobase);
+	ret = check_region(idev->io.iobase, idev->io.io_ext);
+	if (ret < 0) { 
+		DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
+		      idev->io.iobase);
 		/* pc87108_cleanup( self->idev);  */
 		return -ENODEV;
 	}
-	request_region( idev->io.iobase, idev->io.io_ext, idev->name);
+	request_region(idev->io.iobase, idev->io.io_ext, idev->name);
 
 	/* Initialize QoS for this device */
-	irda_init_max_qos_capabilies( &idev->qos);
+	irda_init_max_qos_capabilies(&idev->qos);
 	
 	/* The only value we must override it the baudrate */
 	idev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
 		IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8);
 	
 	idev->qos.min_turn_time.bits = qos_mtt_bits;
-	irda_qos_bits_to_value( &idev->qos);
+	irda_qos_bits_to_value(&idev->qos);
 	
 	idev->flags = IFF_FIR|IFF_MIR|IFF_SIR|IFF_DMA|IFF_PIO|IFF_DONGLE;
 
@@ -245,10 +245,10 @@
 	idev->netdev.stop            = pc87108_net_close;
 
 	idev->io.dongle_id = dongle_id;
-	pc87108_init_dongle_interface( iobase, dongle_id);
+	pc87108_init_dongle_interface(iobase, dongle_id);
 
 	/* Open the IrDA device */
-	irda_device_open( idev, driver_name, self);
+	irda_device_open(idev, driver_name, self);
 	
 	return 0;
 }
@@ -267,15 +267,14 @@
 
 	DEBUG( 4, __FUNCTION__ "()\n");
 
-	ASSERT( idev != NULL, return -1;);
-	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+	ASSERT(idev != NULL, return -1;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);
 
         iobase = idev->io.iobase;
 	self = (struct pc87108 *) idev->priv;
 
 	/* Release the PORT that this driver is using */
-	DEBUG( 4, __FUNCTION__ "(), Releasing Region %03x\n", 
-	       idev->io.iobase);
+	DEBUG(4, __FUNCTION__ "(), Releasing Region %03x\n", idev->io.iobase);
 	release_region(idev->io.iobase, idev->io.io_ext);
 
 	irda_device_close(idev);
@@ -292,22 +291,22 @@
  *    Returns non-negative on success.
  *
  */
-static int pc87108_probe( int iobase, int board_addr, int irq, int dma) 
+static int pc87108_probe(int iobase, int board_addr, int irq, int dma) 
 {
 	int version;
 	__u8 temp=0;
 	int dongle_id;
 	
-	DEBUG( 4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
 
 	/* Base Address and Interrupt Control Register BAIC */
 	outb(0, board_addr);
-	switch ( iobase) {
-	case 0x3E8: outb( 0x14, board_addr+1); break;
-	case 0x2E8: outb( 0x15, board_addr+1); break;
-	case 0x3F8: outb( 0x16, board_addr+1); break;
-	case 0x2F8: outb( 0x17, board_addr+1); break;
-	default:    DEBUG(0, __FUNCTION__ "(), invalid base_address");
+	switch (iobase) {
+	case 0x3E8: outb(0x14, board_addr+1); break;
+	case 0x2E8: outb(0x15, board_addr+1); break;
+	case 0x3F8: outb(0x16, board_addr+1); break;
+	case 0x2F8: outb(0x17, board_addr+1); break;
+	default:    ERROR(__FUNCTION__ "(), invalid base_address");
 	}
 	
 	/* Control Signal Routing Register CSRT */
@@ -319,74 +318,73 @@
 	case 9:  temp = 0x05; break;
 	case 11: temp = 0x06; break;
 	case 15: temp = 0x07; break;
-	default: DEBUG( 0, __FUNCTION__ "(), invalid irq");
+	default: ERROR(__FUNCTION__ "(), invalid irq");
 	}
-	outb( 1, board_addr);
-
+	outb(1, board_addr);
+	
 	switch (dma) {	
-	case 0: outb( 0x08+temp, board_addr+1); break;
-	case 1: outb( 0x10+temp, board_addr+1); break;
-	case 3: outb( 0x18+temp, board_addr+1); break;
+	case 0: outb(0x08+temp, board_addr+1); break;
+	case 1: outb(0x10+temp, board_addr+1); break;
+	case 3: outb(0x18+temp, board_addr+1); break;
 	default: DEBUG( 0, __FUNCTION__ "(), invalid dma");
 	}
 
 	/* Mode Control Register MCTL */
-	outb( 2, board_addr);
-	outb( 0x03, board_addr+1);
+	outb(2, board_addr);
+	outb(0x03, board_addr+1);
 		
 	/* read the Module ID */
-	switch_bank( iobase, BANK3);
-	version =  inb( iobase+MID);
+	switch_bank(iobase, BANK3);
+	version = inb(iobase+MID);
 	
 	/* should be 0x2? */
-	if (0x20 != (version & 0xf0))
-	{
-		DEBUG( 0, __FUNCTION__ "(), Wrong chip version");	
+	if (0x20 != (version & 0xf0)) {
+		ERROR(__FUNCTION__ "(), Wrong chip version %02x\n", version);
 		return -1;
 	}
 	
 	/* Switch to advanced mode */
 	switch_bank( iobase, BANK2);
-	outb( ECR1_EXT_SL, iobase+ECR1);
-	switch_bank( iobase, BANK0);
+	outb(ECR1_EXT_SL, iobase+ECR1);
+	switch_bank(iobase, BANK0);
 
-	dongle_id = pc87108_read_dongle_id( iobase);
-	DEBUG( 0, __FUNCTION__ "(), Found dongle: %s\n", 
-	       dongle_types[ dongle_id]);
+	dongle_id = pc87108_read_dongle_id(iobase);
+	DEBUG(0, __FUNCTION__ "(), Found dongle: %s\n", 
+	      dongle_types[ dongle_id]);
 	
 	/* Set FIFO threshold to TX17, RX16, reset and enable FIFO's */
-	switch_bank( iobase, BANK0);	
-	outb( FCR_RXTH|FCR_TXTH|FCR_TXSR|FCR_RXSR|FCR_FIFO_EN, iobase+FCR);
+	switch_bank(iobase, BANK0);	
+	outb(FCR_RXTH|FCR_TXTH|FCR_TXSR|FCR_RXSR|FCR_FIFO_EN, iobase+FCR);
 	
 	/* Set FIFO size to 32 */
-	switch_bank( iobase, BANK2);
-	outb( EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2);	
+	switch_bank(iobase, BANK2);
+	outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2);	
 
 	/* IRCR2: FEND_MD is set */
-	switch_bank( iobase, BANK5);
-	outb( 0x2a, iobase+4);
+	switch_bank(iobase, BANK5);
+	outb(0x2a, iobase+4);
 
 	/* Make sure that some defaults are OK */
-	switch_bank( iobase, BANK6);
-	outb( 0x20, iobase+0); /* Set 32 bits FIR CRC */
-	outb( 0x0a, iobase+1); /* Set MIR pulse width */
-	outb( 0x0d, iobase+2); /* Set SIR pulse width */
-	outb( 0x2a, iobase+4); /* Set beginning frag, and preamble length */
+	switch_bank(iobase, BANK6);
+	outb(0x20, iobase+0); /* Set 32 bits FIR CRC */
+	outb(0x0a, iobase+1); /* Set MIR pulse width */
+	outb(0x0d, iobase+2); /* Set SIR pulse width */
+	outb(0x2a, iobase+4); /* Set beginning frag, and preamble length */
 
 	/* Receiver frame length */
-	switch_bank( iobase, BANK4);
-	outb( 2048 & 0xff, iobase+6);
-	outb(( 2048 >> 8) & 0x1f, iobase+7);
+	switch_bank(iobase, BANK4);
+	outb(2048 & 0xff, iobase+6);
+	outb((2048 >> 8) & 0x1f, iobase+7);
 
 	/* Transmitter frame length */
-	outb( 2048 & 0xff, iobase+4);
-	outb(( 2048 >> 8) & 0x1f, iobase+5);
+	outb(2048 & 0xff, iobase+4);
+	outb((2048 >> 8) & 0x1f, iobase+5);
 	
-	DEBUG( 0, "PC87108 driver loaded. Version: 0x%02x\n", version);
+	DEBUG(0, "PC87108 driver loaded. Version: 0x%02x\n", version);
 
 	/* Enable receive interrupts */
-	switch_bank( iobase, BANK0);
-	outb( IER_RXHDL_IE, iobase+IER);
+	switch_bank(iobase, BANK0);
+	outb(IER_RXHDL_IE, iobase+IER);
 
 	return dongle_id;
 }
@@ -409,10 +407,10 @@
 	bank = inb( iobase+BSR);
 
 	/* Select Bank 7 */
-	switch_bank( iobase, BANK7);
+	switch_bank(iobase, BANK7);
 	
 	/* IRCFG4: IRSL0_DS and IRSL21_DS are cleared */
-	outb( 0x00, iobase+7);
+	outb(0x00, iobase+7);
 	
 	/* ID0, 1, and 2 are pulled up/down very slowly */
 	udelay(50);
@@ -421,16 +419,16 @@
 	dongle_id = inb( iobase+4) & 0x0f;
 
 #ifdef BROKEN_DONGLE_ID
-	if ( dongle_id == 0x0a)
+	if (dongle_id == 0x0a)
 		dongle_id = 0x09;
 #endif
-
+	
 	/* Go back to  bank 0 before returning */
-	switch_bank( iobase, BANK0);
+	switch_bank(iobase, BANK0);
 
-	DEBUG( 0, __FUNCTION__ "(), Dongle = %#x\n", dongle_id);
+	DEBUG(0, __FUNCTION__ "(), Dongle = %#x\n", dongle_id);
 
-	outb( bank, iobase+BSR);
+	outb(bank, iobase+BSR);
 
 	return dongle_id;
 }
@@ -443,7 +441,7 @@
  *     power-on/reset. It also needs to be used whenever you suspect that
  *     the dongle is changed. 
  */
-static void pc87108_init_dongle_interface ( int iobase, int dongle_id)
+static void pc87108_init_dongle_interface (int iobase, int dongle_id)
 {
 	int bank;
 
@@ -818,11 +816,11 @@
 	      iobase+ECR1);
 	
 	/* Enable DMA */
- 	switch_bank( iobase, BANK0);	
-	outb( inb( iobase+MCR)|MCR_DMA_EN, iobase+MCR);
+ 	switch_bank(iobase, BANK0);	
+	outb(inb(iobase+MCR)|MCR_DMA_EN, iobase+MCR);
 
 	/* Restore bank register */
-	outb( bsr, iobase+BSR);
+	outb(bsr, iobase+BSR);
 }
 
 /*
@@ -832,7 +830,7 @@
  *    got transfered
  *
  */
-static int pc87108_pio_write( int iobase, __u8 *buf, int len, int fifo_size)
+static int pc87108_pio_write(int iobase, __u8 *buf, int len, int fifo_size)
 {
 	int actual = 0;
 	__u8 bank;
@@ -851,16 +849,16 @@
 	}
 
 	/* Fill FIFO with current frame */
-	while (( fifo_size-- > 0) && (actual < len)) {
+	while ((fifo_size-- > 0) && (actual < len)) {
 		/* Transmit next byte */
-		outb( buf[actual++], iobase+TXD);
+		outb(buf[actual++], iobase+TXD);
 	}
         
-	DEBUG( 4, __FUNCTION__ "(), fifo_size %d ; %d sent of %d\n", 
-	       fifo_size, actual, len);
+	DEBUG(4, __FUNCTION__ "(), fifo_size %d ; %d sent of %d\n", 
+	      fifo_size, actual, len);
 
 	/* Restore bank */
-	outb( bank, iobase+BSR);
+	outb(bank, iobase+BSR);
 
 	return actual;
 }
@@ -1466,6 +1464,9 @@
 MODULE_DESCRIPTION("NSC PC87108 IrDA Device Driver");
 
 MODULE_PARM(qos_mtt_bits, "i");
+MODULE_PARM(io, "1-4i");
+MODULE_PARM(io2, "1-4i");
+MODULE_PARM(irq, "1-4i");
 
 /*
  * Function init_module (void)
diff -u --recursive --new-file v2.3.5/linux/drivers/net/irda/smc-ircc.c linux/drivers/net/irda/smc-ircc.c
--- v2.3.5/linux/drivers/net/irda/smc-ircc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/irda/smc-ircc.c	Mon Jun  7 16:18:58 1999
@@ -0,0 +1,969 @@
+/*********************************************************************
+ *                
+ * Filename:      smc-ircc.c
+ * Version:       0.1
+ * Description:   Driver for the SMC Infrared Communications Controller (SMC)
+ * Status:        Experimental.
+ * Author:        Thomas Davis (tadavis@jps.net)
+ * Created at:    
+ * Modified at:   Wed May 19 15:30:08 1999
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (c) 1998-1999 Thomas Davis, All Rights Reserved.
+ *      
+ *     This program is free software; you can redistribute it and/or 
+ *     modify it under the terms of the GNU General Public License as 
+ *     published by the Free Software Foundation; either version 2 of 
+ *     the License, or (at your option) any later version.
+ *  
+ *     I, Thomas Davis, admit no liability nor provide warranty for any
+ *     of this software. This material is provided "AS-IS" and at no charge.
+ *
+ *     Applicable Models : Fujitsu Lifebook 635t
+ *			   Sony PCG-505TX (gets DMA wrong.)
+ *
+ ********************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+
+#include <net/irda/wrapper.h>
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/irlap_frame.h>
+#include <net/irda/irda_device.h>
+
+#include <net/irda/smc-ircc.h>
+#include <net/irda/irport.h>
+
+static char *driver_name = "smc-ircc";
+
+#define CHIP_IO_EXTENT 8
+
+static unsigned int io[]  = { 0x2e8, 0x140, ~0, ~0 };
+static unsigned int io2[] = { 0x2f8, 0x3e8, 0, 0};
+
+static struct ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL};
+
+/* Some prototypes */
+static int  ircc_open( int i, unsigned int iobase, unsigned int board_addr);
+static int  ircc_close( struct irda_device *idev);
+static int  ircc_probe( int iobase, int board_addr);
+static int  ircc_dma_receive( struct irda_device *idev); 
+static int  ircc_dma_receive_complete(struct irda_device *idev, int iobase);
+static int  ircc_hard_xmit( struct sk_buff *skb, struct device *dev);
+static void ircc_dma_write( struct irda_device *idev, int iobase);
+static void ircc_change_speed( struct irda_device *idev, int baud);
+static void ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void ircc_wait_until_sent( struct irda_device *idev);
+static int  ircc_is_receiving( struct irda_device *idev);
+
+static int  ircc_net_init( struct device *dev);
+static int  ircc_net_open( struct device *dev);
+static int  ircc_net_close( struct device *dev);
+
+static int ircc_debug=3;
+static int ircc_irq=255;
+static int ircc_dma=255;
+
+static inline void register_bank(int port, int bank)
+{
+        outb(((inb(port+UART_MASTER) & 0xF0) | (bank & 0x07)),
+             port+UART_MASTER);
+}
+
+static inline unsigned int serial_in(int port, int offset)
+{
+        return inb(port+offset);
+}
+
+static inline void serial_out(int port, int offset, int value)
+{
+        outb(value, port+offset);
+}
+
+/*
+ * Function ircc_init ()
+ *
+ *    Initialize chip. Just try to find out how many chips we are dealing with
+ *    and where they are
+ */
+__initfunc(int ircc_init(void))
+{
+	int i;
+
+	DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+	for ( i=0; (io[i] < 2000) && (i < 4); i++) {
+		int ioaddr = io[i];
+		if (check_region(ioaddr, CHIP_IO_EXTENT))
+			continue;
+		if (ircc_open( i, io[i], io2[i]) == 0)
+			return 0;
+	}
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+
+	return -ENODEV;
+}
+
+/*
+ * Function ircc_cleanup ()
+ *
+ *    Close all configured chips
+ *
+ */
+#ifdef MODULE
+static void ircc_cleanup(void)
+{
+	int i;
+
+	DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+	for ( i=0; i < 4; i++) {
+		if ( dev_self[i])
+			ircc_close( &(dev_self[i]->idev));
+	}
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+}
+#endif /* MODULE */
+
+/*
+ * Function ircc_open (iobase, irq)
+ *
+ *    Open driver instance
+ *
+ */
+static int ircc_open( int i, unsigned int iobase, unsigned int iobase2)
+{
+	struct ircc_cb *self;
+	struct irda_device *idev;
+	int ret;
+	int config;
+
+	DEBUG( ircc_debug, __FUNCTION__ " -->\n");
+
+	if ((config = ircc_probe( iobase, iobase2)) == -1) {
+	        DEBUG(ircc_debug, 
+		      __FUNCTION__ ": addr 0x%04x - no device found!\n", iobase);
+		return -1;
+	}
+	
+	/*
+	 *  Allocate new instance of the driver
+	 */
+	self = kmalloc( sizeof(struct ircc_cb), GFP_KERNEL);
+	if ( self == NULL) {
+		printk( KERN_ERR "IrDA: Can't allocate memory for "
+			"IrDA control block!\n");
+		return -ENOMEM;
+	}
+	memset(self, 0, sizeof(struct ircc_cb));
+   
+	/* Need to store self somewhere */
+	dev_self[i] = self;
+
+	idev = &self->idev;
+
+	/* Initialize IO */
+	idev->io.iobase    = iobase;
+        idev->io.iobase2   = iobase2; /* Used by irport */
+        idev->io.irq       = config >> 4 & 0x0f;
+	if (ircc_irq < 255) {
+	        printk(KERN_INFO "smc: Overriding IRQ - chip says %d, using %d\n",
+		      idev->io.irq, ircc_irq);
+		idev->io.irq = ircc_irq;
+	}
+        idev->io.io_ext    = CHIP_IO_EXTENT;
+        idev->io.io_ext2   = 8;       /* Used by irport */
+        idev->io.dma       = config & 0x0f;
+	if (ircc_dma < 255) {
+	        printk(KERN_INFO "smc: Overriding DMA - chip says %d, using %d\n",
+		      idev->io.dma, ircc_dma);
+		idev->io.dma = ircc_dma;
+	}
+        idev->io.fifo_size = 16;
+
+	/* Lock the port that we need */
+	ret = check_region( idev->io.iobase, idev->io.io_ext);
+	if ( ret < 0) { 
+		DEBUG( 0, __FUNCTION__ ": can't get iobase of 0x%03x\n",
+		       idev->io.iobase);
+		/* ircc_cleanup( self->idev);  */
+		return -ENODEV;
+	}
+	ret = check_region( idev->io.iobase2, idev->io.io_ext2);
+	if ( ret < 0) { 
+		DEBUG( 0, __FUNCTION__ ": can't get iobase of 0x%03x\n",
+		       idev->io.iobase2);
+		/* ircc_cleanup( self->idev);  */
+		return -ENODEV;
+	}
+	request_region( idev->io.iobase, idev->io.io_ext, idev->name);
+        request_region( idev->io.iobase2, idev->io.io_ext2, idev->name);
+
+	/* Initialize QoS for this device */
+	irda_init_max_qos_capabilies( &idev->qos);
+	
+#if 1
+	/* The only value we must override it the baudrate */
+	idev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+		IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8);
+#else
+	/* The only value we must override it the baudrate */
+	idev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+		IR_115200;
+#endif
+
+	idev->qos.min_turn_time.bits = 0x07;
+	irda_qos_bits_to_value( &idev->qos);
+
+	idev->flags = IFF_FIR|IFF_SIR|IFF_DMA|IFF_PIO;
+	
+	/* Specify which buffer allocation policy we need */
+	idev->rx_buff.flags = GFP_KERNEL | GFP_DMA;
+	idev->tx_buff.flags = GFP_KERNEL | GFP_DMA;
+
+	/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
+	idev->rx_buff.truesize = 4000; 
+	idev->tx_buff.truesize = 4000;
+	
+	/* Initialize callbacks */
+	idev->change_speed    = ircc_change_speed;
+	idev->wait_until_sent = ircc_wait_until_sent;
+	idev->is_receiving    = ircc_is_receiving;
+     
+	/* Override the network functions we need to use */
+	idev->netdev.init            = ircc_net_init;
+	idev->netdev.hard_start_xmit = ircc_hard_xmit;
+	idev->netdev.open            = ircc_net_open;
+	idev->netdev.stop            = ircc_net_close;
+
+	irport_start(idev, iobase2);
+
+	/* Open the IrDA device */
+	irda_device_open( idev, driver_name, self);
+	
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+	return 0;
+}
+
+/*
+ * Function ircc_close (idev)
+ *
+ *    Close driver instance
+ *
+ */
+static int ircc_close( struct irda_device *idev)
+{
+	int iobase;
+
+	DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+	ASSERT( idev != NULL, return -1;);
+	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+        iobase = idev->io.iobase;
+
+	irport_stop(idev, idev->io.iobase2);
+
+	register_bank(iobase, 0);
+	serial_out(iobase, UART_IER, 0);
+	serial_out(iobase, UART_MASTER, UART_MASTER_RESET);
+
+	register_bank(iobase, 1);
+
+        serial_out(iobase, UART_SCE_CFGA, 
+		   UART_CFGA_IRDA_SIR_A | UART_CFGA_TX_POLARITY);
+        serial_out(iobase, UART_SCE_CFGB, UART_CFGB_IR);
+ 
+	/* Release the PORT that this driver is using */
+	DEBUG( ircc_debug, 
+	       __FUNCTION__ ": releasing 0x%03x\n", idev->io.iobase);
+
+	release_region( idev->io.iobase, idev->io.io_ext);
+
+	if ( idev->io.iobase2) {
+		DEBUG( ircc_debug, __FUNCTION__ ": releasing 0x%03x\n", 
+		       idev->io.iobase2);
+		release_region( idev->io.iobase2, idev->io.io_ext2);
+	}
+
+	irda_device_close( idev);
+
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+	return 0;
+}
+
+/*
+ * Function ircc_probe (iobase, board_addr, irq, dma)
+ *
+ *    Returns non-negative on success.
+ *
+ */
+static int ircc_probe( int iobase, int iobase2) 
+{
+	int version = 1;
+	int low, high, chip, config, dma, irq;
+	
+	DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+	register_bank(iobase, 3);
+	high = serial_in(iobase, UART_ID_HIGH);
+	low = serial_in(iobase, UART_ID_LOW);
+	chip = serial_in(iobase, UART_CHIP_ID);
+	version = serial_in(iobase, UART_VERSION);
+	config = serial_in(iobase, UART_INTERFACE);
+	irq = config >> 4 & 0x0f;
+	dma = config & 0x0f;
+
+	if (high == 0x10 && low == 0xb8 && chip == 0xf1) {
+		DEBUG(0, "SMC IrDA Controller found; version = %d, "
+		      "port 0x%04x, dma %d, interrupt %d\n",
+		      version, iobase, dma, irq);
+	} else {
+		return -1;
+	}
+
+	serial_out(iobase, UART_MASTER, 0);
+
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+
+	return config;
+}
+
+/*
+ * Function ircc_change_speed (idev, baud)
+ *
+ *    Change the speed of the device
+ *
+ */
+static void ircc_change_speed( struct irda_device *idev, int speed)
+{
+	struct ircc_cb *self;
+	int iobase, ir_mode, select, fast; 
+
+	DEBUG(ircc_debug+1, __FUNCTION__ " -->\n");
+
+	ASSERT(idev != NULL, return;);
+	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+	self = idev->priv;
+	iobase = idev->io.iobase;
+
+	/* Update accounting for new speed */
+	idev->io.baudrate = speed;
+
+	switch ( speed) {
+	case 9600:
+	case 19200:
+	case 37600:
+	case 57600:
+	case 115200:
+	        DEBUG(ircc_debug+1, 
+		      __FUNCTION__ ": using irport to change speed to %d\n",
+		      speed);
+		register_bank(iobase, 0);
+		serial_out(iobase, UART_IER, 0);
+		serial_out(iobase, UART_MASTER, UART_MASTER_RESET);
+		serial_out(iobase, UART_MASTER, UART_MASTER_INT_EN);
+		irport_start(idev, idev->io.iobase2);
+		irport_change_speed( idev, speed);
+		return;
+		break;
+
+	case 576000:		
+		ir_mode = UART_CFGA_IRDA_HDLC;
+		select = 0;
+		fast = 0;
+		DEBUG( ircc_debug, __FUNCTION__ ": handling baud of 576000\n");
+		break;
+	case 1152000:
+		ir_mode = UART_CFGA_IRDA_HDLC;
+		select = UART_1152;
+		fast = 0;
+		DEBUG(ircc_debug, __FUNCTION__ ": handling baud of 1152000\n");
+		break;
+	case 4000000:
+		ir_mode = UART_CFGA_IRDA_4PPM;
+		select = 0;
+		fast = UART_LCR_A_FAST;
+		DEBUG(ircc_debug, __FUNCTION__ ": handling baud of 4000000\n");
+		break;
+	default:
+		DEBUG( 0, __FUNCTION__ ": unknown baud rate of %d\n", speed);
+		return;
+	}
+
+#if 0
+	serial_out(idev->io.iobase2, 4, 0x08);
+#endif
+
+	serial_out(iobase, UART_MASTER, UART_MASTER_RESET);
+
+	register_bank(iobase, 0);
+	serial_out(iobase, UART_IER, 0);
+	
+       	irport_stop(idev, idev->io.iobase2);
+
+	idev->netdev.tbusy = 0;
+	
+	register_bank(iobase, 1);
+
+	serial_out(iobase, UART_SCE_CFGA, 
+		   ((serial_in(iobase, UART_SCE_CFGA) & 0x87) | ir_mode));
+
+	serial_out(iobase, UART_SCE_CFGB,
+		   ((serial_in(iobase, UART_SCE_CFGB) & 0x3f) | UART_CFGB_IR));
+
+	(void) serial_in(iobase, UART_FIFO_THRESHOLD);
+	serial_out(iobase, UART_FIFO_THRESHOLD, 64);
+
+	register_bank(iobase, 4);
+
+	serial_out(iobase, UART_CONTROL,
+		   (serial_in(iobase, UART_CONTROL) & 0x30) 
+		   | select | UART_CRC );
+
+	register_bank(iobase, 0);
+
+	serial_out(iobase, UART_LCR_A, fast);
+
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+}
+
+/*
+ * Function ircc_hard_xmit (skb, dev)
+ *
+ *    Transmit the frame!
+ *
+ */
+static int ircc_hard_xmit( struct sk_buff *skb, struct device *dev)
+{
+	struct irda_device *idev;
+	int iobase;
+	int mtt;
+
+	DEBUG(ircc_debug+1, __FUNCTION__ " -->\n");
+	idev = (struct irda_device *) dev->priv;
+
+	ASSERT( idev != NULL, return 0;);
+	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+	iobase = idev->io.iobase;
+
+	DEBUG(ircc_debug+1, __FUNCTION__ "(%ld), skb->len=%d\n", jiffies, (int) skb->len);
+
+	/* Use irport for SIR speeds */
+	if (idev->io.baudrate <= 115200) {
+		DEBUG(ircc_debug+1, __FUNCTION__ ": calling irport_hard_xmit\n");
+		return irport_hard_xmit(skb, dev);
+	}
+	
+	DEBUG(ircc_debug, __FUNCTION__ ": using dma; len=%d\n", skb->len);
+
+	/* Lock transmit buffer */
+	if (irda_lock((void *) &dev->tbusy) == FALSE)
+		return -EBUSY;
+
+	memcpy( idev->tx_buff.head, skb->data, skb->len);
+
+	/* Make sure that the length is a multiple of 16 bits */
+	if ( skb->len & 0x01)
+		skb->len++;
+
+	idev->tx_buff.len = skb->len;
+	idev->tx_buff.data = idev->tx_buff.head;
+#if 0
+	idev->tx_buff.offset = 0;
+#endif
+	
+	mtt = irda_get_mtt( skb);
+	
+	/* Use udelay for delays less than 50 us. */
+	if (mtt)
+		udelay( mtt);
+	
+	ircc_dma_write( idev, iobase);
+	
+	dev_kfree_skb( skb);
+
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+	return 0;
+}
+
+/*
+ * Function ircc_dma_xmit (idev, iobase)
+ *
+ *    Transmit data using DMA
+ *
+ */
+static void ircc_dma_write( struct irda_device *idev, int iobase)
+{
+	struct ircc_cb *self;
+
+	DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+	ASSERT( idev != NULL, return;);
+	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+	self = idev->priv;
+	iobase = idev->io.iobase;
+
+	setup_dma( idev->io.dma, idev->tx_buff.data, idev->tx_buff.len, 
+		   DMA_MODE_WRITE);
+	
+	idev->io.direction = IO_XMIT;
+
+	serial_out(idev->io.iobase2, 4, 0x08);
+
+	register_bank(iobase, 4);
+	serial_out(iobase, UART_CONTROL, 
+		   (serial_in(iobase, UART_CONTROL) & 0xF0));
+
+	serial_out(iobase, UART_BOF_COUNT_LO, 2);
+	serial_out(iobase, UART_BRICKWALL_CNT_LO, 0);
+#if 1
+	serial_out(iobase, UART_BRICKWALL_TX_CNT_HI, idev->tx_buff.len >> 8);
+	serial_out(iobase, UART_TX_SIZE_LO, idev->tx_buff.len & 0xff);
+#else
+	serial_out(iobase, UART_BRICKWALL_TX_CNT_HI, 0);
+	serial_out(iobase, UART_TX_SIZE_LO, 0);
+#endif
+
+	register_bank(iobase, 1);
+	serial_out(iobase, UART_SCE_CFGB,
+		   serial_in(iobase, UART_SCE_CFGB) | UART_CFGB_DMA_ENABLE);
+
+	register_bank(iobase, 0);
+
+	serial_out(iobase, UART_IER, UART_IER_ACTIVE_FRAME | UART_IER_EOM);
+	serial_out(iobase, UART_LCR_B,
+		   UART_LCR_B_SCE_TRANSMIT|UART_LCR_B_SIP_ENABLE);
+
+	serial_out(iobase, UART_MASTER, UART_MASTER_INT_EN);
+
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+}
+
+/*
+ * Function ircc_dma_xmit_complete (idev)
+ *
+ *    The transfer of a frame in finished. This function will only be called 
+ *    by the interrupt handler
+ *
+ */
+static void ircc_dma_xmit_complete( struct irda_device *idev, int underrun)
+{
+	struct ircc_cb *self;
+	int iobase, d;
+
+	DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+	ASSERT( idev != NULL, return;);
+	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+	register_bank(idev->io.iobase, 1);
+
+	serial_out(idev->io.iobase, UART_SCE_CFGB,
+		   serial_in(idev->io.iobase, UART_SCE_CFGB) &
+		   ~UART_CFGB_DMA_ENABLE);
+
+	d = get_dma_residue(idev->io.dma);
+
+	DEBUG(ircc_debug, __FUNCTION__ ": dma residue = %d, len=%d, sent=%d\n", 
+	      d, idev->tx_buff.len, idev->tx_buff.len - d);
+
+	self = idev->priv;
+
+	iobase = idev->io.iobase;
+
+	/* Check for underrrun! */
+	if ( underrun) {
+		idev->stats.tx_errors++;
+		idev->stats.tx_fifo_errors++;		
+	} else {
+		idev->stats.tx_packets++;
+		idev->stats.tx_bytes +=  idev->tx_buff.len;
+	}
+
+	/* Unlock tx_buff and request another frame */
+	idev->netdev.tbusy = 0; /* Unlock */
+	idev->media_busy = FALSE;
+	
+	/* Tell the network layer, that we can accept more frames */
+	mark_bh( NET_BH);
+
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+}
+
+/*
+ * Function ircc_dma_receive (idev)
+ *
+ *    Get ready for receiving a frame. The device will initiate a DMA
+ *    if it starts to receive a frame.
+ *
+ */
+static int ircc_dma_receive( struct irda_device *idev) 
+{
+	struct ircc_cb *self;
+	int iobase;
+
+	DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+	ASSERT( idev != NULL, return -1;);
+	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+	self = idev->priv;
+	iobase= idev->io.iobase;
+
+	setup_dma( idev->io.dma, idev->rx_buff.data, idev->rx_buff.truesize, 
+		   DMA_MODE_READ);
+	
+	/* driver->media_busy = FALSE; */
+	idev->io.direction = IO_RECV;
+	idev->rx_buff.data = idev->rx_buff.head;
+#if 0
+	idev->rx_buff.offset = 0;
+#endif
+
+	register_bank(iobase, 4);
+	serial_out(iobase, UART_CONTROL, 
+		   (serial_in(iobase, UART_CONTROL) &0xF0));
+	serial_out(iobase, UART_BOF_COUNT_LO, 2);
+	serial_out(iobase, UART_BRICKWALL_CNT_LO, 0);
+	serial_out(iobase, UART_BRICKWALL_TX_CNT_HI, 0);
+	serial_out(iobase, UART_TX_SIZE_LO, 0);
+	serial_out(iobase, UART_RX_SIZE_HI, 0);
+	serial_out(iobase, UART_RX_SIZE_LO, 0);
+
+	register_bank(iobase, 0);
+	serial_out(iobase, 
+		   UART_LCR_B, UART_LCR_B_SCE_RECEIVE | UART_LCR_B_SIP_ENABLE);
+	
+	register_bank(iobase, 1);
+	serial_out(iobase, UART_SCE_CFGB,
+		   serial_in(iobase, UART_SCE_CFGB) | 
+		   UART_CFGB_DMA_ENABLE | UART_CFGB_DMA_BURST);
+
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+	return 0;
+}
+
+/*
+ * Function ircc_dma_receive_complete (idev)
+ *
+ *    Finished with receiving frames
+ *
+ *    
+ */
+static int ircc_dma_receive_complete( struct irda_device *idev, int iobase)
+{
+	struct sk_buff *skb;
+	struct ircc_cb *self;
+	int len, msgcnt;
+
+	DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+	self = idev->priv;
+
+	msgcnt = serial_in(idev->io.iobase, UART_LCR_B) & 0x08;
+
+	DEBUG(ircc_debug, __FUNCTION__ ": dma count = %d\n",
+	      get_dma_residue(idev->io.dma));
+
+	len = idev->rx_buff.truesize - get_dma_residue(idev->io.dma) - 4;
+
+	DEBUG(ircc_debug, __FUNCTION__ ": msgcnt = %d, len=%d\n", msgcnt, len);
+
+	skb = dev_alloc_skb( len+1);
+
+	if (skb == NULL)  {
+		printk( KERN_INFO __FUNCTION__ 
+			": memory squeeze, dropping frame.\n");
+		return FALSE;
+	}
+			
+	/* Make sure IP header gets aligned */
+	skb_reserve( skb, 1); 
+	skb_put( skb, len);
+
+	memcpy(skb->data, idev->rx_buff.data, len);
+	idev->stats.rx_packets++;
+
+	skb->dev = &idev->netdev;
+	skb->mac.raw  = skb->data;
+	skb->protocol = htons(ETH_P_IRDA);
+	netif_rx( skb);
+
+	register_bank(idev->io.iobase, 1);
+	serial_out(idev->io.iobase, UART_SCE_CFGB,
+		   serial_in(idev->io.iobase, UART_SCE_CFGB) &
+		   ~UART_CFGB_DMA_ENABLE);
+
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+	return TRUE;
+}
+
+/*
+ * Function ircc_interrupt (irq, dev_id, regs)
+ *
+ *    An interrupt from the chip has arrived. Time to do some work
+ *
+ */
+static void ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int iobase, iir;
+
+	struct irda_device *idev = (struct irda_device *) dev_id;
+
+	DEBUG(ircc_debug+1, __FUNCTION__ " -->\n");
+
+	if (idev == NULL) {
+		printk( KERN_WARNING "%s: irq %d for unknown device.\n", 
+			driver_name, irq);
+		return;
+	}
+	
+	if (idev->io.baudrate <= 115200) {
+		DEBUG(ircc_debug+1, __FUNCTION__ 
+		      ": routing interrupt to irport_interrupt\n");
+		return irport_interrupt( irq, dev_id, regs);
+	}
+
+	iobase = idev->io.iobase;
+
+	idev->netdev.interrupt = 1;
+
+	serial_out(iobase, UART_MASTER, 0);
+
+	register_bank(iobase, 0);
+
+	iir = serial_in(iobase, UART_IIR);
+
+	serial_out(iobase, UART_IER, 0);
+
+	DEBUG(ircc_debug, __FUNCTION__ ": iir = 0x%02x\n", iir);
+
+	if (iir & UART_IIR_EOM) {
+	        DEBUG(ircc_debug, __FUNCTION__ ": UART_IIR_EOM\n");
+		if (idev->io.direction == IO_RECV) {
+			ircc_dma_receive_complete(idev, iobase);
+		} else {
+			ircc_dma_xmit_complete(idev, iobase);
+		}
+		ircc_dma_receive(idev);
+	}
+
+	if (iir & UART_IIR_ACTIVE_FRAME) {
+	        DEBUG(ircc_debug, __FUNCTION__ ": UART_IIR_ACTIVE_FRAME\n");
+		idev->rx_buff.state = INSIDE_FRAME;
+#if 0
+		ircc_dma_receive(idev);
+#endif
+	}
+
+	if (iir & UART_IIR_RAW_MODE) {
+		DEBUG(ircc_debug, __FUNCTION__ ": IIR RAW mode interrupt.\n");
+	}
+
+	idev->netdev.interrupt = 0;
+
+	register_bank(iobase, 0);
+	serial_out(iobase, UART_IER, UART_IER_ACTIVE_FRAME|UART_IER_EOM);
+	serial_out(iobase, UART_MASTER, UART_MASTER_INT_EN);
+
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+}
+
+/*
+ * Function ircc_wait_until_sent (idev)
+ *
+ *    This function should put the current thread to sleep until all data 
+ *    have been sent, so it is safe to change the speed.
+ */
+static void ircc_wait_until_sent( struct irda_device *idev)
+{
+	DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+	/* Just delay 60 ms */
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(6);
+
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+}
+
+/*
+ * Function ircc_is_receiving (idev)
+ *
+ *    Return TRUE is we are currently receiving a frame
+ *
+ */
+static int ircc_is_receiving( struct irda_device *idev)
+{
+	int status = FALSE;
+	/* int iobase; */
+
+	DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+	ASSERT( idev != NULL, return FALSE;);
+	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return FALSE;);
+
+	DEBUG(ircc_debug, __FUNCTION__ ": dma count = %d\n",
+	      get_dma_residue(idev->io.dma));
+
+	status = ( idev->rx_buff.state != OUTSIDE_FRAME);
+	
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+
+	return status;
+}
+
+/*
+ * Function ircc_net_init (dev)
+ *
+ *    Initialize network device
+ *
+ */
+static int ircc_net_init( struct device *dev)
+{
+	DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+	/* Setup to be a normal IrDA network device driver */
+	irda_device_setup( dev);
+
+	/* Insert overrides below this line! */
+
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+	return 0;
+}
+
+
+/*
+ * Function ircc_net_open (dev)
+ *
+ *    Start the device
+ *
+ */
+static int ircc_net_open( struct device *dev)
+{
+	struct irda_device *idev;
+	int iobase;
+	
+	DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+	
+	ASSERT( dev != NULL, return -1;);
+	idev = (struct irda_device *) dev->priv;
+	
+	ASSERT( idev != NULL, return 0;);
+	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+	
+	iobase = idev->io.iobase;
+
+	if (request_irq( idev->io.irq, ircc_interrupt, 0, idev->name, 
+			 (void *) idev)) {
+		return -EAGAIN;
+	}
+	/*
+	 * Always allocate the DMA channel after the IRQ,
+	 * and clean up on failure.
+	 */
+	if (request_dma(idev->io.dma, idev->name)) {
+		free_irq( idev->io.irq, idev);
+		return -EAGAIN;
+	}
+		
+	/* Ready to play! */
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	/* turn on interrupts */
+	
+	MOD_INC_USE_COUNT;
+
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+	return 0;
+}
+
+/*
+ * Function ircc_net_close (dev)
+ *
+ *    Stop the device
+ *
+ */
+static int ircc_net_close(struct device *dev)
+{
+	struct irda_device *idev;
+	int iobase;
+
+	DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+	
+	/* Stop device */
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	ASSERT( dev != NULL, return -1;);
+	idev = (struct irda_device *) dev->priv;
+	
+	ASSERT( idev != NULL, return 0;);
+	ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+	
+	iobase = idev->io.iobase;
+
+	disable_dma( idev->io.dma);
+
+	/* Disable interrupts */
+       
+	free_irq( idev->io.irq, idev);
+	free_dma( idev->io.dma);
+
+	MOD_DEC_USE_COUNT;
+
+	DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+	return 0;
+}
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Thomas Davis <tadavis@jps.net>");
+MODULE_DESCRIPTION("SMC IrCC controller driver");
+MODULE_PARM(ircc_debug,"1i");
+MODULE_PARM(ircc_dma, "1i");
+MODULE_PARM(ircc_irq, "1i");
+
+/*
+ * Function init_module (void)
+ *
+ *    
+ *
+ */
+int init_module(void)
+{
+	return ircc_init();
+}
+
+/*
+ * Function cleanup_module (void)
+ *
+ *    
+ *
+ */
+void cleanup_module(void)
+{
+	ircc_cleanup();
+}
+
+#endif
diff -u --recursive --new-file v2.3.5/linux/drivers/net/irda/tekram.c linux/drivers/net/irda/tekram.c
--- v2.3.5/linux/drivers/net/irda/tekram.c	Mon May 31 22:28:05 1999
+++ linux/drivers/net/irda/tekram.c	Mon Jun  7 16:18:58 1999
@@ -1,12 +1,12 @@
 /*********************************************************************
  *                
  * Filename:      tekram.c
- * Version:       1.1
+ * Version:       1.2
  * Description:   Implementation of the Tekram IrMate IR-210B dongle
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Wed Oct 21 20:02:35 1998
- * Modified at:   Mon May 10 16:10:17 1999
+ * Modified at:   Sun May 16 14:33:42 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
@@ -28,16 +28,12 @@
 #include <linux/sched.h>
 #include <linux/init.h>
 
-#include <asm/ioctls.h>
-#include <asm/segment.h>
-#include <asm/uaccess.h>
-
 #include <net/irda/irda.h>
 #include <net/irda/irda_device.h>
 #include <net/irda/irtty.h>
 #include <net/irda/dongle.h>
 
-static void tekram_reset(struct irda_device *dev, int unused);
+static void tekram_reset(struct irda_device *dev);
 static void tekram_open(struct irda_device *dev, int type);
 static void tekram_close(struct irda_device *dev);
 static void tekram_change_speed(struct irda_device *dev, int baud);
@@ -49,7 +45,7 @@
 #define TEKRAM_19200  0x03
 #define TEKRAM_9600   0x04
 
-#define TEKRAM_PW 0x10 /* Pulse select bit */
+#define TEKRAM_PW     0x10 /* Pulse select bit */
 
 static struct dongle dongle = {
 	TEKRAM_DONGLE,
@@ -112,7 +108,7 @@
 
 	ASSERT(idev != NULL, return;);
 	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
-	
+
 	switch (baud) {
 	default:
 	case 9600:
@@ -121,7 +117,7 @@
 	case 19200:
 		byte = TEKRAM_PW|TEKRAM_19200;
 		break;
-	case 34800:
+	case 38400:
 		byte = TEKRAM_PW|TEKRAM_38400;
 		break;
 	case 57600:
@@ -132,6 +128,9 @@
 		break;
 	}
 
+	/* Need to reset the dongle and go to 9600 bps before programming */
+	tekram_reset(idev);
+	
 	/* Set DTR, Clear RTS */
 	irda_device_set_dtr_rts(idev, TRUE, FALSE);
 	
@@ -162,7 +161,7 @@
  *        3. clear DTR to SPACE state, wait at least 50 us for further 
  *         operation
  */
-void tekram_reset(struct irda_device *idev, int unused)
+void tekram_reset(struct irda_device *idev)
 {
 	ASSERT(idev != NULL, return;);
 	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
@@ -185,8 +184,10 @@
 	irda_device_set_dtr_rts(idev, TRUE, TRUE);
 	
 	udelay(50);
-
-	/* Finished! */
+	
+	/* Make sure the IrDA chip also goes to defalt speed */
+	if (idev->change_speed)
+		idev->change_speed(idev, 9600);
 }
 
 /*
diff -u --recursive --new-file v2.3.5/linux/drivers/net/irda/uircc.c linux/drivers/net/irda/uircc.c
--- v2.3.5/linux/drivers/net/irda/uircc.c	Mon May 31 22:28:06 1999
+++ linux/drivers/net/irda/uircc.c	Mon Jun  7 16:18:58 1999
@@ -7,7 +7,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sat Dec 26 10:59:03 1998
- * Modified at:   Mon May 10 22:11:09 1999
+ * Modified at:   Wed May 19 15:29:56 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
@@ -216,7 +216,7 @@
 	idev->netdev.open            = uircc_net_open;
 	idev->netdev.stop            = uircc_net_close;
 
-	irport_start(iobase2);
+	irport_start(idev, iobase2);
 
 	/* Open the IrDA device */
 	irda_device_open(idev, driver_name, self);
@@ -251,7 +251,7 @@
 	/* Disable modem */
 	outb(0x00, iobase+UIRCC_CR10);
 
-	irport_stop(idev->io.iobase2);
+	irport_stop(idev, idev->io.iobase2);
 
 	/* Release the PORT that this driver is using */
 	DEBUG(4, __FUNCTION__ "(), Releasing Region %03x\n", idev->io.iobase);
@@ -350,7 +350,7 @@
 	case 37600:
 	case 57600:
 	case 115200:
- 		irport_start(idev->io.iobase2);
+ 		irport_start(idev, idev->io.iobase2);
 		irport_change_speed(idev, speed);
 
 		/* Some magic to disable FIR and enable SIR */
@@ -367,7 +367,7 @@
 		DEBUG(0, __FUNCTION__ "(), handling baud of 1152000\n");
 		break;
 	case 4000000:
-		irport_stop(idev->io.iobase2);
+		irport_stop(idev, idev->io.iobase2);
 
 		/* Some magic to disable SIR and enable FIR */
 		uircc_toshiba_cmd(&status, 0xffff, 0x001b, 0x0001);
diff -u --recursive --new-file v2.3.5/linux/drivers/net/irda/w83977af_ir.c linux/drivers/net/irda/w83977af_ir.c
--- v2.3.5/linux/drivers/net/irda/w83977af_ir.c	Mon May 31 22:28:06 1999
+++ linux/drivers/net/irda/w83977af_ir.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Paul VanderSpek
  * Created at:    Wed Nov  4 11:46:16 1998
- * Modified at:   Thu May 13 08:03:27 1999
+ * Modified at:   Fri May 21 22:18:19 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>
@@ -80,8 +80,6 @@
 
 static struct w83977af_ir *dev_self[] = { NULL, NULL, NULL, NULL};
 
-static struct st_fifo_entry prev;
-
 /* Some prototypes */
 static int  w83977af_open(int i, unsigned int iobase, unsigned int irq, 
                           unsigned int dma);
@@ -112,8 +110,6 @@
         int i;
 
 	DEBUG(0, __FUNCTION__ "()\n");
-
-	prev.status = 0;
 
 	for (i=0; (io[i] < 2000) && (i < 4); i++) { 
 		int ioaddr = io[i];
diff -u --recursive --new-file v2.3.5/linux/drivers/net/myri_sbus.c linux/drivers/net/myri_sbus.c
--- v2.3.5/linux/drivers/net/myri_sbus.c	Fri Mar 26 13:57:41 1999
+++ linux/drivers/net/myri_sbus.c	Wed Jun  9 14:45:36 1999
@@ -756,6 +756,7 @@
 	eth->h_proto = type;
 	memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
 	memcpy(eth->h_dest, neigh->ha, dev->addr_len);
+	hh->hh_len = 16;
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.5/linux/drivers/net/ni52.c linux/drivers/net/ni52.c
--- v2.3.5/linux/drivers/net/ni52.c	Wed Oct  7 15:51:45 1998
+++ linux/drivers/net/ni52.c	Tue Jun  8 10:27:18 1999
@@ -5,7 +5,7 @@
  * same Gnu Public License that covers that work.
  *
  * Alphacode 0.82 (96/09/29) for Linux 2.0.0 (or later)
- * Copyrights (c) 1994,1995,1996 by M.Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * Copyrights (c) 1994,1995,1996 by M.Hipp (hippm@informatik.uni-tuebingen.de)
  *    [feel free to mail ....]
  *
  * when using as module: (no autoprobing!)
diff -u --recursive --new-file v2.3.5/linux/drivers/net/ni52.h linux/drivers/net/ni52.h
--- v2.3.5/linux/drivers/net/ni52.h	Thu Apr 11 23:49:38 1996
+++ linux/drivers/net/ni52.h	Tue Jun  8 10:27:18 1999
@@ -4,7 +4,7 @@
  * This is an extension to the Linux operating system, and is covered by the
  * same Gnu Public License that covers that work.
  *
- * copyrights (c) 1994 by Michael Hipp (mhipp@student.uni-tuebingen.de)
+ * copyrights (c) 1994 by Michael Hipp (hippm@informatik.uni-tuebingen.de)
  *
  * I have done a look in the following sources:
  *   crynwr-packet-driver by Russ Nelson
diff -u --recursive --new-file v2.3.5/linux/drivers/net/ni65.c linux/drivers/net/ni65.c
--- v2.3.5/linux/drivers/net/ni65.c	Fri Oct  9 11:56:59 1998
+++ linux/drivers/net/ni65.c	Tue Jun  8 10:27:18 1999
@@ -16,7 +16,7 @@
  *
  * comments/bugs/suggestions can be sent to:
  *   Michael Hipp
- *   email: Michael.Hipp@student.uni-tuebingen.de
+ *   email: hippm@informatik.uni-tuebingen.de
  *
  * sources:
  *   some things are from the 'ni6510-packet-driver for dos by Russ Nelson'
@@ -45,6 +45,7 @@
  */
 
 /*
+ * 99.Jun.8: added support for /proc/net/dev byte count for xosview (HK)
  * 96.Sept.29: virt_to_bus stuff added for new memory modell
  * 96.April.29: Added Harald Koenig's Patches (MH)
  * 96.April.13: enhanced error handling .. more tests (MH)
@@ -966,8 +967,10 @@
 				p->stats.tx_errors++;
 			tmdp->status2 = 0;
 		}
-		else
+		else {
+			p->stats.tx_bytes -= (short)(tmdp->blen);
 			p->stats.tx_packets++;
+		}
 
 #ifdef XMT_VIA_SKB
 		if(p->tmd_skb[p->tmdlast]) {
@@ -1054,6 +1057,7 @@
 				eth_copy_and_sum(skb, (unsigned char *) p->recvbounce[p->rmdnum],len,0);
 #endif
 				p->stats.rx_packets++;
+				p->stats.rx_bytes += len;
 				skb->protocol=eth_type_trans(skb,dev);
 				netif_rx(skb);
 			}
diff -u --recursive --new-file v2.3.5/linux/drivers/net/ptifddi.c linux/drivers/net/ptifddi.c
--- v2.3.5/linux/drivers/net/ptifddi.c	Wed Jun  2 14:44:39 1999
+++ linux/drivers/net/ptifddi.c	Wed Jun  9 14:45:36 1999
@@ -1,4 +1,4 @@
-/* $Id: ptifddi.c,v 1.5 1997/04/16 10:27:27 jj Exp $
+/* $Id: ptifddi.c,v 1.7 1999/06/09 08:19:01 davem Exp $
  * ptifddi.c: Network driver for Performance Technologies single-attach
  *            and dual-attach FDDI sbus cards.
  *
diff -u --recursive --new-file v2.3.5/linux/drivers/net/sdladrv.c linux/drivers/net/sdladrv.c
--- v2.3.5/linux/drivers/net/sdladrv.c	Thu Jan  7 09:21:53 1999
+++ linux/drivers/net/sdladrv.c	Wed Jun  2 14:40:22 1999
@@ -13,6 +13,7 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
+* May 19, 1999	Arnaldo Melo	wanpipe_init belongs to sdlamain.c
 * Dec 20, 1996	Gene Kozin	Version 3.0.0. Complete overhaul.
 * Jul 12, 1996	Gene Kozin	Changes for Linux 2.0 compatibility.
 * Jun 12, 1996	Gene Kozin 	Added support for S503 card.
@@ -89,7 +90,6 @@
 #include <linux/sched.h>	/* for jiffies, HZ, etc. */
 #include <linux/sdladrv.h>	/* API definitions */
 #include <linux/sdlasfm.h>	/* SDLA firmware module definitions */
-#include <linux/init.h>
 #include <asm/io.h>		/* for inb(), outb(), etc. */
 #define _INB(port)		(inb(port))
 #define _OUTB(port, byte)	(outb((byte),(port)))
@@ -288,9 +288,6 @@
 
 #ifdef MODULE
 int init_module (void)
-#else
-__initfunc(int wanpipe_init(void))
-#endif
 {
 	printk(KERN_INFO "%s v%u.%u %s\n",
 		fullname, MOD_VERSION, MOD_RELEASE, copyright);
@@ -301,7 +298,6 @@
 	return 0;
 }
 
-#ifdef MODULE
 /*============================================================================
  * Module 'remove' entry point.
  * o release all remaining system resources
diff -u --recursive --new-file v2.3.5/linux/drivers/net/sdlamain.c linux/drivers/net/sdlamain.c
--- v2.3.5/linux/drivers/net/sdlamain.c	Mon Dec 28 11:05:14 1998
+++ linux/drivers/net/sdlamain.c	Wed Jun  2 14:40:22 1999
@@ -11,6 +11,7 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
+* May 19, 1999  Arnaldo Melo    __initfunc for wanpipe_init
 * Nov 28, 1997	Jaspreet Singh	Changed DRV_RELEASE to 1
 * Nov 10, 1997	Jaspreet Singh	Changed sti() to restore_flags();
 * Nov 06, 1997 	Jaspreet Singh	Changed DRV_VERSION to 4 and DRV_RELEASE to 0
@@ -42,6 +43,7 @@
 #include <linux/wanpipe.h>	/* WANPIPE common user API definitions */
 #include <asm/uaccess.h>	/* kernel <-> user copy */
 #include <asm/io.h>		/* phys_to_virt() */
+#include <linux/init.h>         /* __initfunc (when not using as a module) */
 
 
 /****** Defines & Macros ****************************************************/
@@ -122,7 +124,7 @@
 #ifdef MODULE
 int init_module (void)
 #else
-int wanpipe_init(void)
+__initfunc(int wanpipe_init(void))
 #endif
 {
 	int cnt, err = 0;
diff -u --recursive --new-file v2.3.5/linux/drivers/net/sk_mca.c linux/drivers/net/sk_mca.c
--- v2.3.5/linux/drivers/net/sk_mca.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/sk_mca.c	Mon Jun  7 14:34:46 1999
@@ -0,0 +1,1143 @@
+/* 
+net-3-driver for the SKNET MCA-based cards
+
+This is an extension to the Linux operating system, and is covered by the
+same Gnu Public License that covers that work.
+
+Copyright 1999 by Alfred Arnold (alfred@ccac.rwth-aachen.de, aarnold@elsa.de)
+
+This driver is based both on the 3C523 driver and the SK_G16 driver.
+
+paper sources:
+  'PC Hardware: Aufbau, Funktionsweise, Programmierung' by 
+  Hans-Peter Messmer for the basic Microchannel stuff
+  
+  'Linux Geraetetreiber' by Allesandro Rubini, Kalle Dalheimer
+  for help on Ethernet driver programming
+
+  'Ethernet/IEEE 802.3 Family 1992 World Network Data Book/Handbook' by AMD
+  for documentation on the AM7990 LANCE
+
+  'SKNET Personal Technisches Manual', Version 1.2 by Schneider&Koch
+  for documentation on the Junior board
+
+  'SK-NET MC2+ Technical Manual", Version 1.1 by Schneider&Koch for
+  documentation on the MC2 bord
+  
+  A big thank you to the S&K support for providing me so quickly with
+  documentation!
+
+  Also see http://www.syskonnect.com/
+
+  Missing things:
+
+  -> set debug level via ioctl instead of compile-time switches
+  -> I didn't follow the development of the 2.1.x kernels, so my
+     assumptions about which things changed with which kernel version 
+     are probably nonsense
+
+History:
+  May 16th, 1999
+  	startup
+  May 22st, 1999
+	added private structure, methods
+        begun building data structures in RAM
+  May 23nd, 1999
+	can receive frames, send frames
+  May 24th, 1999
+        modularized intialization of LANCE
+        loadable as module
+	still Tx problem :-(
+  May 26th, 1999
+  	MC2 works
+  	support for multiple devices
+  	display media type for MC2+
+  May 28th, 1999
+	fixed problem in GetLANCE leaving interrupts turned off
+        increase TX queue to 4 packets to improve send performance
+  May 29th, 1999
+	a few corrections in statistics, caught rcvr overruns 
+        reinitialization of LANCE/board in critical situations
+        MCA info implemented
+	implemented LANCE multicast filter
+  Jun 6th, 1999
+	additions for Linux 2.2
+
+ *************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/mca.h>
+#include <asm/processor.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#define _SK_MCA_DRIVER_
+#include "sk_mca.h"
+
+/* ------------------------------------------------------------------------
+ * global static data - not more since we can handle multiple boards and
+ * have to pack all state info into the device struct!
+ * ------------------------------------------------------------------------ */
+
+static char *MediaNames[Media_Count]=
+            {"10Base2", "10BaseT", "10Base5", "Unknown"};
+
+static unsigned char poly[] =
+       {1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0,
+        1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0};
+
+/* ------------------------------------------------------------------------
+ * private subfunctions
+ * ------------------------------------------------------------------------ */
+
+/* dump parts of shared memory - only needed during debugging */
+
+#ifdef DEBUG
+static void dumpmem(struct device *dev, u32 start, u32 len)
+{
+  int z;
+
+  for (z = 0; z < len; z++)
+  {
+    if ((z & 15) == 0)
+      printk("%04x:", z);
+    printk(" %02x", readb(dev->mem_start + start + z));
+    if ((z & 15) == 15)
+      printk("\n");
+  }
+}
+
+/* print exact time - ditto */
+
+static void PrTime(void)
+{
+  struct timeval tv;
+
+  do_gettimeofday(&tv);
+  printk("%9d:%06d: ", tv.tv_sec, tv.tv_usec);
+}
+#endif
+
+/* deduce resources out of POS registers */
+
+static void getaddrs(int slot, int junior, int *base, int *irq,
+                     skmca_medium *medium)
+{
+  u_char pos0, pos1, pos2;
+  
+  if (junior)
+  {
+    pos0 = mca_read_stored_pos(slot, 2);
+    *base = ((pos0 & 0x0e) << 13) + 0xc0000;
+    *irq = ((pos0 & 0x10) >> 4) + 10;
+    *medium = Media_Unknown;
+  }
+  else
+  {
+    /* reset POS 104 Bits 0+1 so the shared memory region goes to the
+       configured area between 640K and 1M.  Afterwards, enable the MC2.
+       I really don't know what rode SK to do this... */
+       
+    mca_write_pos(slot, 4, mca_read_stored_pos(slot, 4) & 0xfc);
+    mca_write_pos(slot, 2, mca_read_stored_pos(slot, 2) | 0x01);
+       
+    pos1 = mca_read_stored_pos(slot, 3);
+    pos2 = mca_read_stored_pos(slot, 4);
+    *base = ((pos1 & 0x07) << 14) + 0xc0000;
+    switch (pos2 & 0x0c)
+    {
+      case 0: *irq = 3; break;
+      case 4: *irq = 5; break;
+      case 8: *irq = 10; break;
+      case 12: *irq = 11; break;
+    }
+    *medium = (pos2 >> 6) & 3;
+  }
+}
+
+/* check for both cards:
+   When the MC2 is turned off, it was configured for more than 15MB RAM,
+   is disabled and won't get detected using the standard probe.  We
+   therefore have to scan the slots manually :-( */
+
+static int dofind(int *junior, int firstslot)
+{
+  int slot;
+  unsigned int id;
+
+  for (slot = firstslot; slot < MCA_MAX_SLOT_NR; slot++)
+  {
+    id = mca_read_stored_pos(slot, 0)
+       + (((unsigned int) mca_read_stored_pos(slot, 1)) << 8);
+       
+    *junior = 0;
+    if (id == SKNET_MCA_ID)
+      return slot;
+    *junior = 1;
+    if (id == SKNET_JUNIOR_MCA_ID)
+      return slot;
+  }
+  return MCA_NOTFOUND;
+}
+
+/* reset the whole board */
+
+static void ResetBoard(struct device *dev)
+{
+  skmca_priv *priv = (skmca_priv*) dev->priv;
+
+  writeb(CTRL_RESET_ON, priv->ctrladdr);
+  udelay(10);
+  writeb(CTRL_RESET_OFF, priv->ctrladdr);
+}
+
+/* set LANCE register - must be atomic */
+
+static void SetLANCE(struct device *dev, u16 addr, u16 value)
+{
+  skmca_priv *priv = (skmca_priv*) dev->priv;
+  unsigned long flags;
+
+  /* disable interrupts */
+
+  save_flags(flags);
+  cli();
+
+  /* wait until no transfer is pending */
+  
+  while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+
+  /* transfer register address to RAP */
+
+  writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_RAP, priv->ctrladdr);
+  writew(addr, priv->ioregaddr);
+  writeb(IOCMD_GO, priv->cmdaddr);
+  udelay(1);
+  while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+
+  /* transfer data to register */
+
+  writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_DATA, priv->ctrladdr);
+  writew(value, priv->ioregaddr);
+  writeb(IOCMD_GO, priv->cmdaddr);
+  udelay(1);
+  while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+
+  /* reenable interrupts */
+
+  restore_flags(flags);
+}
+
+/* get LANCE register */
+
+static u16 GetLANCE(struct device *dev, u16 addr)
+{
+  skmca_priv *priv = (skmca_priv*) dev->priv;
+  unsigned long flags;
+  unsigned int res;
+
+  /* disable interrupts */
+
+  save_flags(flags);
+  cli();
+
+  /* wait until no transfer is pending */
+
+  while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+
+  /* transfer register address to RAP */
+
+  writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_RAP, priv->ctrladdr);
+  writew(addr, priv->ioregaddr);
+  writeb(IOCMD_GO, priv->cmdaddr);
+  udelay(1);
+  while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+
+  /* transfer data from register */
+
+  writeb(CTRL_RESET_OFF | CTRL_RW_READ | CTRL_ADR_DATA, priv->ctrladdr);
+  writeb(IOCMD_GO, priv->cmdaddr);
+  udelay(1);
+  while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+  res = readw(priv->ioregaddr);
+
+  /* reenable interrupts */
+
+  restore_flags(flags);
+
+  return res;
+}
+
+/* build up descriptors in shared RAM */
+
+static void InitDscrs(struct device *dev)
+{
+  u32 bufaddr;
+
+  /* Set up Tx descriptors. The board has only 16K RAM so bits 16..23
+     are always 0. */
+
+  bufaddr = RAM_DATABASE;
+  {
+    LANCE_TxDescr descr;
+    int z;
+
+    for (z = 0; z < TXCOUNT; z++)
+    {
+      descr.LowAddr = bufaddr;
+      descr.Flags = 0;
+      descr.Len = 0xf000;
+      descr.Status = 0;
+      memcpy_toio(dev->mem_start + RAM_TXBASE + (z * sizeof(LANCE_TxDescr)),
+                  &descr, sizeof(LANCE_TxDescr));
+      memset_io(dev->mem_start + bufaddr, 0, RAM_BUFSIZE);
+      bufaddr += RAM_BUFSIZE;
+    }
+  }
+
+  /* do the same for the Rx descriptors */
+ 
+  {
+    LANCE_RxDescr descr;
+    int z;
+
+    for (z = 0; z < RXCOUNT; z++)
+    {
+      descr.LowAddr = bufaddr;
+      descr.Flags = RXDSCR_FLAGS_OWN;
+      descr.MaxLen = -RAM_BUFSIZE;
+      descr.Len = 0;
+      memcpy_toio(dev->mem_start + RAM_RXBASE + (z * sizeof(LANCE_RxDescr)),
+                  &descr, sizeof(LANCE_RxDescr));
+      memset_io(dev->mem_start + bufaddr, 0, RAM_BUFSIZE);
+      bufaddr += RAM_BUFSIZE;
+    }
+  }
+}
+
+/* calculate the hash bit position for a given multicast address
+   taken more or less directly from the AMD datasheet... */
+
+static void UpdateCRC(unsigned char *CRC, int bit)
+{
+  int j;
+
+  /* shift CRC one bit */
+
+  memmove(CRC + 1, CRC, 32 * sizeof(unsigned char));
+  CRC[0] = 0;
+
+  /* if bit XOR controlbit = 1, set CRC = CRC XOR polynomial */
+
+  if (bit ^ CRC[32])
+    for (j = 0; j < 32; j++)
+      CRC[j] ^= poly[j];
+}
+
+static unsigned int GetHash(char *address)
+{
+  unsigned char CRC[33];
+  int i, byte, hashcode;
+
+  /* a multicast address has bit 0 in the first byte set */
+
+  if ((address[0] & 1) == 0)
+    return -1;
+
+  /* initialize CRC */
+
+  memset(CRC, 1, sizeof(CRC));
+
+  /* loop through address bits */
+
+  for (byte = 0; byte < 6; byte++)
+    for (i = 0; i < 8; i++)
+      UpdateCRC(CRC, (address[byte] >> i) & 1);
+
+  /* hashcode is the 6 least significant bits of the CRC */
+
+  hashcode = 0;
+  for (i = 0; i < 6; i++)
+    hashcode = (hashcode << 1) + CRC[i];
+  return hashcode;
+}
+
+/* feed ready-built initialization block into LANCE */
+
+static void InitLANCE(struct device *dev)
+{
+  skmca_priv *priv = (skmca_priv*) dev->priv;
+
+  /* build up descriptors. */
+
+  InitDscrs(dev);
+
+  /* next RX descriptor to be read is the first one.  Since the LANCE
+     will start from the beginning after initialization, we have to 
+     reset out pointers too. */
+
+  priv->nextrx = 0;
+
+  /* no TX descriptors active */
+
+  priv->nexttxput = priv->nexttxdone = priv->txbusy = 0;
+
+  /* set up the LANCE bus control register - constant for SKnet boards */
+
+  SetLANCE(dev, LANCE_CSR3, CSR3_BSWAP_OFF | CSR3_ALE_LOW | CSR3_BCON_HOLD);
+
+  /* write address of initialization block into LANCE */
+
+  SetLANCE(dev, LANCE_CSR1, RAM_INITBASE & 0xffff);
+  SetLANCE(dev, LANCE_CSR2, (RAM_INITBASE >> 16) & 0xff);
+
+  /* we don't get ready until the LANCE has read the init block */
+
+  dev->tbusy = 1;
+
+  /* let LANCE read the initialization block.  LANCE is ready
+     when we receive the corresponding interrupt. */
+
+  SetLANCE(dev, LANCE_CSR0, CSR0_INEA | CSR0_INIT);
+}
+
+/* stop the LANCE so we can reinitialize it */
+
+static void StopLANCE(struct device *dev)
+{
+  /* can't take frames any more */
+
+  dev->tbusy = 1;
+
+  /* disable interrupts, stop it */
+
+  SetLANCE(dev, LANCE_CSR0, CSR0_STOP);
+}
+
+/* initialize card and LANCE for proper operation */
+
+static void InitBoard(struct device *dev)
+{
+  LANCE_InitBlock block;
+
+  /* Lay out the shared RAM - first we create the init block for the LANCE.
+     We do not overwrite it later because we need it again when we switch
+     promiscous mode on/off. */
+
+  block.Mode = 0;
+  if (dev->flags & IFF_PROMISC)
+    block.Mode |= LANCE_INIT_PROM;
+  memcpy(block.PAdr, dev->dev_addr, 6);
+  memset(block.LAdrF, 0, sizeof(block.LAdrF));
+  block.RdrP = (RAM_RXBASE & 0xffffff) | (LRXCOUNT << 29);
+  block.TdrP = (RAM_TXBASE & 0xffffff) | (LTXCOUNT << 29);
+
+  memcpy_toio(dev->mem_start + RAM_INITBASE, &block, sizeof(block));
+
+  /* initialize LANCE. Implicitly sets up other structures in RAM. */
+
+  InitLANCE(dev);
+}
+
+/* deinitialize card and LANCE */
+
+static void DeinitBoard(struct device *dev)
+{
+  /* stop LANCE */
+
+  StopLANCE(dev);
+
+  /* reset board */
+
+  ResetBoard(dev);
+}
+
+/* ------------------------------------------------------------------------
+ * interrupt handler(s)
+ * ------------------------------------------------------------------------ */
+
+/* LANCE has read initializazion block -> start it */
+
+static u16 irqstart_handler(struct device *dev, u16 oldcsr0)
+{
+  /* now we're ready to transmit */
+
+  dev->tbusy = 0;
+
+  /* reset IDON bit, start LANCE */
+
+  SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_IDON | CSR0_STRT);
+  return GetLANCE(dev, LANCE_CSR0);
+}
+
+/* receive interrupt */
+
+static u16 irqrx_handler(struct device *dev, u16 oldcsr0)
+{
+  skmca_priv *priv = (skmca_priv*) dev->priv;
+  LANCE_RxDescr descr;
+  unsigned int descraddr;
+
+  /* did we loose blocks due to a FIFO overrun ? */
+
+  if (oldcsr0 & CSR0_MISS)
+    priv->stat.rx_fifo_errors++;
+
+  /* run through queue until we reach a descriptor we do not own */
+
+  descraddr = RAM_RXBASE + (priv->nextrx * sizeof(LANCE_RxDescr));
+  while (1)
+  {
+    /* read descriptor */
+    memcpy_fromio(&descr, dev->mem_start + descraddr, sizeof(LANCE_RxDescr));
+ 
+    /* if we reach a descriptor we do not own, we're done */
+    if ((descr.Flags & RXDSCR_FLAGS_OWN) != 0)
+      break;
+
+#ifdef DEBUG
+    PrTime(); printk("Receive packet on descr %d len %d\n", priv->nextrx, descr.Len);
+#endif
+
+    /* erroneous packet ? */
+    if ((descr.Flags & RXDSCR_FLAGS_ERR) != 0)
+    {
+      priv->stat.rx_errors++;
+      if ((descr.Flags & RXDSCR_FLAGS_CRC) != 0)
+        priv->stat.rx_crc_errors++;
+      else if ((descr.Flags & RXDSCR_FLAGS_CRC) != 0)
+        priv->stat.rx_frame_errors++;
+      else if ((descr.Flags & RXDSCR_FLAGS_OFLO) != 0)
+        priv->stat.rx_fifo_errors++;
+    }
+
+    /* good packet ? */
+    else
+    {
+      struct sk_buff *skb;
+
+      skb = dev_alloc_skb(descr.Len + 2);
+      if (skb == NULL)
+        priv->stat.rx_dropped++;
+      else
+      {
+        memcpy_fromio(skb_put(skb, descr.Len),
+                      dev->mem_start + descr.LowAddr, descr.Len);
+        skb->dev = dev;
+        skb->protocol = eth_type_trans(skb, dev);
+        skb->ip_summed = CHECKSUM_NONE;
+        priv->stat.rx_packets++;
+#if LINUX_VERSION_CODE >= 0x020119       /* byte counters for >= 2.1.25 */
+        priv->stat.rx_bytes += descr.Len;
+#endif
+        netif_rx(skb);
+      }
+    }
+
+    /* give descriptor back to LANCE */
+    descr.Len = 0;
+    descr.Flags |= RXDSCR_FLAGS_OWN;
+
+    /* update descriptor in shared RAM */
+    memcpy_toio(dev->mem_start + descraddr, &descr, sizeof(LANCE_RxDescr));
+
+    /* go to next descriptor */
+    priv->nextrx++; descraddr += sizeof(LANCE_RxDescr);
+    if (priv->nextrx >= RXCOUNT)
+    {
+      priv->nextrx = 0;
+      descraddr = RAM_RXBASE;
+    }
+  }
+
+  /* reset RINT bit */
+
+  SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_RINT);
+  return GetLANCE(dev, LANCE_CSR0);
+}
+
+/* transmit interrupt */
+
+static u16 irqtx_handler(struct device *dev, u16 oldcsr0)
+{
+  skmca_priv *priv = (skmca_priv*) dev->priv;
+  LANCE_TxDescr descr;
+  unsigned int descraddr;
+
+  /* check descriptors at most until no busy one is left */
+
+  descraddr = RAM_TXBASE + (priv->nexttxdone * sizeof(LANCE_TxDescr));
+  while (priv->txbusy > 0)
+  {
+    /* read descriptor */
+    memcpy_fromio(&descr, dev->mem_start + descraddr, sizeof(LANCE_TxDescr));
+
+    /* if the LANCE still owns this one, we've worked out all sent packets */
+    if ((descr.Flags & TXDSCR_FLAGS_OWN) != 0)
+      break;
+
+#ifdef DEBUG
+    PrTime(); printk("Send packet done on descr %d\n", priv->nexttxdone);
+#endif
+
+    /* update statistics */
+    if ((descr.Flags & TXDSCR_FLAGS_ERR) == 0)
+    {
+      priv->stat.tx_packets++;
+#if LINUX_VERSION_CODE >= 0x020119       /* byte counters for >= 2.1.25 */
+      priv->stat.tx_bytes++;
+#endif
+    }
+    else
+    {
+      priv->stat.tx_errors++;
+      if ((descr.Status & TXDSCR_STATUS_UFLO) != 0)
+      {
+        priv->stat.tx_fifo_errors++;
+        InitLANCE(dev);
+      }
+      else if ((descr.Status & TXDSCR_STATUS_LCOL) != 0)
+        priv->stat.tx_window_errors++;
+      else if ((descr.Status & TXDSCR_STATUS_LCAR) != 0)
+        priv->stat.tx_carrier_errors++;
+      else if ((descr.Status & TXDSCR_STATUS_RTRY) != 0)
+        priv->stat.tx_aborted_errors++;
+    }
+
+    /* go to next descriptor */
+    priv->nexttxdone++;
+    descraddr += sizeof(LANCE_TxDescr);
+    if (priv->nexttxdone >= TXCOUNT)
+    {
+      priv->nexttxdone = 0;
+      descraddr = RAM_TXBASE;
+    }
+    priv->txbusy--;
+  }
+
+  /* reset TX interrupt bit */
+
+  SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_TINT);
+  oldcsr0 = GetLANCE(dev, LANCE_CSR0);
+
+  /* at least one descriptor is freed.  Therefore we can accept
+     a new one */
+
+  dev->tbusy = 0;
+
+  /* inform upper layers we're in business again */
+
+  mark_bh(NET_BH);
+
+  return oldcsr0;
+}
+
+/* general interrupt entry */
+
+static void irq_handler(int irq, void *device, struct pt_regs *regs)
+{
+  struct device *dev = (struct device*) device;
+  u16 csr0val;
+
+  /* read CSR0 to get interrupt cause */
+
+  csr0val = GetLANCE(dev, LANCE_CSR0);
+
+  /* in case we're not meant... */
+
+  if ((csr0val & CSR0_INTR) == 0)
+    return;
+
+  dev->interrupt = 1;
+
+  /* loop through the interrupt bits until everything is clear */
+
+  do
+  {
+    if ((csr0val & CSR0_IDON) != 0)
+      csr0val = irqstart_handler(dev, csr0val);
+    if ((csr0val & CSR0_RINT) != 0)
+      csr0val = irqrx_handler(dev, csr0val);
+    if ((csr0val & CSR0_TINT) != 0)
+      csr0val = irqtx_handler(dev, csr0val);
+  }
+  while ((csr0val & CSR0_INTR) != 0);
+
+  dev->interrupt = 0;
+}
+
+/* ------------------------------------------------------------------------
+ * driver methods
+ * ------------------------------------------------------------------------ */
+
+/* MCA info */
+
+static int skmca_getinfo(char *buf, int slot, void *d)
+{
+  int len = 0, i;
+  struct device *dev = (struct device*) d;
+  skmca_priv *priv;
+  
+  /* can't say anything about an uninitialized device... */
+
+  if (dev == NULL)
+    return len;
+  if (dev->priv == NULL)
+    return len;
+  priv = (skmca_priv*) dev->priv;
+
+  /* print info */
+
+  len += sprintf(buf + len, "IRQ: %d\n", priv->realirq);
+  len += sprintf(buf + len, "Memory: %#lx-%#lx\n", dev->mem_start,
+                 dev->mem_end - 1);
+  len += sprintf(buf + len, "Transceiver: %s\n", MediaNames[priv->medium]);
+  len += sprintf(buf + len, "Device: %s\n", dev->name);
+  len += sprintf(buf + len, "MAC address:");
+  for (i = 0; i < 6; i ++ )
+    len += sprintf( buf+len, " %02x", dev->dev_addr[i] );
+  buf[len++] = '\n';
+  buf[len] = 0;
+
+  return len;
+}
+
+/* open driver.  Means also initialization and start of LANCE */
+
+static int skmca_open(struct device *dev)
+{
+  int result;
+  skmca_priv *priv = (skmca_priv*) dev->priv;
+
+  /* register resources - only necessary for IRQ */
+  result = request_irq(priv->realirq, irq_handler, SA_SHIRQ | SA_SAMPLE_RANDOM,
+                       "sk_mca", dev);
+  if (result != 0)
+  {
+    printk("%s: failed to register irq %d\n", dev->name, dev->irq);
+    return result;
+  }
+  dev->irq = priv->realirq;
+
+  /* set up the card and LANCE */
+  InitBoard(dev);
+
+#ifdef MODULE
+  MOD_INC_USE_COUNT;
+#endif
+
+  return 0;
+}
+
+/* close driver.  Shut down board and free allocated resources */
+
+static int skmca_close(struct device *dev)
+{
+  /* turn off board */
+  DeinitBoard(dev);
+
+  /* release resources */
+  if (dev->irq != 0)
+    free_irq(dev->irq, dev);
+  dev->irq = 0;
+
+#ifdef MODULE
+  MOD_DEC_USE_COUNT;
+#endif
+
+  return 0;
+}
+
+/* transmit a block. */
+
+static int skmca_tx(struct sk_buff *skb, struct device *dev)
+{
+  skmca_priv *priv = (skmca_priv*) dev->priv;
+  LANCE_TxDescr descr;
+  unsigned int address;
+  int tmplen, retval = 0;
+  unsigned long flags;
+
+  /* if we get called with a NULL descriptor, the Ethernet layer thinks 
+     our card is stuck an we should reset it.  We'll do this completely: */
+
+  if (skb == NULL)
+  {
+    DeinitBoard(dev);
+    InitBoard(dev);
+    return 0; /* don't try to free the block here ;-) */
+  }
+
+  /* is there space in the Tx queue ? If no, the upper layer gave us a
+     packet in spite of us not being ready and is really in trouble.
+     We'll do the dropping for him: */
+  if (priv->txbusy >= TXCOUNT)
+  {
+    priv->stat.tx_dropped++;
+    retval = -EIO;
+    goto tx_done;
+  }
+
+  /* get TX descriptor */
+  address = RAM_TXBASE + (priv->nexttxput * sizeof(LANCE_TxDescr));
+  memcpy_fromio(&descr, dev->mem_start + address, sizeof(LANCE_TxDescr));
+
+  /* enter packet length as 2s complement - assure minimum length */
+  tmplen = skb->len;
+  if (tmplen < 60)
+    tmplen = 60;
+  descr.Len = 65536 - tmplen;
+
+  /* copy filler into RAM - in case we're filling up... 
+     we're filling a bit more than necessary, but that doesn't harm
+     since the buffer is far larger... */
+  if (tmplen > skb->len)
+  {
+    char *fill = "NetBSD is a nice OS too! ";
+    unsigned int destoffs = 0, l = strlen(fill);
+
+    while (destoffs < tmplen)
+    {
+      memcpy_toio(dev->mem_start + descr.LowAddr + destoffs, fill, l);
+      destoffs += l;
+    }
+  }
+
+  /* do the real data copying */
+  memcpy_toio(dev->mem_start + descr.LowAddr, skb->data, skb->len);
+
+  /* hand descriptor over to LANCE - this is the first and last chunk */
+  descr.Flags = TXDSCR_FLAGS_OWN | TXDSCR_FLAGS_STP | TXDSCR_FLAGS_ENP;
+
+#ifdef DEBUG
+  PrTime(); printk("Send packet on descr %d len %d\n", priv->nexttxput, skb->len);
+#endif
+
+  /* one more descriptor busy */
+  save_flags(flags);
+  cli();
+  priv->nexttxput++;
+  if (priv->nexttxput >= TXCOUNT)
+    priv->nexttxput = 0;
+  priv->txbusy++;
+  dev->tbusy = (priv->txbusy >= TXCOUNT);
+
+  /* write descriptor back to RAM */
+  memcpy_toio(dev->mem_start + address, &descr, sizeof(LANCE_TxDescr));
+
+  /* if no descriptors were active, give the LANCE a hint to read it
+     immediately */
+
+  if (priv->txbusy == 0)
+    SetLANCE(dev, LANCE_CSR0, CSR0_INEA | CSR0_TDMD);
+
+  restore_flags(flags);
+
+tx_done:
+
+  /* When did that change exactly ? */
+
+#if LINUX_VERSION_CODE >= 0x020200
+  dev_kfree_skb(skb);
+#else
+  dev_kfree_skb(skb, FREE_WRITE);
+#endif
+  return retval;
+}
+
+/* return pointer to Ethernet statistics */
+
+static struct enet_statistics *skmca_stats(struct device *dev)
+{
+  skmca_priv *priv = (skmca_priv*) dev->priv;
+
+  return &(priv->stat);
+}
+
+/* we don't support runtime reconfiguration, since am MCA card can
+   be unambigously identified by its POS registers. */
+
+static int skmca_config(struct device *dev, struct ifmap *map)
+{
+  return 0;
+}
+
+/* switch receiver mode.  We use the LANCE's multicast filter to prefilter
+   multicast addresses. */
+
+static void skmca_set_multicast_list(struct device *dev)
+{
+  LANCE_InitBlock block;
+
+  /* first stop the LANCE... */
+  StopLANCE(dev);
+
+  /* ...then modify the initialization block... */
+  memcpy_fromio(&block, dev->mem_start + RAM_INITBASE, sizeof(block));
+  if (dev->flags & IFF_PROMISC)
+    block.Mode |= LANCE_INIT_PROM;
+  else
+    block.Mode &= ~LANCE_INIT_PROM;
+
+  if (dev->flags & IFF_ALLMULTI)   /* get all multicasts */
+  {
+    memset(block.LAdrF, 8, 0xff);
+  }
+  else                             /* get selected/no multicasts */
+  {
+    struct dev_mc_list *mptr;
+    int code;
+
+    memset(block.LAdrF, 8, 0x00);
+    for (mptr = dev->mc_list; mptr != NULL; mptr = mptr->next)
+    {
+      code = GetHash(mptr->dmi_addr);
+      block.LAdrF[(code >> 3) & 7] |= 1 << (code & 7);
+    }
+  }
+
+  memcpy_toio(dev->mem_start + RAM_INITBASE, &block, sizeof(block));
+
+  /* ...then reinit LANCE with the correct flags */
+  InitLANCE(dev);
+}
+
+/* ------------------------------------------------------------------------
+ * hardware check
+ * ------------------------------------------------------------------------ */
+
+#ifdef MODULE
+static int startslot; /* counts through slots when probing multiple devices */
+#else
+#define startslot 0   /* otherwise a dummy, since there is only eth0 in-kern*/
+#endif
+
+int skmca_probe(struct device *dev)
+{
+  int force_detect = 0;
+  int junior, slot, i;
+  int base = 0, irq = 0;
+  skmca_priv *priv;
+  skmca_medium medium;
+
+  /* can't work without an MCA bus ;-) */
+  
+  if (MCA_bus == 0)
+    return ENODEV;
+    
+  /* start address of 1 --> forced detection */
+  
+  if (dev->mem_start == 1)
+    force_detect = 1;
+
+  /* search through slots */
+  
+  if (dev != NULL)
+  {
+    base = dev->mem_start;
+    irq = dev->irq;
+  }
+  slot = dofind(&junior, startslot);
+
+  while (slot != -1)
+  {
+    /* deduce card addresses */
+
+    getaddrs(slot, junior, &base, &irq, &medium);
+
+#if 0
+    /* this should work, but it doesn't with 2.2.9 :-( 
+       somehow 'mca_is_adapter_used()' is missing in kernel syms... */
+#if LINUX_VERSION_CODE >= 0x020200
+    /* slot already in use ? */
+
+    if (mca_is_adapter_used(slot))
+    {
+      slot = dofind(&junior, slot + 1);
+      continue;
+    }
+#endif
+#endif
+
+    /* were we looking for something different ? */
+    
+    if ((dev->irq != 0) || (dev->mem_start != 0))
+    {
+      if ((dev->irq != 0) && (dev->irq != irq))
+      {
+        slot = dofind(&junior, slot + 1);
+        continue;
+      }
+      if ((dev->mem_start != 0) && (dev->mem_start != base))
+      {
+        slot = dofind(&junior, slot + 1);
+        continue;
+      }
+    }
+  
+    /* found something that matches */
+    
+    break;
+  }
+
+  /* nothing found ? */
+  
+  if (slot == -1)
+    return ((base != 0) || (irq != 0)) ? ENXIO : ENODEV;
+    
+  /* make procfs entries */
+    
+  if (junior)
+    mca_set_adapter_name(slot, "SKNET junior MC2 Ethernet Adapter");
+  else
+    mca_set_adapter_name(slot, "SKNET MC2+ Ethernet Adapter");
+  mca_set_adapter_procfn(slot, (MCA_ProcFn) skmca_getinfo, dev);
+
+#if LINUX_VERSION_CODE >= 0x020200
+  mca_mark_as_used(slot);
+#endif
+  
+  /* announce success */
+  printk("%s: SKNet %s adapter found in slot %d\n", dev->name, 
+         junior ? "Junior MC2" : "MC2+", slot + 1);
+
+  /* allocate structure */
+  priv = dev->priv = (skmca_priv*) kmalloc(sizeof(skmca_priv), GFP_KERNEL);
+  priv->slot = slot;
+  priv->macbase = base + 0x3fc0;
+  priv->ioregaddr = base + 0x3ff0;
+  priv->ctrladdr = base + 0x3ff2;
+  priv->cmdaddr = base + 0x3ff3;
+  priv->realirq = irq;
+  priv->medium = medium;
+  memset(&(priv->stat), 0, sizeof(struct enet_statistics));
+  
+  /* set base + irq for this device (irq not allocated so far) */
+  dev->irq = 0;
+  dev->mem_start = base;
+  dev->mem_end = base + 0x4000;
+ 
+  /* set methods */
+  dev->open = skmca_open;
+  dev->stop = skmca_close;
+  dev->set_config = skmca_config;
+  dev->hard_start_xmit = skmca_tx;
+  dev->do_ioctl = NULL;
+  dev->get_stats = skmca_stats;
+  dev->set_multicast_list = skmca_set_multicast_list;
+  dev->flags |= IFF_MULTICAST;
+
+  /* generic setup */
+  ether_setup(dev);
+  dev->interrupt = 0;
+  dev->tbusy = 0;
+  dev->start = 0;
+ 
+  /* copy out MAC address */
+  for (i = 0; i < 6; i++)
+    dev->dev_addr[i] = readb(priv->macbase + (i << 1));
+
+  /* print config */
+  printk("%s: IRQ %d, memory %#lx-%#lx, "
+         "MAC address %02x:%02x:%02x:%02x:%02x:%02x.\n",
+         dev->name, priv->realirq,  dev->mem_start, dev->mem_end - 1,
+         dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+         dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+  printk("%s: %s medium\n", dev->name, MediaNames[priv->medium]);
+
+  /* reset board */
+
+  ResetBoard(dev);
+
+#ifdef MODULE
+  startslot = slot + 1;
+#endif
+
+  return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * modularization support
+ * ------------------------------------------------------------------------ */
+
+#ifdef MODULE
+
+#define DEVMAX 5
+
+static char NameSpace[8 * DEVMAX];
+static struct device moddevs[DEVMAX] =
+       {{NameSpace +  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+        {NameSpace +  8, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+        {NameSpace + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+        {NameSpace + 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+        {NameSpace + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe}};
+
+int irq=0;
+int io=0;
+
+int init_module(void)
+{
+  int z, res;
+
+  startslot = 0;
+  for (z = 0; z < DEVMAX; z++)
+  {
+    strcpy(moddevs[z].name, "     ");
+    res = register_netdev(moddevs + z);
+    if (res != 0)
+      return (z > 0) ? 0 : -EIO;
+  }
+
+  return 0;
+}
+
+void cleanup_module(void)
+{
+  struct device *dev;
+  skmca_priv *priv;
+  int z;
+
+  if (MOD_IN_USE)
+  {
+    printk("cannot unload, module in use\n");
+    return;
+  }
+
+  for (z = 0; z < DEVMAX; z++)
+  {
+    dev = moddevs + z;
+    if (dev->priv != NULL)
+    {
+      priv = (skmca_priv*) dev->priv;
+      DeinitBoard(dev);
+      if (dev->irq != 0)
+        free_irq(dev->irq, dev);
+      dev->irq = 0;
+      unregister_netdev(dev);
+#if LINUX_VERSION_CODE >= 0x020200
+      mca_mark_as_unused(priv->slot);
+#endif
+      mca_set_adapter_procfn(priv->slot, NULL, NULL);
+      kfree_s(dev->priv, sizeof(skmca_priv));
+      dev->priv = NULL;
+    }
+  }
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.3.5/linux/drivers/net/sk_mca.h linux/drivers/net/sk_mca.h
--- v2.3.5/linux/drivers/net/sk_mca.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/sk_mca.h	Mon Jun  7 14:34:46 1999
@@ -0,0 +1,174 @@
+#ifndef _SK_MCA_INCLUDE_
+#define _SK_MCA_INCLUDE_
+
+#ifdef _SK_MCA_DRIVER_
+
+/* Adapter ID's */
+#define SKNET_MCA_ID 0x6afd
+#define SKNET_JUNIOR_MCA_ID 0x6be9
+
+/* media enumeration - defined in a way that it fits onto the MC2+'s
+   POS registers... */
+   
+typedef enum {Media_10Base2, Media_10BaseT,
+              Media_10Base5, Media_Unknown, Media_Count} skmca_medium;
+
+/* private structure */
+typedef struct
+        {
+          unsigned int slot;       /* MCA-Slot-#                       */
+          unsigned int macbase;    /* base address of MAC address PROM */
+          unsigned int ioregaddr;  /* address of I/O-register (Lo)     */
+          unsigned int ctrladdr;   /* address of control/stat register */
+          unsigned int cmdaddr;    /* address of I/O-command register  */
+          int nextrx;              /* index of next RX descriptor to
+                                      be read                          */
+          int nexttxput;           /* index of next free TX descriptor */
+          int nexttxdone;          /* index of next TX descriptor to 
+                                      be finished                      */
+          int txbusy;              /* # of busy TX descriptors         */
+          struct enet_statistics stat; /* packet statistics            */
+          int realirq;             /* memorizes actual IRQ, even when 
+                                      currently not allocated          */
+          skmca_medium medium;     /* physical cannector               */
+        } skmca_priv;
+
+/* card registers: control/status register bits */
+
+#define CTRL_ADR_DATA      0      /* Bit 0 = 0 ->access data register  */
+#define CTRL_ADR_RAP       1      /* Bit 0 = 1 ->access RAP register   */
+#define CTRL_RW_WRITE      0      /* Bit 1 = 0 ->write register        */
+#define CTRL_RW_READ       2      /* Bit 1 = 1 ->read register         */
+#define CTRL_RESET_ON      0      /* Bit 3 = 0 ->reset board           */
+#define CTRL_RESET_OFF     8      /* Bit 3 = 1 ->no reset of board     */
+
+#define STAT_ADR_DATA      0      /* Bit 0 of ctrl register read back  */
+#define STAT_ADR_RAP       1
+#define STAT_RW_WRITE      0      /* Bit 1 of ctrl register read back  */
+#define STAT_RW_READ       2
+#define STAT_RESET_ON      0      /* Bit 3 of ctrl register read back  */
+#define STAT_RESET_OFF     8
+#define STAT_IRQ_ACT       0      /* interrupt pending                 */
+#define STAT_IRQ_NOACT     16     /* no interrupt pending              */
+#define STAT_IO_NOBUSY     0      /* no transfer busy                  */
+#define STAT_IO_BUSY       32     /* transfer busy                     */
+
+/* I/O command register bits */
+
+#define IOCMD_GO           128    /* Bit 7 = 1 -> start register xfer  */
+
+/* LANCE registers */
+
+#define LANCE_CSR0         0      /* Status/Control                    */
+
+#define CSR0_ERR           0x8000 /* general error flag                */
+#define CSR0_BABL          0x4000 /* transmitter timeout               */
+#define CSR0_CERR          0x2000 /* collision error                   */
+#define CSR0_MISS          0x1000 /* lost Rx block                     */
+#define CSR0_MERR          0x0800 /* memory access error               */
+#define CSR0_RINT          0x0400 /* receiver interrupt                */
+#define CSR0_TINT          0x0200 /* transmitter interrupt             */
+#define CSR0_IDON          0x0100 /* initialization done               */
+#define CSR0_INTR          0x0080 /* general interrupt flag            */
+#define CSR0_INEA          0x0040 /* interrupt enable                  */
+#define CSR0_RXON          0x0020 /* receiver enabled                  */
+#define CSR0_TXON          0x0010 /* transmitter enabled               */
+#define CSR0_TDMD          0x0008 /* force transmission now            */
+#define CSR0_STOP          0x0004 /* stop LANCE                        */
+#define CSR0_STRT          0x0002 /* start LANCE                       */
+#define CSR0_INIT          0x0001 /* read initialization block         */          
+
+#define LANCE_CSR1         1      /* addr bit 0..15 of initialization  */
+#define LANCE_CSR2         2      /*          16..23 block             */
+
+#define LANCE_CSR3         3      /* Bus control                       */
+#define CSR3_BCON_HOLD     0      /* Bit 0 = 0 -> BM1,BM0,HOLD         */
+#define CSR3_BCON_BUSRQ    1      /* Bit 0 = 1 -> BUSAK0,BYTE,BUSRQ    */
+#define CSR3_ALE_HIGH      0      /* Bit 1 = 0 -> ALE asserted high    */
+#define CSR3_ALE_LOW       2      /* Bit 1 = 1 -> ALE asserted low     */
+#define CSR3_BSWAP_OFF     0      /* Bit 2 = 0 -> no byte swap         */
+#define CSR3_BSWAP_ON      0      /* Bit 2 = 1 -> byte swap            */
+
+/* LANCE structures */
+
+typedef struct                    /* LANCE initialization block        */
+        {
+          u16 Mode;               /* mode flags                        */
+          u8  PAdr[6];            /* MAC address                       */
+          u8  LAdrF[8];           /* Multicast filter                  */
+          u32 RdrP;               /* Receive descriptor                */
+          u32 TdrP;               /* Transmit descriptor               */
+        } LANCE_InitBlock;
+        
+/* Mode flags init block */
+
+#define LANCE_INIT_PROM    0x8000 /* enable promiscous mode            */
+#define LANCE_INIT_INTL    0x0040 /* internal loopback                 */
+#define LANCE_INIT_DRTY    0x0020 /* disable retry                     */
+#define LANCE_INIT_COLL    0x0010 /* force collision                   */
+#define LANCE_INIT_DTCR    0x0008 /* disable transmit CRC              */
+#define LANCE_INIT_LOOP    0x0004 /* loopback                          */
+#define LANCE_INIT_DTX     0x0002 /* disable transmitter               */
+#define LANCE_INIT_DRX     0x0001 /* disable receiver                  */
+
+typedef struct                    /* LANCE Tx descriptor               */
+        {
+          u16 LowAddr;            /* bit 0..15 of address              */
+          u16 Flags;              /* bit 16..23 of address + Flags     */
+          u16 Len;                /* 2s complement of packet length    */
+          u16 Status;             /* Result of transmission            */
+        } LANCE_TxDescr;
+
+#define TXDSCR_FLAGS_OWN   0x8000 /* LANCE owns descriptor             */
+#define TXDSCR_FLAGS_ERR   0x4000 /* summary error flag                */
+#define TXDSCR_FLAGS_MORE  0x1000 /* more than one retry needed?       */
+#define TXDSCR_FLAGS_ONE   0x0800 /* one retry?                        */
+#define TXDSCR_FLAGS_DEF   0x0400 /* transmission deferred?            */
+#define TXDSCR_FLAGS_STP   0x0200 /* first packet in chain?            */
+#define TXDSCR_FLAGS_ENP   0x0100 /* last packet in chain?             */
+
+#define TXDSCR_STATUS_BUFF 0x8000 /* buffer error?                     */
+#define TXDSCR_STATUS_UFLO 0x4000 /* silo underflow during transmit?   */
+#define TXDSCR_STATUS_LCOL 0x1000 /* late collision?                   */
+#define TXDSCR_STATUS_LCAR 0x0800 /* loss of carrier?                  */
+#define TXDSCR_STATUS_RTRY 0x0400 /* retry error?                      */
+        
+typedef struct                    /* LANCE Rx descriptor               */
+        {
+          u16 LowAddr;            /* bit 0..15 of address              */
+          u16 Flags;              /* bit 16..23 of address + Flags     */
+          u16 MaxLen;             /* 2s complement of buffer length    */
+          u16 Len;                /* packet length                     */
+        } LANCE_RxDescr;
+
+#define RXDSCR_FLAGS_OWN   0x8000 /* LANCE owns descriptor             */
+#define RXDSCR_FLAGS_ERR   0x4000 /* summary error flag                */
+#define RXDSCR_FLAGS_FRAM  0x2000 /* framing error flag                */
+#define RXDSCR_FLAGS_OFLO  0x1000 /* FIFO overflow?                    */
+#define RXDSCR_FLAGS_CRC   0x0800 /* CRC error?                        */
+#define RXDSCR_FLAGS_BUFF  0x0400 /* buffer error?                     */
+#define RXDSCR_FLAGS_STP   0x0200 /* first packet in chain?            */
+#define RXDCSR_FLAGS_ENP   0x0100 /* last packet in chain?             */
+
+/* RAM layout */
+
+#define TXCOUNT            4      /* length of TX descriptor queue     */
+#define LTXCOUNT           2      /* log2 of it                        */
+#define RXCOUNT            4      /* length of RX descriptor queue     */
+#define LRXCOUNT           2      /* log2 of it                        */
+
+#define RAM_INITBASE       0      /* LANCE init block                  */
+#define RAM_TXBASE         24     /* Start of TX descriptor queue      */
+#define RAM_RXBASE         \
+(RAM_TXBASE + (TXCOUNT * 8))      /* Start of RX descriptor queue      */
+#define RAM_DATABASE       \
+(RAM_RXBASE + (RXCOUNT * 8))      /* Start of data area for frames     */
+#define RAM_BUFSIZE        1580   /* max. frame size - should never be
+                                     reached                           */
+
+#endif /* _SK_MCA_DRIVER_ */
+
+extern int skmca_probe(struct device *);
+
+
+#endif /* _SK_MCA_INCLUDE_ */
\ No newline at end of file
diff -u --recursive --new-file v2.3.5/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c
--- v2.3.5/linux/drivers/pci/oldproc.c	Fri May 14 18:55:20 1999
+++ linux/drivers/pci/oldproc.c	Wed Jun  9 16:59:15 1999
@@ -102,6 +102,7 @@
 	DEVICE( DEC,		DEC_21150,	"DC21150"),
 	DEVICE( DEC,		DEC_21152,	"DC21152"),
 	DEVICE( DEC,		DEC_21153,	"DC21153"),
+	DEVICE( DEC,		DEC_21154,	"DC21154"),
 	DEVICE( CIRRUS,		CIRRUS_7548,	"GD 7548"),
 	DEVICE( CIRRUS,		CIRRUS_5430,	"GD 5430"),
 	DEVICE( CIRRUS,		CIRRUS_5434_4,	"GD 5434"),
@@ -202,6 +203,8 @@
 	DEVICE( MOTOROLA,	MOTOROLA_MPC105,"MPC105 Eagle"),
 	DEVICE( MOTOROLA,	MOTOROLA_MPC106,"MPC106 Grackle"),
 	DEVICE( MOTOROLA,	MOTOROLA_RAVEN,	"Raven"),
+	DEVICE( MOTOROLA,	MOTOROLA_FALCON,"Falcon"),
+	DEVICE( MOTOROLA,	MOTOROLA_CPX8216,"CPX8216"),
 	DEVICE( PROMISE,        PROMISE_20246,	"IDE UltraDMA/33"),
 	DEVICE(	PROMISE,	PROMISE_20262,	"IDE UltraDMA/66"),
 	DEVICE( PROMISE,	PROMISE_5300,	"DC5030"),
@@ -467,6 +470,7 @@
 	DEVICE( PICTUREL,	PICTUREL_PCIVST,"PCIVST"),
 	DEVICE( NVIDIA_SGS,	NVIDIA_SGS_RIVA128,	"Riva 128"),
 	DEVICE( CBOARDS,	CBOARDS_DAS1602_16,"DAS1602/16"),
+	DEVICE( MOTOROLA_OOPS,	MOTOROLA_FALCON,"Falcon"),
 	DEVICE( SYMPHONY,	SYMPHONY_101,	"82C101"),
 	DEVICE( TEKRAM,		TEKRAM_DC290,	"DC-290"),
 	DEVICE( 3DLABS,		3DLABS_300SX,	"GLINT 300SX"),
@@ -536,9 +540,11 @@
  	DEVICE( INTEL,		INTEL_82450GX,	"82450GX Orion P6"),
 	DEVICE(	KTI,		KTI_ET32P2,	"ET32P2"),
 	DEVICE( ADAPTEC,	ADAPTEC_7810,	"AIC-7810 RAID"),
+	DEVICE( ADAPTEC,	ADAPTEC_7821,	"AIC-7860"),
 	DEVICE( ADAPTEC,	ADAPTEC_7850,	"AIC-7850"),
 	DEVICE( ADAPTEC,	ADAPTEC_7855,	"AIC-7855"),
 	DEVICE( ADAPTEC,	ADAPTEC_5800,	"AIC-5800"),
+	DEVICE( ADAPTEC,	ADAPTEC_3860,	"AIC-7860"),
 	DEVICE( ADAPTEC,	ADAPTEC_7860,	"AIC-7860"),
 	DEVICE( ADAPTEC,	ADAPTEC_7861,	"AIC-7861"),
 	DEVICE( ADAPTEC,	ADAPTEC_7870,	"AIC-7870"),
@@ -552,13 +558,26 @@
 	DEVICE( ADAPTEC,	ADAPTEC_7882,	"AIC-7882U"),
 	DEVICE( ADAPTEC,	ADAPTEC_7883,	"AIC-7883U"),
 	DEVICE( ADAPTEC,	ADAPTEC_7884,	"AIC-7884U"),
+	DEVICE( ADAPTEC,	ADAPTEC_7885,	"AIC-7885U"),
+	DEVICE( ADAPTEC,	ADAPTEC_7886,	"AIC-7886U"),
+	DEVICE( ADAPTEC,	ADAPTEC_7887,	"AIC-7887U"),
+	DEVICE( ADAPTEC,	ADAPTEC_7888,	"AIC-7888U"),
 	DEVICE( ADAPTEC,	ADAPTEC_1030,	"ABA-1030 DVB receiver"),
 	DEVICE( ADAPTEC2,	ADAPTEC2_2940U2,"AHA-2940U2"),
-	DEVICE( ADAPTEC2,	ADAPTEC2_78902,	"AIC-7890/1"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_2930U2,"AHA-2930U2"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7890B,	"AIC-7890/1"),
 	DEVICE( ADAPTEC2,	ADAPTEC2_7890,	"AIC-7890/1"),
 	DEVICE( ADAPTEC2,	ADAPTEC2_3940U2,"AHA-3940U2"),
 	DEVICE( ADAPTEC2,	ADAPTEC2_3950U2D,"AHA-3950U2D"),
 	DEVICE( ADAPTEC2,	ADAPTEC2_7896,	"AIC-7896/7"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7892A,	"AIC-7892"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7892B,	"AIC-7892"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7892D,	"AIC-7892"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7892P,	"AIC-7892"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7899A,	"AIC-7899"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7899B,	"AIC-7899"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7899D,	"AIC-7899"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7899P,	"AIC-7899"),
   	DEVICE( ATRONICS,	ATRONICS_2015,	"IDE-2015PL"),
 	DEVICE( TIGERJET,	TIGERJET_300,	"Tiger300 ISDN"),
 	DEVICE( ARK,		ARK_STING,	"Stingray"),
@@ -676,6 +695,8 @@
 	      case PCI_CLASS_SERIAL_USB:		return "USB Controller";
 	      case PCI_CLASS_SERIAL_FIBER:		return "Fiber Channel";
 
+	      case PCI_CLASS_HOT_SWAP_CONTROLLER:	return "Hot Swap Controller";
+
 	      default:					return "Unknown class";
 	}
 }
@@ -715,6 +736,7 @@
 	      case PCI_VENDOR_ID_OAK: 		return "OAK";
 	      case PCI_VENDOR_ID_WINBOND2:	return "Winbond";
 	      case PCI_VENDOR_ID_MOTOROLA:	return "Motorola";
+	      case PCI_VENDOR_ID_MOTOROLA_OOPS:	return "Motorola";
 	      case PCI_VENDOR_ID_PROMISE:	return "Promise Technology";
 	      case PCI_VENDOR_ID_N9:		return "Number Nine";
 	      case PCI_VENDOR_ID_UMC:		return "UMC";
diff -u --recursive --new-file v2.3.5/linux/drivers/sbus/char/Config.in linux/drivers/sbus/char/Config.in
--- v2.3.5/linux/drivers/sbus/char/Config.in	Mon May 31 22:28:06 1999
+++ linux/drivers/sbus/char/Config.in	Wed Jun  9 14:44:25 1999
@@ -12,4 +12,10 @@
   tristate 'Bidirectional parallel port support (EXPERIMENTAL)' CONFIG_SUN_BPP
   tristate 'Videopix Frame Grabber (EXPERIMENTAL)' CONFIG_SUN_VIDEOPIX
   tristate 'Aurora Multiboard 1600se (EXPERIMENTAL)' CONFIG_SUN_AURORA
+
+  # XXX Why don't we do "source drivers/char/Config.in" somewhere?
+  if [ "$CONFIG_PCI" = "y" ]; then
+    define_bool CONFIG_APM_RTC_IS_GMT y		# no shit
+    bool 'PC-style RTC' CONFIG_RTC
+  fi
 fi
diff -u --recursive --new-file v2.3.5/linux/drivers/sbus/char/aurora.c linux/drivers/sbus/char/aurora.c
--- v2.3.5/linux/drivers/sbus/char/aurora.c	Mon May 31 22:28:06 1999
+++ linux/drivers/sbus/char/aurora.c	Wed Jun  9 14:44:25 1999
@@ -53,6 +53,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/tqueue.h>
+#include <linux/delay.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -60,7 +61,6 @@
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <asm/bitops.h>
-#include <asm/delay.h>
 #include <asm/kdebug.h>
 #include <asm/sbus.h>
 #include <asm/uaccess.h>
diff -u --recursive --new-file v2.3.5/linux/drivers/sbus/char/bpp.c linux/drivers/sbus/char/bpp.c
--- v2.3.5/linux/drivers/sbus/char/bpp.c	Fri May 14 18:55:21 1999
+++ linux/drivers/sbus/char/bpp.c	Wed Jun  9 14:44:25 1999
@@ -997,7 +997,7 @@
       instances[idx].enhanced = 0;
       instances[idx].direction = 0;
       instances[idx].mode = COMPATIBILITY;
-      instances[idx].wait_queue = 0;
+      init_waitqueue_head(&instances[idx].wait_queue);
       instances[idx].run_length = 0;
       instances[idx].run_flag = 0;
       init_timer(&instances[idx].timer_list);
diff -u --recursive --new-file v2.3.5/linux/drivers/sbus/char/pcikbd.c linux/drivers/sbus/char/pcikbd.c
--- v2.3.5/linux/drivers/sbus/char/pcikbd.c	Mon May 17 09:55:22 1999
+++ linux/drivers/sbus/char/pcikbd.c	Wed Jun  9 14:44:25 1999
@@ -1,8 +1,8 @@
-/* $Id: pcikbd.c,v 1.29 1999/05/16 13:47:53 ecd Exp $
+/* $Id: pcikbd.c,v 1.30 1999/06/03 15:02:36 davem Exp $
  * pcikbd.c: Ultra/AX PC keyboard support.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
- * JavaStation(MrCoffee) support by Pete A. Zaitcev.
+ * JavaStation support by Pete A. Zaitcev.
  *
  * This code is mainly put together from various places in
  * drivers/char, please refer to these sources for credits
@@ -30,13 +30,16 @@
 #include <asm/io.h>
 #include <asm/uaccess.h>
 
-#ifdef __sparc_v9__
-#define	PCI_KB_NAME	"kb_ps2"
-#define PCI_MS_NAME	"kdmouse"
-#else
-#define PCI_KB_NAME	"keyboard"
-#define PCI_MS_NAME	"mouse"
-#endif
+/*
+ * Different platforms provide different permutations of names.
+ * AXi - kb_ps2, kdmouse.
+ * MrCoffee - keyboard, mouse.
+ * Espresso - keyboard, kdmouse.
+ */
+#define	PCI_KB_NAME1	"kb_ps2"
+#define PCI_KB_NAME2	"keyboard"
+#define PCI_MS_NAME1	"kdmouse"
+#define PCI_MS_NAME2	"mouse"
 
 #include "pcikbd.h"
 #include "sunserial.h"
@@ -502,7 +505,8 @@
 			for_each_ebusdev(edev, ebus) {
 				if(!strcmp(edev->prom_name, "8042")) {
 					for_each_edevchild(edev, child) {
-                                                if (!strcmp(child->prom_name, PCI_KB_NAME))
+                                                if (strcmp(child->prom_name, PCI_KB_NAME1) == 0 ||
+						    strcmp(child->prom_name, PCI_KB_NAME2) == 0)
 							goto found;
 					}
 				}
@@ -513,12 +517,14 @@
 
 found:
 		pcikbd_iobase = child->base_address[0];
+#ifdef __sparc_v9__
 		if (check_region(pcikbd_iobase, sizeof(unsigned long))) {
 			printk("8042: can't get region %lx, %d\n",
 			       pcikbd_iobase, (int)sizeof(unsigned long));
 			return;
 		}
 		request_region(pcikbd_iobase, sizeof(unsigned long), "8042 controller");
+#endif
 
 		pcikbd_irq = child->irqs[0];
 		if (request_irq(pcikbd_irq, &pcikbd_interrupt,
@@ -548,7 +554,7 @@
 	 * XXX: my 3.1.3 PROM does not give me the beeper node for the audio
 	 *      auxio register, though I know it is there... (ecd)
 	 *
-	 * Both JE1 & MrCoffe have no beeper. How about Krups? --zaitcev
+	 * JavaStations appear not to have beeper. --zaitcev
 	 */
 	if (!edev)
 		pcibeep_iobase = (pcikbd_iobase & ~(0xffffff)) | 0x722000;
@@ -575,7 +581,6 @@
 }
 
 
-
 /*
  * Here begins the Mouse Driver.
  */
@@ -955,7 +960,8 @@
 			for_each_ebusdev(edev, ebus) {
 				if(!strcmp(edev->prom_name, "8042")) {
 					for_each_edevchild(edev, child) {
-							if (!strcmp(child->prom_name, PCI_MS_NAME))
+							if (strcmp(child->prom_name, PCI_MS_NAME1) == 0 ||
+							    strcmp(child->prom_name, PCI_MS_NAME2) == 0)
 							goto found;
 					}
 				}
@@ -1023,7 +1029,7 @@
 
 __initfunc(int ps2kbd_probe(unsigned long *memory_start))
 {
-	int pnode, enode, node, dnode;
+	int pnode, enode, node, dnode, xnode;
 	int kbnode = 0, msnode = 0, bnode = 0;
 	int devices = 0;
 	char prop[128];
@@ -1103,18 +1109,20 @@
 			 * For each '8042' on this EBus...
 			 */
 			while (node) {
+				dnode = prom_getchild(node);
+
 				/*
 				 * Does it match?
 				 */
-				dnode = prom_getchild(node);
-				dnode = prom_searchsiblings(dnode, PCI_KB_NAME);
-				if (dnode == kbnode) {
+				if ((xnode = prom_searchsiblings(dnode, PCI_KB_NAME1)) == kbnode) {
+					++devices;
+				} else if ((xnode = prom_searchsiblings(dnode, PCI_KB_NAME2)) == kbnode) {
 					++devices;
 				}
 
-				dnode = prom_getchild(node);
-				dnode = prom_searchsiblings(dnode, PCI_MS_NAME);
-				if (dnode == msnode) {
+				if ((xnode = prom_searchsiblings(dnode, PCI_MS_NAME1)) == msnode) {
+					++devices;
+				} else if ((xnode = prom_searchsiblings(dnode, PCI_MS_NAME2)) == msnode) {
 					++devices;
 				}
 
diff -u --recursive --new-file v2.3.5/linux/drivers/sbus/char/rtc.c linux/drivers/sbus/char/rtc.c
--- v2.3.5/linux/drivers/sbus/char/rtc.c	Sun Oct  4 10:22:44 1998
+++ linux/drivers/sbus/char/rtc.c	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-/* $Id: rtc.c,v 1.13 1998/08/26 10:29:44 davem Exp $
+/* $Id: rtc.c,v 1.14 1999/06/03 15:02:38 davem Exp $
  *
  * Linux/SPARC Real Time Clock Driver
  * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
@@ -107,6 +107,7 @@
 
 static int rtc_open(struct inode *inode, struct file *file)
 {
+
 	if (rtc_busy)
 		return -EBUSY;
 
@@ -144,14 +145,20 @@
 #ifdef MODULE
 int init_module(void)
 #else
-__initfunc(int rtc_init(void))
+__initfunc(int rtc_sun_init(void))
 #endif
 {
 	int error;
 
+	if (mstk48t02_regs == 0) {
+		/* This diagnostic is a debugging aid... But a useful one. */
+		printk(KERN_ERR "rtc: no Mostek in this computer\n");
+		return -ENODEV;
+	}
+
 	error = misc_register(&rtc_dev);
 	if (error) {
-		printk(KERN_ERR "rtc: unable to get misc minor\n");
+		printk(KERN_ERR "rtc: unable to get misc minor for Mostek\n");
 		return error;
 	}
 
diff -u --recursive --new-file v2.3.5/linux/drivers/sbus/char/su.c linux/drivers/sbus/char/su.c
--- v2.3.5/linux/drivers/sbus/char/su.c	Fri May 14 18:55:21 1999
+++ linux/drivers/sbus/char/su.c	Wed Jun  9 14:44:25 1999
@@ -1,8 +1,8 @@
-/* $Id: su.c,v 1.19 1999/05/12 11:15:14 davem Exp $
+/* $Id: su.c,v 1.20 1999/06/03 15:02:40 davem Exp $
  * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
- * Coypright (C) 1998  Pete Zaitcev   (zaitcev@metabyte.com)
+ * Copyright (C) 1998-1999  Pete Zaitcev   (zaitcev@metabyte.com)
  *
  * This is mainly a variation of drivers/char/serial.c,
  * credits go to authors mentioned therein.
@@ -92,6 +92,11 @@
 int su_serial_console_init(void);
 #endif
 
+enum su_type { SU_PORT_NONE, SU_PORT_MS, SU_PORT_KBD, SU_PORT_PORT };
+static char *su_typev[] = { "???", "mouse", "kbd", "serial" };
+
+#define SU_PROPSIZE	128
+
 /*
  * serial.c saves memory when it allocates async_info upon first open.
  * We have parts of state structure together because we do call startup
@@ -107,8 +112,7 @@
 	int		 line;
 	int		 cflag;
 
-	int		 kbd_node;
-	int		 ms_node;
+	enum su_type	 port_type;	/* Hookup type: e.g. mouse */
 	int		 port_node;
 
 	char		 name[16];
@@ -145,6 +149,18 @@
 	unsigned long		last_active;	/* For async_struct, to be */
 };
 
+/*
+ * Scan status structure.
+ * "prop" is a local variable but it eats stack to keep it in each
+ * stack frame of a recursive procedure.
+ */
+struct su_probe_scan {
+	int msnode, kbnode;	/* PROM nodes for mouse and keyboard */
+	int msx, kbx;		/* minors for mouse and keyboard */
+	int devices;		/* scan index */
+	char prop[SU_PROPSIZE];
+};
+
 static char *serial_name = "PCIO serial driver";
 static char serial_version[16];
 
@@ -223,8 +239,6 @@
 	return 0;
 }
 
-#ifdef __sparc_v9__
-
 static inline
 unsigned int su_inb(struct su_struct *info, unsigned long offset)
 {
@@ -234,20 +248,7 @@
 static inline void
 su_outb(struct su_struct *info, unsigned long offset, int value)
 {
-	outb(value, info->port + offset);
-}
-
-#else
-
-static inline
-unsigned int su_inb(struct su_struct *info, unsigned long offset)
-{
-	return (unsigned int)(*(volatile unsigned char *)(info->port + offset));
-}
-
-static inline void
-su_outb(struct su_struct *info, unsigned long offset, int value)
-{
+#ifndef __sparc_v9__
 	/*
 	 * MrCoffee has weird schematics: IRQ4 & P10(?) pins of SuperIO are
 	 * connected with a gate then go to SlavIO. When IRQ4 goes tristated
@@ -257,10 +258,9 @@
 	 * This problem is similar to what Alpha people suffer, see serial.c.
 	 */
 	if (offset == UART_MCR) value |= UART_MCR_OUT2;
-	*(volatile unsigned char *)(info->port + offset) = value;
-}
-
 #endif
+	outb(value, info->port + offset);
+}
 
 #define serial_in(info, off)		su_inb(info, off)
 #define serial_inp(info, off)		su_inb(info, off)
@@ -348,7 +348,7 @@
 
 	do {
 		ch = serial_inp(info, UART_RX);
-		if (info->kbd_node) {
+		if (info->port_type == SU_PORT_KBD) {
 			if(ch == SUNKBD_RESET) {
                         	l1a_state.kbd_id = 1;
                         	l1a_state.l1_down = 0;
@@ -529,7 +529,7 @@
 			    (status & UART_MSR_DCD))
 				hardpps();
 #endif
-	}
+		}
 		if (status & UART_MSR_DCTS)
 			icount->cts++;
 		wake_up_interruptible(&info->delta_msr_wait);
@@ -775,7 +775,7 @@
 	/*
 	 * Allocate the IRQ if necessary
 	 */
-	if (info->kbd_node || info->ms_node) {
+	if (info->port_type != SU_PORT_PORT) {
 		retval = request_irq(info->irq, su_kbd_ms_interrupt,
 				     SA_SHIRQ, info->name, info);
 	} else {
@@ -956,7 +956,7 @@
 	int		bits;
 	unsigned long	flags;
 
-	if (!info->kbd_node && !info->ms_node) {
+	if (info->port_type == SU_PORT_PORT) {
 		if (!info->tty || !info->tty->termios)
 			return;
 		if (!info->port)
@@ -1133,9 +1133,9 @@
 	struct su_struct *info = su_table;
 	int lsr;
 
-	if (!info->kbd_node)
+	if (!info->port_type != SU_PORT_KBD)
 		++info;
-	if (!info)
+	if (!info->port_type != SU_PORT_KBD)
 		return;
 
 	do {
@@ -1151,9 +1151,9 @@
 {
 	struct su_struct *info = su_table;
 
-	if (!info->ms_node)
+	if (!info->port_type != SU_PORT_MS)
 		++info;
-	if (!info)
+	if (!info->port_type != SU_PORT_MS)
 		return;
 
 	info->cflag &= ~(CBAUDEX | CBAUD);
@@ -2202,9 +2202,9 @@
 
 /*
  * ---------------------------------------------------------------------
- * su_init() and friends
+ * su_XXX_init() and friends
  *
- * su_init() is called at boot-time to initialize the serial driver.
+ * su_XXX_init() is called at boot-time to initialize the serial driver.
  * ---------------------------------------------------------------------
  */
 
@@ -2215,7 +2215,7 @@
  */
 __initfunc(static __inline__ void show_su_version(void))
 {
-	char *revision = "$Revision: 1.19 $";
+	char *revision = "$Revision: 1.20 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -2226,8 +2226,8 @@
 }
 
 /*
- * This routine is called by su_init() to initialize a specific serial
- * port.  It determines what type of UART chip this serial port is
+ * This routine is called by su_{serial|kbd_ms}_init() to initialize a specific
+ * serial port.  It determines what type of UART chip this serial port is
  * using: 8250, 16450, 16550, 16550A.  The important question is
  * whether or not this UART is a 16550A, since this will determine
  * whether or not we can use its FIFO features.
@@ -2236,38 +2236,42 @@
 autoconfig(struct su_struct *info)
 {
 	unsigned char status1, status2, scratch, scratch2;
-#ifdef __sparc_v9__
 	struct linux_ebus_device *dev = 0;
 	struct linux_ebus *ebus;
-#else
+#ifndef __sparc_v9__
 	struct linux_prom_registers reg0;
 #endif
 	unsigned long flags;
 
-#ifdef __sparc_v9__
+	if (!info->port_node || !info->port_type)
+		return;
+
+	/*
+	 * First we look for Ebus-bases su's
+	 */
 	for_each_ebus(ebus) {
 		for_each_ebusdev(dev, ebus) {
-			if (!strncmp(dev->prom_name, "su", 2)) {
-				if (dev->prom_node == info->kbd_node)
-					goto ebus_done;
-				if (dev->prom_node == info->ms_node)
-					goto ebus_done;
+			if (dev->prom_node == info->port_node) {
+				info->port = dev->base_address[0];
+#ifdef __sparc_v9__
+				if (check_region(info->port, 8))
+					return;
+#endif
+				info->irq = dev->irqs[0];
+				goto ebus_done;
 			}
 		}
 	}
-ebus_done:
-	if (!dev)
-		return;
-
-	info->port = dev->base_address[0];
-	if (check_region(info->port, 8))
-		return;
 
-	info->irq = dev->irqs[0];
+#ifdef __sparc_v9__
+	/*
+	 * Not on Ebus, bailing.
+	 */
+	return;
 #else
-	if (!info->port_node)
-		return;
-
+	/*
+	 * Not on Ebus, must be OBIO.
+	 */
 	if (prom_getproperty(info->port_node, "reg",
 	    (char *)&reg0, sizeof(reg0)) == -1) {
 		prom_printf("su: no \"reg\" property\n");
@@ -2279,21 +2283,24 @@
 		prom_printf("su: cannot map\n");
 		return;
 	}
+
 	/*
-	 * There is no intr property on MrCoffee, so hardwire it. Krups?
+	 * There is no intr property on MrCoffee, so hardwire it.
 	 */
 	info->irq = IRQ_4M(13);
 #endif
 
-#ifdef DEBUG_SERIAL_OPEN
-	printk("Found 'su' at %016lx IRQ %s\n", dev->base_address[0],
-	       __irq_itoa(dev->irqs[0]));
+ebus_done:
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("Found 'su' at %016lx IRQ %s\n", info->port,
+		__irq_itoa(info->irq));
 #endif
 
 	info->magic = SERIAL_MAGIC;
 
 	save_flags(flags); cli();
-	
+
 	/*
 	 * Do a simple existence test first; if we fail this, there's
 	 * no point trying anything else.
@@ -2312,17 +2319,20 @@
 		return;		/* We failed; there's nothing here */
 	}
 
-#if 0 /* P3: This does not work on MrCoffee. OUT2 is 0x80 - should work... */
 	scratch = serial_inp(info, UART_MCR);
 	serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch);
 	serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A);
 	status1 = serial_inp(info, UART_MSR) & 0xF0;
 	serial_outp(info, UART_MCR, scratch);
 	if (status1 != 0x90) {
+		/*
+		 * This code fragment used to fail, now it fixed itself.
+		 * We keep the printout for a case.
+		 */
+		printk("su: loopback returned status 0x%02x\n", status1);
 		restore_flags(flags);
 		return;
 	} 
-#endif
 
 	scratch2 = serial_in(info, UART_LCR);
 	serial_outp(info, UART_LCR, 0xBF);	/* set up for StarTech test */
@@ -2389,10 +2399,7 @@
 		return;
 	}
 
-	if (info->kbd_node || info->ms_node)
-		sprintf(info->name, "su(%s)", info->ms_node ? "mouse" : "kbd");
-	else
-		strcpy(info->name, "su(serial)");
+	sprintf(info->name, "su(%s)", su_typev[info->port_type]);
 
 #ifdef __sparc_v9__
 	request_region(info->port, 8, info->name);
@@ -2494,13 +2501,16 @@
 		info->tqueue.routine = do_softint;
 		info->tqueue.data = info;
 		info->cflag = serial_driver.init_termios.c_cflag;
+		init_waitqueue_head(&info->open_wait);
+		init_waitqueue_head(&info->close_wait);
+		init_waitqueue_head(&info->delta_msr_wait);
 
 		autoconfig(info);
 		if (info->type == PORT_UNKNOWN)
 			continue;
 
-		printk(KERN_INFO "%s at %16lx (irq = %s) is a %s\n",
-		       info->name, info->port, __irq_itoa(info->irq),
+		printk(KERN_INFO "%s at 0x%lx (tty %d irq %s) is a %s\n",
+		       info->name, (long)info->port, i, __irq_itoa(info->irq),
 		       uart_config[info->type].name);
 	}
 
@@ -2519,11 +2529,15 @@
 		info->type = PORT_UNKNOWN;
 		info->baud_base = BAUD_BASE;
 
-		if (info->kbd_node)
+		if (info->port_type == SU_PORT_KBD)
 			info->cflag = B1200 | CS8 | CLOCAL | CREAD;
 		else
 			info->cflag = B4800 | CS8 | CLOCAL | CREAD;
 
+		init_waitqueue_head(&info->open_wait);
+		init_waitqueue_head(&info->close_wait);
+		init_waitqueue_head(&info->delta_msr_wait);
+
 		autoconfig(info);
 		if (info->type == PORT_UNKNOWN)
 			continue;
@@ -2533,7 +2547,7 @@
 		       uart_config[info->type].name);
 
 		startup(info);
-		if (info->kbd_node)
+		if (info->port_type == SU_PORT_KBD)
 			keyboard_zsinit(su_put_char_kbd);
 		else
 			sun_mouse_zsinit();
@@ -2541,154 +2555,126 @@
 	return 0;
 }
 
-__initfunc(int su_probe (unsigned long *memory_start))
+/*
+ * We got several platforms which present 'su' in different parts
+ * of device tree. 'su' may be found under obio, ebus, isa and pci.
+ * We walk over the tree and find them wherever PROM hides them.
+ */
+__initfunc(void su_probe_any(struct su_probe_scan *t, int sunode))
 {
-	struct su_struct *info = su_table;
-        int node, enode, tnode, sunode;
-	int kbnode = 0, msnode = 0;
-	int devices = 0;
-	char prop[128];
+	struct su_struct *info;
 	int len;
 
-	/*
-	 * Find su on MrCoffee. We return OK code if find any.
-	 * Then su_init finds every one and initializes them.
-	 * We do this early because MrCoffee got no aliases.
-	 */
-	node = prom_getchild(prom_root_node);
-	if ((node = prom_searchsiblings(node, "obio")) != 0) {
-		if ((sunode = prom_getchild(node)) != 0) {
-			if ((sunode = prom_searchsiblings(sunode, "su")) != 0) {
-				info->port_node = sunode;
-#ifdef CONFIG_SERIAL_CONSOLE
-				/*
-				 * Console must be initiated after the generic
-				 * initialization.
-				 * sunserial_setinitfunc inverts order, so
-				 * call this before next one.
-				 */
-				sunserial_setinitfunc(memory_start,
-						      su_serial_console_init);
-#endif
-        			sunserial_setinitfunc(memory_start,
-						      su_serial_init);
-				return 0;
+	if (t->devices >= NR_PORTS) return;
+
+	for (; sunode != 0; sunode = prom_getsibling(sunode)) {
+		len = prom_getproperty(sunode, "name", t->prop, SU_PROPSIZE);
+		if (len <= 1) continue;		/* Broken PROM node */
+		if (strncmp(t->prop, "su", len) == 0 ||
+		    strncmp(t->prop, "serial", len) == 0 ||
+		    strncmp(t->prop, "su_pnp", len) == 0) {
+			info = &su_table[t->devices];
+			if (t->kbnode != 0 && sunode == t->kbnode) {
+				t->kbx = t->devices;
+				info->port_type = SU_PORT_KBD;
+			} else if (t->msnode != 0 && sunode == t->msnode) {
+				t->msx = t->devices;
+				info->port_type = SU_PORT_MS;
+			} else {
+				info->port_type = SU_PORT_PORT;
 			}
+			info->port_node = sunode;
+			++t->devices;
+		} else {
+			su_probe_any(t, prom_getchild(sunode));
 		}
 	}
+}
+
+__initfunc(int su_probe (unsigned long *memory_start))
+{
+	int node;
+	int len;
+	struct su_probe_scan scan;
+
+	/*
+	 * First, we scan the tree.
+	 */
+	scan.devices = 0;
+	scan.msx = -1;
+	scan.kbx = -1;
+	scan.kbnode = 0;
+	scan.msnode = 0;
 
 	/*
 	 * Get the nodes for keyboard and mouse from 'aliases'...
 	 */
         node = prom_getchild(prom_root_node);
 	node = prom_searchsiblings(node, "aliases");
-	if (!node)
-		return -ENODEV;
+	if (node != 0) {
 
-	len = prom_getproperty(node, "keyboard", prop, sizeof(prop));
-	if (len > 0) {
-		prop[len] = 0;
-		kbnode = prom_finddevice(prop);
-	}
-	if (!kbnode)
-		return -ENODEV;
+		len = prom_getproperty(node, "keyboard", scan.prop,SU_PROPSIZE);
+		if (len > 0) {
+			scan.prop[len] = 0;
+			scan.kbnode = prom_finddevice(scan.prop);
+		}
 
-	len = prom_getproperty(node, "mouse", prop, sizeof(prop));
-	if (len > 0) {
-		prop[len] = 0;
-		msnode = prom_finddevice(prop);
+		len = prom_getproperty(node, "mouse", scan.prop, SU_PROPSIZE);
+		if (len > 0) {
+			scan.prop[len] = 0;
+			scan.msnode = prom_finddevice(scan.prop);
+		}
 	}
-	if (!msnode)
-		return -ENODEV;
 
-	/*
-	 * Find matching EBus nodes...
-	 */
-        node = prom_getchild(prom_root_node);
-	if ((node = prom_searchsiblings(node, "pci")) == 0) {
-		return -ENODEV;		/* Plain sparc */
-	}
+	su_probe_any(&scan, prom_getchild(prom_root_node));
 
 	/*
-	 * Check for SUNW,sabre on Ultra 5/10/AXi.
-	 */
-	len = prom_getproperty(node, "model", prop, sizeof(prop));
-	if ((len > 0) && !strncmp(prop, "SUNW,sabre", len)) {
-        	node = prom_getchild(node);
-		node = prom_searchsiblings(node, "pci");
+	 * Second, we process the special case of keyboard and mouse.
+	 *
+	 * Currently if we got keyboard and mouse hooked to "su" ports
+	 * we do not use any possible remaining "su" as a serial port.
+	 * Thus, we ignore values of .msx and .kbx, then compact ports.
+	 * Those who want to address this issue need to merge
+	 * su_serial_init() and su_ms_kbd_init().
+	 */
+	if (scan.msx != -1 && scan.kbx != -1) {
+		su_table[0].port_type = SU_PORT_MS;
+		su_table[0].port_node = scan.msnode;
+		su_table[1].port_type = SU_PORT_KBD;
+		su_table[1].port_node = scan.kbnode;
+
+        	sunserial_setinitfunc(memory_start, su_kbd_ms_init);
+        	rs_ops.rs_change_mouse_baud = su_change_mouse_baud;
+		sunkbd_setinitfunc(memory_start, sun_kbd_init);
+		kbd_ops.compute_shiftstate = sun_compute_shiftstate;
+		kbd_ops.setledstate = sun_setledstate;
+		kbd_ops.getledstate = sun_getledstate;
+		kbd_ops.setkeycode = sun_setkeycode;
+		kbd_ops.getkeycode = sun_getkeycode;
+#ifdef CONFIG_PCI
+		sunkbd_install_keymaps(memory_start, sun_key_maps,
+		    sun_keymap_count, sun_func_buf, sun_func_table,
+		    sun_funcbufsize, sun_funcbufleft,
+		    sun_accent_table, sun_accent_table_size);
+#endif
+		return 0;
+	}
+	if (scan.msx != -1 || scan.kbx != -1) {
+		printk("su_probe: cannot match keyboard and mouse, confused\n");
+		return -ENODEV;
 	}
 
+	if (scan.devices == 0)
+		return -ENODEV;
+
+#ifdef CONFIG_SERIAL_CONSOLE
 	/*
-	 * For each PCI bus...
+	 * Console must be initiated after the generic initialization.
+	 * sunserial_setinitfunc inverts order, so call this before next one.
 	 */
-	while (node) {
-		enode = prom_getchild(node);
-		enode = prom_searchsiblings(enode, "ebus");
-
-		/*
-		 * For each EBus on this PCI...
-		 */
-		while (enode) {
-			sunode = prom_getchild(enode);
-			tnode = prom_searchsiblings(sunode, "su");
-			if (!tnode)
-				tnode = prom_searchsiblings(sunode, "su_pnp");
-			sunode = tnode;
-
-			/*
-			 * For each 'su' on this EBus...
-			 */
-			while (sunode) {
-				/*
-				 * Does it match?
-				 */
-				if (sunode == kbnode) {
-					info->kbd_node = sunode;
-					++info;
-					++devices;
-				}
-				if (sunode == msnode) {
-					info->ms_node = sunode;
-					++info;
-					++devices;
-				}
-
-				/*
-				 * Found everything we need?
-				 */
-				if (devices == 2)
-					goto found;
-
-				sunode = prom_getsibling(sunode);
-				tnode = prom_searchsiblings(sunode, "su");
-				if (!tnode)
-					tnode = prom_searchsiblings(sunode,
-								    "su_pnp");
-				sunode = tnode;
-			}
-			enode = prom_getsibling(enode);
-			enode = prom_searchsiblings(enode, "ebus");
-		}
-		node = prom_getsibling(node);
-		node = prom_searchsiblings(node, "pci");
-	}
-	return -ENODEV;
-
-found:
-        sunserial_setinitfunc(memory_start, su_kbd_ms_init);
-        rs_ops.rs_change_mouse_baud = su_change_mouse_baud;
-	sunkbd_setinitfunc(memory_start, sun_kbd_init);
-	kbd_ops.compute_shiftstate = sun_compute_shiftstate;
-	kbd_ops.setledstate = sun_setledstate;
-	kbd_ops.getledstate = sun_getledstate;
-	kbd_ops.setkeycode = sun_setkeycode;
-	kbd_ops.getkeycode = sun_getkeycode;
-#ifdef CONFIG_PCI
-	sunkbd_install_keymaps(memory_start, sun_key_maps, sun_keymap_count,
-			       sun_func_buf, sun_func_table,
-			       sun_funcbufsize, sun_funcbufleft,
-			       sun_accent_table, sun_accent_table_size);
+	sunserial_setinitfunc(memory_start, su_serial_console_init);
 #endif
+       	sunserial_setinitfunc(memory_start, su_serial_init);
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.5/linux/drivers/sbus/char/vfc_dev.c linux/drivers/sbus/char/vfc_dev.c
--- v2.3.5/linux/drivers/sbus/char/vfc_dev.c	Sun Mar  7 10:43:40 1999
+++ linux/drivers/sbus/char/vfc_dev.c	Wed Jun  9 14:44:25 1999
@@ -129,9 +129,9 @@
 int init_vfc_devstruct(struct vfc_dev *dev, int instance) 
 {
 	dev->instance=instance;
-	dev->device_lock_sem=MUTEX;
+	init_MUTEX(&dev->device_lock_sem);
 	dev->control_reg=0;
-	dev->poll_wait=NULL;
+	init_waitqueue_head(&dev->poll_wait);
 	dev->busy=0;
 	return 0;
 }
@@ -203,7 +203,6 @@
 {
 	struct vfc_debug_inout inout;
 	unsigned char *buffer;
-	int ret;
 
 	if(!capable(CAP_SYS_ADMIN)) return -EPERM;
 
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.3.5/linux/drivers/scsi/Config.in	Thu Apr 29 11:53:41 1999
+++ linux/drivers/scsi/Config.in	Wed Jun  9 16:59:15 1999
@@ -25,10 +25,8 @@
 dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
 dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
 if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
-    bool '   Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N
-    if [ "$CONFIG_OVERRIDE_CMDS" != "n" ]; then
-      int  '   Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 24
-    fi
+    bool '   Enable Tagged Command Queueing (TCQ) by default' CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+    int  '   Maximum number of TCQ commands per device' CONFIG_AIC7XXX_CMDS_PER_DEVICE 8
     bool '   Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
     int  '   Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5
 fi
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/README.aic7xxx linux/drivers/scsi/README.aic7xxx
--- v2.3.5/linux/drivers/scsi/README.aic7xxx	Mon Jan  4 12:57:44 1999
+++ linux/drivers/scsi/README.aic7xxx	Wed Jun  9 16:59:15 1999
@@ -17,6 +17,10 @@
     AHA-274xT               
     AHA-2842
     AHA-2910B               
+    AHA-2920C
+    AHA-2930
+    AHA-2930U
+    AHA-2930U2
     AHA-2940               
     AHA-2940W              
     AHA-2940U              
@@ -77,8 +81,8 @@
     Adaptec Cards
     ----------------------------
     AHA-2920 (Only the cards that use the Future Domain chipset are not
-              supported, any 2920 cards based on Adaptec AIC chipsets are
-	      supported)
+              supported, any 2920 cards based on Adaptec AIC chipsets,
+	      such as the 2920C, are supported)
     AAA-13x Raid Adapters
     AAA-113x Raid Port Card
 
@@ -108,7 +112,7 @@
     Jess Johnson    jester@frenzy.com
       (AIC7xxx FAQ author)
     Doug Ledford    dledford@redhat.com
-      (Current Linux aic7xxx-5.x.x Driver/Patch/FTP/FAQ maintainer)
+      (Current Linux aic7xxx-5.x.x Driver/Patch/FTP maintainer)
     
     Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original
     author of the driver. John has since retired from the project. Thanks
@@ -325,11 +329,12 @@
 	list and someone can help you out.
 
     "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to disable
-        tagged queueing on specific devices.  As of driver version 5.1.8, we
-        now globally enable tagged queueing by default.  In order to
-        disable tagged queueing for certian devices at boot time, a user may
-        use this boot param.  The driver will then parse this message out
-        and disable the specific device entries that are present based upon
+        or enable Tagged Command Queueing (TCQ) on specific devices.  As of
+	driver version 5.1.11, TCQ is now either on or off by default
+	according to the setting you choose during the make config process.
+	In order to en/disable TCQ for certian devices at boot time, a user
+	may use this boot param.  The driver will then parse this message out
+        and en/disable the specific device entries that are present based upon
         the value given.  The param line is parsed in the following manner:
 
           { - first instance indicates the start of this parameter values
@@ -419,10 +424,10 @@
     see this documentation, you need to use one of the advanced configuration
     programs (menuconfig and xconfig).  If you are using the "make menuconfig"
     method of configuring your kernel, then you would simply highlight the
-    option in question and hit the F1 key.  If you are using the "make xconfig"
-    method of configuring your kernel, then simply click on the help button next
-    to the option you have questions about.  The help information from the
-    Configure.help file will then get automatically displayed.
+    option in question and hit the ? key.  If you are using the "make xconfig"
+    method of configuring your kernel, then simply click on the help button
+    next to the option you have questions about.  The help information from
+    the Configure.help file will then get automatically displayed.
 
   /proc support
   ------------------------------
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/aic7xxx/aic7xxx.reg linux/drivers/scsi/aic7xxx/aic7xxx.reg
--- v2.3.5/linux/drivers/scsi/aic7xxx/aic7xxx.reg	Thu Jan  7 09:24:00 1999
+++ linux/drivers/scsi/aic7xxx/aic7xxx.reg	Wed Jun  9 16:59:15 1999
@@ -214,6 +214,25 @@
 }
 
 /*
+ * Option Mode Register (Alternate Mode) (p. 5-198)
+ * This register is used to set certain options on Ultra3 based chips.
+ * The chip must be in alternate mode (bit ALT_MODE in SFUNCT must be set)
+ */
+register OPTIONMODE {
+	address			0x008
+	access_mode RW
+	bit	AUTORATEEN	0x80
+	bit	AUTOACKEN	0x40
+	bit	ATNMGMNTEN	0x20
+	bit	BUSFREEREV	0x10
+	bit	EXPPHASEDIS	0x08
+	bit	SCSIDATL_IMGEN	0x04
+	bit	AUTO_MSGOUT_DE	0x02
+	bit	DIS_MSGIN_DUALEDGE	0x01
+}
+
+
+/*
  * Clear SCSI Interrupt 0 (p. 3-20)
  * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
  */
@@ -285,7 +304,13 @@
 	address			0x00d
 	access_mode RO
 	bit	OVERRUN		0x80
+	bit	SHVALID		0x40
+	bit	WIDE_RES	0x20
 	bit	EXP_ACTIVE	0x10	/* SCSI Expander Active */
+	bit	CRCVALERR	0x08	/* CRC Value Error */
+	bit	CRCENDERR	0x04	/* CRC End Error */
+	bit	CRCREQERR	0x02	/* CRC REQ Error */
+	bit	DUAL_EDGE_ERROR	0x01	/* Invalid pins for Dual Edge phase */
 	mask	SFCNT		0x1f
 }
 
@@ -709,6 +734,7 @@
 	bit	SQPARERR	0x08
 	bit	ILLOPCODE	0x04
 	bit	ILLSADDR	0x02
+	bit	DSCTMOUT	0x02	/* Ultra3 only */
 	bit	ILLHADDR	0x01
 }
 
@@ -788,6 +814,17 @@
 }
 
 /*
+ * SCSIDATL IMAGE Register (p. 5-104)
+ * Write to this register also go to SCSIDATL but this register will preserve
+ * the data for later reading as long as the SCSIDATL_IMGEN bit in the
+ * OPTIONMODE register is set.
+ */
+register SCSIDATL_IMG {
+	address			0x09c
+	access_mode RW
+}
+
+/*
  * Queue Out FIFO (p. 3-61)
  * Queue of SCBs that have completed and await the host
  */
@@ -797,6 +834,21 @@
 }
 
 /*
+ * CRC Control 1 Register (p. 5-105)
+ * Control bits for the Ultra 160/m CRC facilities
+ */
+register CRCCONTROL1 {
+	address			0x09d
+	access_mode RW
+	bit	CRCONSEEN	0x80 /* CRC ON Single Edge ENable */
+	bit	CRCVALCHKEN	0x40 /* CRC Value Check Enable */
+	bit	CRCENDCHKEN	0x20 /* CRC End Check Enable */
+	bit	CRCREQCHKEN	0x10
+	bit	TARGCRCENDEN	0x08 /* Enable End CRC transfer when target */
+	bit	TARGCRCCNTEN	0x40 /* Enable CRC transfer when target */
+}
+
+/*
  * Queue Out Count (p. 3-61)
  * Number of queued SCBs in the Out FIFO
  */
@@ -806,11 +858,27 @@
 }
 
 /*
+ * SCSI Phase Register (p. 5-106)
+ * Current bus phase
+ */
+register SCSIPHASE {
+	address			0x09e
+	access_mode RO
+	bit	SP_STATUS		0x20
+	bit	SP_COMMAND		0x10
+	bit	SP_MSG_IN		0x08
+	bit	SP_MSG_OUT		0x04
+	bit	SP_DATA_IN		0x02
+	bit	SP_DATA_OUT	0x01
+}
+
+/*
  * Special Function
  */
 register SFUNCT {
 	address			0x09f
 	access_mode RW
+	bit	ALT_MODE	0x80
 }
 
 /*
@@ -960,19 +1028,29 @@
 	address			0x0F4
 }
 
+register HESCB_QOFF {
+	address			0x0F5
+}
+
 register SNSCB_QOFF {
 	address			0x0F6
 }
 
+register SESCB_QOFF {
+	address			0x0F7
+}
+
 register SDSCB_QOFF {
 	address			0x0F8
 }
 
 register QOFF_CTLSTA {
 	address			0x0FA
+	bit	ESTABLISH_SCB_AVAIL	0x80
 	bit	SCB_AVAIL	0x40
 	bit	SNSCB_ROLLOVER	0x20
 	bit	SDSCB_ROLLOVER	0x10
+	bit	SESCB_ROLLOVER	0x08
 	mask	SCB_QSIZE	0x07
 	mask	SCB_QSIZE_256	0x06
 }
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/aic7xxx/aic7xxx.seq linux/drivers/scsi/aic7xxx/aic7xxx.seq
--- v2.3.5/linux/drivers/scsi/aic7xxx/aic7xxx.seq	Thu Oct  8 08:07:33 1998
+++ linux/drivers/scsi/aic7xxx/aic7xxx.seq	Wed Jun  9 16:59:15 1999
@@ -1,7 +1,7 @@
 /*
  * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
  *
- * Copyright (c) 1994-1998 Justin Gibbs.
+ * Copyright (c) 1994-1999 Justin Gibbs.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -58,6 +58,7 @@
 
 reset:
 	clr	SCSISIGO;		/* De-assert BSY */
+	and	SXFRCTL1, ~BITBUCKET;
 	/* Always allow reselection */
 	if ((p->flags & AHC_TARGETMODE) != 0) {
 		mvi	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP;
@@ -72,8 +73,8 @@
 	}
 
 	call	clear_target_state;
-	and	SXFRCTL0, ~SPIOEN;
 poll_for_work:
+	and	SXFRCTL0, ~SPIOEN;
 	if ((p->features & AHC_QUEUE_REGS) == 0) {
 		mov	A, QINPOS;
 	}
@@ -134,6 +135,21 @@
 	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
 	mov	RETURN_2	 call dma_scb;
 
+/*
+ * Preset the residual fields in case we never go through a data phase.
+ * This isn't done by the host so we can avoid a DMA to clear these
+ * fields for the normal case of I/O that completes without underrun
+ * or overrun conditions.
+ */
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov    SCB_RESID_DCNT, SCB_DATACNT, 3;
+	} else {
+		mov     SCB_RESID_DCNT[0],SCB_DATACNT[0];
+		mov     SCB_RESID_DCNT[1],SCB_DATACNT[1];
+		mov     SCB_RESID_DCNT[2],SCB_DATACNT[2];
+	}
+	mov     SCB_RESID_SGCNT, SCB_SGCOUNT;
+
 start_scb:
 	/*
 	 * Place us on the waiting list in case our selection
@@ -174,8 +190,7 @@
  * set in SXFRCTL0.
  */
 initialize_channel:
-	or	A, CLRSTCNT|CLRCHN, SINDEX;
-	or	SXFRCTL0, A;
+	or	SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX;
 	if ((p->features & AHC_ULTRA) != 0) {
 ultra:
 		mvi	SINDEX, ULTRA_ENB+1;
@@ -402,29 +417,42 @@
 	mvi	INTSTAT, BAD_PHASE;
 	
 clear_target_state:
-	clr	DFCNTRL;		/*
-					 * We assume that the kernel driver
-					 * may reset us at any time, even
-					 * in the middle of a DMA, so clear
-					 * DFCNTRL too.
-					 */
-	clr	SCSIRATE;		/*
-					 * We don't know the target we will
-					 * connect to, so default to narrow
-					 * transfers to avoid parity problems.
-					 */
-	and	SXFRCTL0, ~(FAST20);
+	/*
+	 * We assume that the kernel driver may reset us
+	 * at any time, even in the middle of a DMA, so
+	 * clear DFCNTRL too.
+	 */
+	clr	DFCNTRL;
+
+	/*
+	 * We don't know the target we will connect to,
+	 * so default to narrow transfers to avoid
+	 * parity problems.
+	 */
+	if ((p->features & AHC_ULTRA2) != 0) {
+		bmov    SCSIRATE, ALLZEROS, 2;
+	} else {
+		clr     SCSIRATE;
+		and     SXFRCTL0, ~(FAST20);
+	}
 	mvi	LASTPHASE, P_BUSFREE;
 	/* clear target specific flags */
-	and	SEQ_FLAGS, (WIDE_BUS|TWIN_BUS) ret;
+	clr	SEQ_FLAGS ret;
 
 /*
  * If we re-enter the data phase after going through another phase, the
  * STCNT may have been cleared, so restore it from the residual field.
  */
 data_phase_reinit:
-	mvi	DINDEX, STCNT;
-	mvi	SCB_RESID_DCNT	call bcopy_3;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		if ((p->features & AHC_ULTRA2) != 0) {
+			bmov	HCNT, SCB_RESID_DCNT, 3;
+		}
+		bmov    STCNT, SCB_RESID_DCNT, 3;
+	} else {
+		mvi	DINDEX, STCNT;
+		mvi	SCB_RESID_DCNT	call bcopy_3;
+	}
 	jmp	data_phase_loop;
 
 p_data:
@@ -455,20 +483,16 @@
 	 */
 	if ((p->features & AHC_CMD_CHAN) != 0) {
 		bmov	HADDR, SCB_DATAPTR, 7;
+		bmov    STCNT, HCNT, 3;
+		bmov    SG_COUNT, SCB_SGCOUNT, 5;
 	} else {
 		mvi	DINDEX, HADDR;
 		mvi	SCB_DATAPTR	call bcopy_7;
-	}
-
-	if ((p->features & AHC_ULTRA2) == 0) {
 		call	set_stcnt_from_hcnt;
+		mvi	DINDEX, SG_COUNT;
+		mvi	SCB_SGCOUNT	call bcopy_5;
 	}
 
-	mov	SG_COUNT,SCB_SGCOUNT;
-
-	mvi	DINDEX, SG_NEXT;
-	mvi	SCB_SGPTR	call bcopy_4;
-
 data_phase_loop:
 /* Guard against overruns */
 	test	SG_COUNT, 0xff jnz data_phase_inbounds;
@@ -480,8 +504,11 @@
  */
 	or	SXFRCTL1,BITBUCKET;
 	and	DMAPARAMS, ~(HDMAEN|SDMAEN);
-	if ((p->features & AHC_ULTRA2) != 0) {
-		bmov	HCNT, ALLONES, 3;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		if ((p->features & AHC_ULTRA2) != 0) {
+			bmov	HCNT, ALLONES, 3;
+		}
+		bmov	STCNT, ALLONES, 3;
 	} else {
 		mvi	STCNT[0], 0xFF;
 		mvi	STCNT[1], 0xFF;
@@ -489,23 +516,21 @@
 	}
 data_phase_inbounds:
 /* If we are the last SG block, tell the hardware. */
+if ((p->features & AHC_ULTRA2) == 0) {
 	cmp	SG_COUNT,0x01 jne data_phase_wideodd;
-	if ((p->features & AHC_ULTRA2) != 0) {
-		or	SG_CACHEPTR, LAST_SEG;
-	} else {
-		and	DMAPARAMS, ~WIDEODD;
-	}
+	and	DMAPARAMS, ~WIDEODD;
+}
 data_phase_wideodd:
 	if ((p->features & AHC_ULTRA2) != 0) {
 		mov	SINDEX, ALLONES;
 		mov	DFCNTRL, DMAPARAMS;
-		test	SSTAT0, SDONE jnz .;/* Wait for preload to complete */
+		test	SSTAT0, SDONE jnz .;
 data_phase_dma_loop:
-		test	SSTAT0,	SDONE jnz data_phase_dma_done;
+		test	SSTAT0, SDONE jnz data_phase_dma_done;
 		test	SSTAT1,PHASEMIS	jz data_phase_dma_loop;	/* ie. underrun */
 data_phase_dma_phasemis:
 		test	SSTAT0,SDONE	jnz . + 2;
-		mov	SINDEX,ALLZEROS;	/* Remeber the phasemiss */
+		clr	SINDEX;			/* Remember the phasemiss */
 	} else {
 		mov	DMAPARAMS  call dma;
 	}
@@ -554,6 +579,9 @@
 		mvi	CCSGCTL, CCSGRESET;
 prefetched_segs_avail:
 		bmov 	HADDR, CCSGRAM, 8;
+		if ((p->features & AHC_ULTRA2) == 0) {
+			bmov    STCNT, HCNT, 3;
+		}
 	} else {
 		mvi	DINDEX, HADDR;
 		mvi	SG_NEXT	call bcopy_4;
@@ -575,10 +603,6 @@
 		 * };
 		 */
 		mvi	HADDR	call dfdat_in_7;
-	}
-
-	if ((p->features & AHC_ULTRA2) == 0) {
-		/* Load STCNT as well.  It is a mirror of HCNT */
 		call	set_stcnt_from_hcnt;
 	}
 
@@ -587,28 +611,33 @@
 	add	SG_NEXT[0],SG_SIZEOF;
 	adc	SG_NEXT[1],A;
 
+	test    SSTAT1, REQINIT jz .;
 	test	SSTAT1,PHASEMIS	jz data_phase_loop;
-	/* Ensure the last seg is visable at the shaddow layer */
+
 	if ((p->features & AHC_ULTRA2) != 0) {
-		or	DFCNTRL, PRELOADEN;
+		mov	DFCNTRL, DMAPARAMS;
+		test	SSTAT0, SDONE jnz .;
 	}
 
 data_phase_finish:
-	if ((p->features & AHC_ULTRA2) != 0) {
-		call	ultra2_dmafinish;
-	}
 /*
  * After a DMA finishes, save the SG and STCNT residuals back into the SCB
  * We use STCNT instead of HCNT, since it's a reflection of how many bytes 
  * were transferred on the SCSI (as opposed to the host) bus.
  */
-	mov	SCB_RESID_DCNT[0],STCNT[0];
-	mov	SCB_RESID_DCNT[1],STCNT[1];
-	mov	SCB_RESID_DCNT[2],STCNT[2];
-	mov	SCB_RESID_SGCNT, SG_COUNT;
-
 	if ((p->features & AHC_ULTRA2) != 0) {
-		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+		call	ultra2_dmafinish;
+	}
+	if ((p->features & AHC_ULTRA2) == 0) {
+		if ((p->features & AHC_CMD_CHAN) != 0) {
+			bmov    SCB_RESID_DCNT, STCNT, 3;
+			mov	SCB_RESID_SGCNT, SG_COUNT;
+		} else {
+			mov	SCB_RESID_DCNT[0],STCNT[0];
+			mov	SCB_RESID_DCNT[1],STCNT[1];
+			mov	SCB_RESID_DCNT[2],STCNT[2];
+			mov	SCB_RESID_SGCNT, SG_COUNT;
+		}
 	}
 
 	jmp	ITloop;
@@ -616,7 +645,6 @@
 data_phase_overrun:
 	if ((p->features & AHC_ULTRA2) != 0) {
 		call	ultra2_dmafinish;
-		or	SXFRCTL0, CLRSTCNT|CLRCHN;
 	}
 /*
  * Turn off BITBUCKET mode and notify the host
@@ -635,6 +663,9 @@
 ultra2_dmahalt:
 		and     DFCNTRL, ~(SCSIEN|HDMAEN);
 		test	DFCNTRL, HDMAEN jnz .;
+		bmov	SCB_RESID_DCNT, STCNT, 3;
+		mov	SCB_RESID_SGCNT, SG_COUNT;
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
 		ret;
 	}
 
@@ -647,25 +678,32 @@
 /*
  * Load HADDR and HCNT.
  */
-	if ((p->features & AHC_ULTRA2) != 0) {
-		or	SG_CACHEPTR, LAST_SEG;
-	}
-
 	if ((p->features & AHC_CMD_CHAN) != 0) {
 		bmov	HADDR, SCB_CMDPTR, 5;
 		bmov	HCNT[1], ALLZEROS, 2;
+		if ((p->features & AHC_ULTRA2) == 0) {
+			bmov	STCNT, HCNT, 3;
+		}
 	} else {
 		mvi	DINDEX, HADDR;
 		mvi	SCB_CMDPTR	call bcopy_5;
 		clr	HCNT[1];
 		clr	HCNT[2];
+		call	set_stcnt_from_hcnt;
 	}
 
 	if ((p->features & AHC_ULTRA2) == 0) {
-		call	set_stcnt_from_hcnt;
 		mvi	(SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
 	} else {
-		mvi	(PRELOADEN|SCSIEN|HDMAEN|DIRECTION) call dma;
+		mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
+		test	SSTAT0, SDONE jnz .;
+p_command_dma_loop:
+		test	SSTAT0, DMADONE jnz p_command_ultra2_dma_done;
+		test	SSTAT1,PHASEMIS	jz p_command_dma_loop;	/* ie. underrun */
+p_command_ultra2_dma_done:
+		and     DFCNTRL, ~(SCSIEN|HDMAEN);
+		test	DFCNTRL, HDMAEN jnz .;
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
 	}
 	jmp	ITloop;
 
@@ -698,6 +736,8 @@
  * in case the target decides to put us in this phase for some strange
  * reason.
  */
+p_mesgout_retry:
+	or      SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
 p_mesgout:
 	mov	SINDEX, MSG_OUT;
 	cmp	SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
@@ -749,9 +789,7 @@
  * that the target is requesting that the last message(s) be resent.
  */
 	call	phase_lock;
-	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
-	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
-	jmp	p_mesgout;
+	cmp     LASTPHASE, P_MESGOUT    je p_mesgout_retry;
 
 p_mesgout_done:
 	mvi	CLRSINT1,CLRATNO;	/* Be sure to turn ATNO off */
@@ -889,21 +927,23 @@
  */
 mesgin_sdptrs:
 	test	SEQ_FLAGS, DPHASE	jz mesgin_done;
-	mov	SCB_SGCOUNT,SG_COUNT;
-
-	/* The SCB SGPTR becomes the next one we'll download */
-	mvi	DINDEX, SCB_SGPTR;
-	mvi	SG_NEXT	call bcopy_4;
-	
-	/* The SCB DATAPTR0 becomes the current SHADDR */
-	mvi	DINDEX, SCB_DATAPTR;
-	mvi	SHADDR		call bcopy_4;
-
-/*
- * Use the residual number since STCNT is corrupted by any message transfer.
- */
-	mvi	SCB_RESID_DCNT	call	bcopy_3;
-
+	/*
+	 * The SCB SGPTR becomes the next one we'll download,
+	 * and the SCB DATAPTR becomes the current SHADDR.
+	 * Use the residual number since STCNT is corrupted by
+	 * any message transfer.
+	 */
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov    SCB_SGCOUNT, SG_COUNT, 5;
+		bmov    SCB_DATAPTR, SHADDR, 4;
+		bmov    SCB_DATACNT, SCB_RESID_DCNT, 3;
+	} else {
+		mvi	DINDEX, SCB_SGCOUNT;
+		mvi	SG_COUNT	call bcopy_5;
+		mvi	DINDEX, SCB_DATAPTR;
+		mvi	SHADDR		call bcopy_4;
+		mvi	SCB_RESID_DCNT	call	bcopy_3;
+	}
 	jmp	mesgin_done;
 
 /*
@@ -934,6 +974,7 @@
 	}
 	or      SAVED_TCL,A;		/* SAVED_TCL should be complete now */
 
+	mvi     ARG_2, SCB_LIST_NULL;   /* SCBID of prev SCB in disc List */
 	call	get_untagged_SCBID;
 	cmp	ARG_1, SCB_LIST_NULL	je snoop_tag;
 	if ((p->flags & AHC_PAGESCBS) != 0) {
@@ -964,19 +1005,8 @@
 get_tag:
 	mvi	ARG_1	call inb_next;	/* tag value */
 
-	if ((p->flags & AHC_PAGESCBS) == 0) {
-index_by_tag:
-		mov	SCBPTR,ARG_1;
-		test	SCB_CONTROL,TAG_ENB	jz  not_found;
-		mov	SCBPTR	call rem_scb_from_disc_list;
-	} else {
-		/*
-		 * Ensure that the SCB the tag points to is for
-		 * an SCB transaction to the reconnecting target.
-		 */
 use_retrieveSCB:
-		call	retrieveSCB;
-	}
+	call	retrieveSCB;
 setup_SCB:
 	mov	A, SAVED_TCL;
 	cmp	SCB_TCL, A	jne not_found_cleanup_scb;
@@ -1079,6 +1109,7 @@
  * host->scsi, or 0x39 for scsi->host.  The SCSI channel is cleared
  * during initialization.
  */
+if ((p->features & AHC_ULTRA2) == 0) {
 dma:
 	mov	DFCNTRL,SINDEX;
 dma_loop:
@@ -1118,10 +1149,9 @@
 	 * to drain the data fifo until there is space for the input
 	 * latch to drain and HDMAEN de-asserts.
 	 */
-	if ((p->features & AHC_ULTRA2) == 0) {
-		mov	NONE, DFDAT;
-	}
-	test	DFCNTRL, HDMAEN jnz dma_halt;
+	mov	NONE, DFDAT;
+	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
+}
 return:
 	ret;
 
@@ -1150,6 +1180,7 @@
 	mov	A, ARG_1;			/* Tag passed in ARG_1 */
 	mvi	SCB_TAG	jmp findSCB_loop;	/* &SCB_TAG -> SINDEX */
 findSCB_next:
+	mov     ARG_2, SCBPTR;
 	cmp	SCB_NEXT, SCB_LIST_NULL je notFound;
 	mov	SCBPTR,SCB_NEXT;
 	dec	SINDEX;		/* Last comparison moved us too far */
@@ -1173,19 +1204,15 @@
 
 /*
  * This routine expects SINDEX to contain the index of the SCB to be
- * removed and SCBPTR to be pointing to that SCB.
+ * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the
+ * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL
+ * if it is at the head.
  */
 rem_scb_from_disc_list:
 /* Remove this SCB from the disconnection list */
-	cmp	SCB_NEXT,SCB_LIST_NULL je unlink_prev;
-	mov	DINDEX, SCB_PREV;
-	mov	SCBPTR, SCB_NEXT;
-	mov	SCB_PREV, DINDEX;
-	mov	SCBPTR, SINDEX;
-unlink_prev:
-	cmp	SCB_PREV,SCB_LIST_NULL	je rHead;/* At the head of the list */
+	cmp     ARG_2, SCB_LIST_NULL    je rHead;
 	mov	DINDEX, SCB_NEXT;
-	mov	SCBPTR, SCB_PREV;
+	mov	SCBPTR, ARG_2;
 	mov	SCB_NEXT, DINDEX;
 	mov	SCBPTR, SINDEX ret;
 rHead:
@@ -1285,9 +1312,10 @@
 phase_lock:     
 	test	SSTAT1, REQINIT jz phase_lock;
 	test	SSTAT1, SCSIPERR jnz phase_lock;
-	and	LASTPHASE, PHASE_MASK, SCSISIGI;
-	mov	SCSISIGO, LASTPHASE ret;
+	and	SCSISIGO, PHASE_MASK, SCSISIGI;
+	and	LASTPHASE, PHASE_MASK, SCSISIGI ret;
 
+if ((p->features & AHC_CMD_CHAN) == 0) {
 set_stcnt_from_hcnt:
 	mov	STCNT[0], HCNT[0];
 	mov	STCNT[1], HCNT[1];
@@ -1304,6 +1332,7 @@
 	mov	DINDIR, SINDIR;
 	mov	DINDIR, SINDIR;
 	mov	DINDIR, SINDIR ret;
+}
 
 /*
  * Setup addr assuming that A is an index into
@@ -1407,12 +1436,14 @@
  * Wait for DMA from host memory to data FIFO to complete, then disable
  * DMA and wait for it to acknowledge that it's off.
  */
+if ((p->features & AHC_CMD_CHAN) == 0) {
 dma_finish:
 	test	DFSTATUS,HDONE	jz dma_finish;
 	/* Turn off DMA */
 	and	DFCNTRL, ~HDMAEN;
 	test	DFCNTRL, HDMAEN jnz .;
 	ret;
+}
 
 add_scb_to_free_list:
 	if ((p->flags & AHC_PAGESCBS) != 0) {
@@ -1433,8 +1464,7 @@
 	mvi	DMAPARAMS, FIFORESET;
 	mov	SCB_TAG		call dma_scb;
 unlink_disc_scb:
-	/* jmp instead of call since we want to return anyway */
-	mov	SCBPTR	jmp rem_scb_from_disc_list;
+	mov	DISCONNECTED_SCBH, SCB_NEXT ret;
 dequeue_free_scb:
 	mov	SCBPTR, FREE_SCBH;
 	mov	FREE_SCBH, SCB_NEXT ret;
@@ -1446,10 +1476,5 @@
  * candidates for paging out an SCB if one is needed for a new command.
  * Modifying the disconnected list is a critical(pause dissabled) section.
  */
-	mvi	SCB_PREV, SCB_LIST_NULL;
 	mov	SCB_NEXT, DISCONNECTED_SCBH;
-	mov	DISCONNECTED_SCBH, SCBPTR;
-	cmp	SCB_NEXT,SCB_LIST_NULL je return;
-	mov	SCBPTR,SCB_NEXT;
-	mov	SCB_PREV,DISCONNECTED_SCBH;
-	mov	SCBPTR,DISCONNECTED_SCBH ret;
+	mov	DISCONNECTED_SCBH, SCBPTR ret;
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/aic7xxx/scsi_message.h linux/drivers/scsi/aic7xxx/scsi_message.h
--- v2.3.5/linux/drivers/scsi/aic7xxx/scsi_message.h	Sat Apr 11 11:19:34 1998
+++ linux/drivers/scsi/aic7xxx/scsi_message.h	Wed Jun  9 16:59:15 1999
@@ -38,4 +38,12 @@
 #define MSG_EXT_WDTR_LEN	0x02
 #define MSG_EXT_WDTR_BUS_8_BIT	0x00
 #define MSG_EXT_WDTR_BUS_16_BIT	0x01
-#define MSG_EXT_WDTR_BUS_32_BIT	0x02 
+#define MSG_EXT_WDTR_BUS_32_BIT	0x02
+
+#define MSG_EXT_PPR     0x04
+#define MSG_EXT_PPR_LEN	0x06
+#define MSG_EXT_PPR_OPTION_ST 0x00
+#define MSG_EXT_PPR_OPTION_DT_CRC 0x02
+#define MSG_EXT_PPR_OPTION_DT_UNITS 0x03
+#define MSG_EXT_PPR_OPTION_DT_CRC_QUICK 0x04
+#define MSG_EXT_PPR_OPTION_DT_UNITS_QUICK 0x05
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.3.5/linux/drivers/scsi/aic7xxx.c	Sun Jan 17 18:28:06 1999
+++ linux/drivers/scsi/aic7xxx.c	Wed Jun  9 16:59:15 1999
@@ -171,91 +171,6 @@
  */
 
 /*
- * AIC7XXX_FAKE_NEGOTIATION_CMDS
- *   We now have two distinctly different methods of device negotiation
- *   in this code.  The two methods are selected by either defining or not
- *   defining this option.  The difference is as follows:
- *
- *   With AIC7XXX_FAKE_NEGOTIATION_CMDS not set (commented out)
- *     When the driver is in need of issuing a negotiation command for any
- *     given device, it will add the negotiation message on to part of a
- *     regular SCSI command for the device.  In the process, if the device
- *     is configured for and using tagged queueing, then the code will
- *     also issue that single command as a non-tagged command, attach the
- *     negotiation message to that one command, and use a temporary
- *     queue depth of one to keep the untagged and tagged commands from
- *     overlapping.
- *       Pros: This doesn't use any extra SCB structures, it's simple, it
- *         works most of the time (if not all of the time now), and
- *         since we get the device capability info frmo the INQUIRY data
- *         now, shouldn't cause any problems.
- *       Cons: When we need to send a negotiation command to a device, we
- *         must use a command that is being sent to LUN 0 of the device.
- *         If we try sending one to high LUN numbers, then some devices
- *         get noticeably upset.  Since we have to wait for a command with
- *         LUN == 0 to come along, we may not be able to renegotiate when
- *         we want if the user is actually using say LUN 1 of a CD Changer
- *         instead of using LUN 0 for an extended period of time.
- *
- *   With AIC7XXX_FAKE_NEGOTIATION_CMDS defined
- *     When we need to negotiate with a device, instead of attaching our
- *     negotiation message to an existing command, we insert our own
- *     fictional Scsi_Cmnd into the chain that has the negotiation message
- *     attached to it.  We send this one command as untagged regardless
- *     of the device type, and we fiddle with the queue depth the same as
- *     we would with the option unset to avoid overlapping commands.  The
- *     primary difference between this and the unset option is that the
- *     negotiation message is no longer attached to a specific command,
- *     instead it is its own command and is merely triggered by a
- *     combination of both A) We need to negotiate and B) The mid level
- *     SCSI code has sent us a command.  We still don't do any negotiation
- *     unless there is a valid SCSI command to be processed.
- *       Pros: This fixes the problem above in the Cons section.  Since we
- *         issue our own fake command, we can set the LUN to 0 regardless
- *         of what the LUN is in the real command.  It also means that if
- *         the device get's nasty over negotiation issues, it won't be
- *         showing up on a regular command, so we won't get any SENSE buffer
- *         data or STATUS_BYTE returns to the mid level code that are caused
- *         by snits in the negotiation code.
- *       Cons: We add more code, and more complexity.  This means more ways
- *         in which things could break.  It means a larger driver.  It means
- *         more resource consumption for the fake commands.  However, the
- *         biggest problem is this.  Take a system where there is a CD-ROM
- *         on the SCSI bus.  Someone has a CD in the CD-ROM and is using it.
- *         For some reason the SCSI bus gets reset.  We don't touch the
- *         CD-ROM again for quite a period of time (so we don't renegotiate
- *         after the reset until we do touch the CD-ROM again).  In the
- *         time while we aren't using the CD-ROM, the current disc is
- *         removed and a new one put in.  When we go to check that disc, we
- *         will first have to renegotiate.  In so doing, we issue our fake
- *         SCSI command, which happens to be TEST_UNIT_READY.  The CD-ROM
- *         negotiates with us, then responds to our fake command with a
- *         CHECK_CONDITION status.  We REQUEST_SENSE from the CD-ROM, it
- *         then sends the SENSE data to our fake command to tell it that
- *         it has been through a disc change.  There, now we've cleared out
- *         the SENSE data along with our negotiation command, and when the
- *         real command executes, it won't pick up that the CD was changed.
- *         That's the biggest Con to this approach.  In the future, I could
- *         probably code around this problem though, so this option is still
- *         viable.
- *
- *  So, which command style should you use?  I would appreciate it if people
- *  could try out both types.  I want to know about any cases where one
- *  method works and the other doesn't.  If one method works on significantly
- *  more systems than another, then it will become the default.  If the second
- *  option turns out to work best, then I'll find a way to work around that
- *  big con I listed.
- *
- *  -- July 7, 02:33
- *    OK...I just added some code that should make the Con listed for the
- *    fake commands a non issue now.  However, it needs testing.  For now,
- *    I'm going to make the default to use the fake commands, we'll see how
- *    it goes.
- */
- 
-#define AIC7XXX_FAKE_NEGOTIATION_CMDS
-
-/*
  * AIC7XXX_STRICT_PCI_SETUP
  *   Should we assume the PCI config options on our controllers are set with
  *   sane and proper values, or should we be anal about our PCI config
@@ -336,6 +251,7 @@
 #include "aic7xxx/sequencer.h"
 #include "aic7xxx/scsi_message.h"
 #include "aic7xxx_reg.h"
+#include <scsi/scsicam.h>
 
 #include <linux/stat.h>
 #include <linux/malloc.h>        /* for kmalloc() */
@@ -354,7 +270,7 @@
     0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "5.1.10"
+#define AIC7XXX_C_VERSION  "5.1.17"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
@@ -447,10 +363,10 @@
  * You can try raising me if tagged queueing is enabled, or lowering
  * me if you only have 4 SCBs.
  */
-#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN
-#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN
+#ifdef CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#define AIC7XXX_CMDS_PER_DEVICE CONFIG_AIC7XXX_CMDS_PER_DEVICE
 #else
-#define AIC7XXX_CMDS_PER_LUN 24
+#define AIC7XXX_CMDS_PER_DEVICE 8
 #endif
 
 /* Set this to the delay in seconds after SCSI bus reset. */
@@ -495,7 +411,7 @@
  *
  * *** Determining commands per LUN ***
  * 
- * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its
+ * When AIC7XXX_CMDS_PER_DEVICE is not defined, the driver will use its
  * own algorithm to determine the commands/LUN.  If SCB paging is
  * enabled, which is always now, the default is 8 commands per lun
  * that indicates it supports tagged queueing.  All non-tagged devices
@@ -513,8 +429,13 @@
  * Make a define that will tell the driver not to use tagged queueing
  * by default.
  */
+#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0,\
+                              0, 0, 0, 0, 0, 0, 0, 0}
+#else
 #define DEFAULT_TAG_COMMANDS {255, 255, 255, 255, 255, 255, 255, 255,\
                               255, 255, 255, 255, 255, 255, 255, 255}
+#endif
 
 /*
  * Modify this as you see fit for your system.  By setting tag_commands
@@ -553,6 +474,27 @@
 };
 */
 
+static adapter_tag_info_t aic7xxx_tag_info[] =
+{
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS}
+};
+
+
 /*
  * Define an array of board names that can be indexed by aha_type.
  * Don't forget to change this when changing the types!
@@ -579,11 +521,14 @@
   "Adaptec AHA-2944 Ultra SCSI host adapter",           /* AIC_7884 */
   "Adaptec AIC-7895 Ultra SCSI host adapter",           /* AIC_7895 */
   "Adaptec AIC-7890/1 Ultra2 SCSI host adapter",        /* AIC_7890 */
+  "Adaptec AHA-293X Ultra2 SCSI host adapter",          /* AIC_7890 */
   "Adaptec AHA-294X Ultra2 SCSI host adapter",          /* AIC_7890 */
   "Adaptec AIC-7896/7 Ultra2 SCSI host adapter",        /* AIC_7896 */
   "Adaptec AHA-394X Ultra2 SCSI host adapter",          /* AIC_7897 */
   "Adaptec AHA-395X Ultra2 SCSI host adapter",          /* AIC_7897 */
   "Adaptec PCMCIA SCSI controller",                     /* card bus stuff */
+  "Adaptec AIC-7892 Ultra 160/m SCSI host adapter",     /* AIC_7892 */
+  "Adaptec AIC-7899 Ultra 160/m SCSI host adapter",     /* AIC_7899 */
 };
 
 /*
@@ -851,15 +796,17 @@
         SCB_DEVICE_RESET        = 0x0020,
         SCB_RESET               = 0x0040,
         SCB_RECOVERY_SCB        = 0x0080,
-        SCB_WAS_BUSY            = 0x0100,
+        SCB_MSGOUT_PPR          = 0x0100,
         SCB_MSGOUT_SENT         = 0x0200,
         SCB_MSGOUT_SDTR         = 0x0400,
         SCB_MSGOUT_WDTR         = 0x0800,
-        SCB_MSGOUT_BITS         = SCB_MSGOUT_SENT | 
+        SCB_MSGOUT_BITS         = SCB_MSGOUT_PPR |
+                                  SCB_MSGOUT_SENT | 
                                   SCB_MSGOUT_SDTR |
                                   SCB_MSGOUT_WDTR,
         SCB_QUEUED_ABORT        = 0x1000,
-        SCB_QUEUED_FOR_DONE     = 0x2000
+        SCB_QUEUED_FOR_DONE     = 0x2000,
+        SCB_WAS_BUSY            = 0x4000
 } scb_flag_type;
 
 typedef enum {
@@ -913,6 +860,8 @@
   AHC_AIC7890          = 0x0006,
   AHC_AIC7895          = 0x0007,
   AHC_AIC7896          = 0x0008,
+  AHC_AIC7892          = 0x0009,
+  AHC_AIC7899          = 0x000a,
   AHC_VL               = 0x0100,
   AHC_EISA             = 0x0200,
   AHC_PCI              = 0x0400,
@@ -929,6 +878,7 @@
   AHC_QUEUE_REGS       = 0x0040,
   AHC_SG_PRELOAD       = 0x0080,
   AHC_SPIOCAP          = 0x0100,
+  AHC_ULTRA3           = 0x0200,
   AHC_AIC7770_FE       = AHC_FENONE,
   AHC_AIC7850_FE       = AHC_SPIOCAP,
   AHC_AIC7860_FE       = AHC_ULTRA|AHC_SPIOCAP,
@@ -938,6 +888,8 @@
                          AHC_QUEUE_REGS|AHC_SG_PRELOAD,
   AHC_AIC7895_FE       = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA,
   AHC_AIC7896_FE       = AHC_AIC7890_FE,
+  AHC_AIC7892_FE       = AHC_AIC7890_FE|AHC_ULTRA3,
+  AHC_AIC7899_FE       = AHC_AIC7890_FE|AHC_ULTRA3,
 } ahc_feature;
 
 struct aic7xxx_scb {
@@ -1014,9 +966,12 @@
   unsigned char goal_period;
   unsigned char cur_offset;
   unsigned char goal_offset;
+  unsigned char cur_options;
+  unsigned char goal_options;
   unsigned char user_width;
   unsigned char user_period;
   unsigned char user_offset;
+  unsigned char user_options;
 } transinfo_type;
 
 /*
@@ -1045,10 +1000,11 @@
   unsigned long            isr_count;        /* Interrupt count */
   unsigned long            spurious_int;
   scb_data_type           *scb_data;
+  volatile unsigned short  needdv;
+  volatile unsigned short  needppr;
   volatile unsigned short  needsdtr;
-  volatile unsigned short  sdtr_pending;
   volatile unsigned short  needwdtr;
-  volatile unsigned short  wdtr_pending;
+  volatile unsigned short  dtr_pending;
   struct aic7xxx_cmd_queue {
     Scsi_Cmnd *head;
     Scsi_Cmnd *tail;
@@ -1071,9 +1027,10 @@
 #define  DEVICE_PRESENT                 0x01
 #define  BUS_DEVICE_RESET_PENDING       0x02
 #define  DEVICE_RESET_DELAY             0x04
-#define  DEVICE_PRINT_SDTR              0x08
-#define  DEVICE_PRINT_WDTR              0x10
+#define  DEVICE_PRINT_DTR               0x08
+#define  DEVICE_PARITY_ERROR            0x10
 #define  DEVICE_WAS_BUSY                0x20
+#define  DEVICE_SCSI_3                  0x40
 #define  DEVICE_SCANNED                 0x80
   volatile unsigned char   dev_flags[MAX_TARGETS];
   volatile unsigned char   dev_active_cmds[MAX_TARGETS];
@@ -1090,11 +1047,10 @@
 #endif
 
 
-#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
-  Scsi_Cmnd               *dev_wdtr_cmnd[MAX_TARGETS];
-  Scsi_Cmnd               *dev_sdtr_cmnd[MAX_TARGETS];
-#endif
+  Scsi_Cmnd               *dev_dtr_cmnd[MAX_TARGETS];
 
+  unsigned int             dev_checksum[MAX_TARGETS];
+  
   unsigned char            dev_last_queue_full[MAX_TARGETS];
   unsigned char            dev_last_queue_full_count[MAX_TARGETS];
   unsigned char            dev_max_queue_depth[MAX_TARGETS];
@@ -1102,7 +1058,7 @@
   volatile scb_queue_type  delayed_scbs[MAX_TARGETS];
 
 
-  unsigned char            msg_buf[9];       /* The message for the target */
+  unsigned char            msg_buf[13];      /* The message for the target */
   unsigned char            msg_type;
 #define MSG_TYPE_NONE              0x00
 #define MSG_TYPE_INITIATOR_MSGOUT  0x01
@@ -1132,6 +1088,7 @@
   int                      scsi_id_b;        /* channel B for twin adapters */
   unsigned int             bios_address;
   int                      board_name_index;
+  unsigned short           needppr_copy;     /* default config */
   unsigned short           needsdtr_copy;    /* default config */
   unsigned short           needwdtr_copy;    /* default config */
   unsigned short           ultraenb;         /* Ultra mode target list */
@@ -1192,9 +1149,12 @@
  * Provides a mapping of transfer periods in ns/4 to the proper value to
  * stick in the SCSIRATE reg to use that transfer rate.
  */
-#define AHC_SYNCRATE_ULTRA2 0
-#define AHC_SYNCRATE_ULTRA  2
-#define AHC_SYNCRATE_FAST   5
+#define AHC_SYNCRATE_ULTRA3 0
+#define AHC_SYNCRATE_ULTRA2 1
+#define AHC_SYNCRATE_ULTRA  3
+#define AHC_SYNCRATE_FAST   6
+#define AHC_SYNCRATE_CRC 0x40
+#define AHC_SYNCRATE_SE  0x10
 static struct aic7xxx_syncrate {
   /* Rates in Ultra mode have bit 8 of sxfr set */
 #define                ULTRA_SXFR 0x100
@@ -1203,6 +1163,7 @@
   unsigned char period;
   const char *rate[2];
 } aic7xxx_syncrates[] = {
+  { 0x42,  0x000,   9,  {"80.0", "160.0"} },
   { 0x13,  0x000,  10,  {"40.0", "80.0"} },
   { 0x14,  0x000,  11,  {"33.0", "66.6"} },
   { 0x15,  0x100,  12,  {"20.0", "40.0"} },
@@ -1410,35 +1371,6 @@
 
 #endif
 
-/*
- * See the comments earlier in the file for what this item is all about
- * If you have more than 4 controllers, you will need to increase the
- * the number of items in the array below.  Additionally, if you don't
- * want to have lilo pass a humongous config line to the aic7xxx driver,
- * then you can get in and manually adjust these instead of leaving them
- * at the default.  Pay attention to the comments earlier in this file
- * concerning this array if you are going to hand modify these values.
- */
-static adapter_tag_info_t aic7xxx_tag_info[] =
-{
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS}
-};
-
 #define VERBOSE_NORMAL         0x0000
 #define VERBOSE_NEGOTIATION    0x0001
 #define VERBOSE_SEQINT         0x0002
@@ -1920,6 +1852,7 @@
       aic_outb(p, ((instr.integer >> 8) & 0xff), SEQRAM);
       aic_outb(p, ((instr.integer >> 16) & 0xff), SEQRAM);
       aic_outb(p, ((instr.integer >> 24) & 0xff), SEQRAM);
+      udelay(15);
       break;
 
     default:
@@ -2084,28 +2017,101 @@
  *-F*************************************************************************/
 static struct aic7xxx_syncrate *
 aic7xxx_find_syncrate(struct aic7xxx_host *p, unsigned int *period,
-  unsigned int maxsync)
+  unsigned int maxsync, unsigned char *options)
 {
   struct aic7xxx_syncrate *syncrate;
+  int done = FALSE;
 
+  switch(*options)
+  {
+    case MSG_EXT_PPR_OPTION_DT_CRC:
+    case MSG_EXT_PPR_OPTION_DT_UNITS:
+      if(!(p->features & AHC_ULTRA3))
+      {
+        *options = 0;
+        maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
+      }
+      break;
+    case MSG_EXT_PPR_OPTION_DT_CRC_QUICK:
+    case MSG_EXT_PPR_OPTION_DT_UNITS_QUICK:
+      if(!(p->features & AHC_ULTRA3))
+      {
+        *options = 0;
+        maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
+      }
+      else
+      {
+        /*
+         * we don't support the Quick Arbitration variants of dual edge
+         * clocking.  As it turns out, we want to send back the
+         * same basic option, but without the QA attribute.
+         * We know that we are responding because we would never set
+         * these options ourself, we would only respond to them.
+         */
+        switch(*options)
+        {
+          case MSG_EXT_PPR_OPTION_DT_CRC_QUICK:
+            *options = MSG_EXT_PPR_OPTION_DT_CRC;
+            break;
+          case MSG_EXT_PPR_OPTION_DT_UNITS_QUICK:
+            *options = MSG_EXT_PPR_OPTION_DT_UNITS;
+            break;
+        }
+      }
+      break;
+    default:
+      *options = 0;
+      maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
+      break;
+  }
   syncrate = &aic7xxx_syncrates[maxsync];
   while ( (syncrate->rate[0] != NULL) &&
          (!(p->features & AHC_ULTRA2) || syncrate->sxfr_ultra2) )
   {
-    if ( *period <= syncrate->period )
+    if (*period <= syncrate->period) 
     {
-      /*
-       * When responding to a target that requests sync, the requested rate
-       * may fall between two rates that we can output, but still be a rate
-       * that we can receive.  Because of this, we want to respond with the
-       * same rate that it sent to us even if the persiod we use to send
-       * data to it is lower.  Only lower the response period if we must.
-       */
-      if(syncrate == &aic7xxx_syncrates[maxsync])
+      switch(*options)
+      {
+        case MSG_EXT_PPR_OPTION_DT_CRC:
+        case MSG_EXT_PPR_OPTION_DT_UNITS:
+          if(!(syncrate->sxfr_ultra2 & AHC_SYNCRATE_CRC))
+          {
+            done = TRUE;
+            /*
+             * oops, we went too low for the CRC/DualEdge signalling, so
+             * clear the options byte
+             */
+            *options = 0;
+            /*
+             * We'll be sending a reply to this packet to set the options
+             * properly, so unilaterally set the period as well.
+             */
+            *period = syncrate->period;
+          }
+          else
+          {
+            done = TRUE;
+            if(syncrate == &aic7xxx_syncrates[maxsync])
+            {
+              *period = syncrate->period;
+            }
+          }
+          break;
+        default:
+          if(!(syncrate->sxfr_ultra2 & AHC_SYNCRATE_CRC))
+          {
+            done = TRUE;
+            if(syncrate == &aic7xxx_syncrates[maxsync])
+            {
+              *period = syncrate->period;
+            }
+          }
+          break;
+      }
+      if(done)
       {
-        *period = syncrate->period;
+        break;
       }
-      break;
     }
     syncrate++;
   }
@@ -2115,6 +2121,7 @@
     /*
      * Use async transfers for this target
      */
+    *options = 0;
     *period = 0;
     syncrate = NULL;
   }
@@ -2135,7 +2142,7 @@
 {
   struct aic7xxx_syncrate *syncrate;
 
-  if ((p->features & AHC_ULTRA2) != 0)
+  if (p->features & AHC_ULTRA2)
   {
     scsirate &= SXFR_ULTRA2;
   }
@@ -2147,12 +2154,14 @@
   syncrate = &aic7xxx_syncrates[maxsync];
   while (syncrate->rate[0] != NULL)
   {
-    if ((p->features & AHC_ULTRA2) != 0)
+    if (p->features & AHC_ULTRA2)
     {
       if (syncrate->sxfr_ultra2 == 0)
         break;
       else if (scsirate == syncrate->sxfr_ultra2)
         return (syncrate->period);
+      else if (scsirate == (syncrate->sxfr_ultra2 & ~AHC_SYNCRATE_CRC))
+        return (syncrate->period);
     }
     else if (scsirate == (syncrate->sxfr & ~ULTRA_SXFR))
     {
@@ -2206,11 +2215,11 @@
 static void
 aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate,
     int target, int channel, unsigned int period, unsigned int offset,
-    unsigned int type)
+    unsigned char options, unsigned int type)
 {
   unsigned char tindex;
   unsigned short target_mask;
-  unsigned char lun;
+  unsigned char lun, old_options;
   unsigned int old_period, old_offset;
 
   tindex = target | (channel << 3);
@@ -2225,6 +2234,7 @@
 
   old_period = p->transinfo[tindex].cur_period;
   old_offset = p->transinfo[tindex].cur_offset;
+  old_options = p->transinfo[tindex].cur_options;
 
   
   if (type & AHC_TRANS_CUR)
@@ -2237,7 +2247,18 @@
       scsirate &= ~SXFR_ULTRA2;
       if (syncrate != NULL)
       {
-        scsirate |= syncrate->sxfr_ultra2;
+        switch(options)
+        {
+          case MSG_EXT_PPR_OPTION_DT_UNITS:
+            /*
+             * mask off the CRC bit in the xfer settings
+             */
+            scsirate |= (syncrate->sxfr_ultra2 & ~AHC_SYNCRATE_CRC);
+            break;
+          default:
+            scsirate |= syncrate->sxfr_ultra2;
+            break;
+        }
       }
       if (type & AHC_TRANS_ACTIVE)
       {
@@ -2278,9 +2299,10 @@
     aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
     p->transinfo[tindex].cur_period = period;
     p->transinfo[tindex].cur_offset = offset;
+    p->transinfo[tindex].cur_options = options;
     if ( !(type & AHC_TRANS_QUITE) &&
          (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-         (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
+         (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
     {
       if (offset)
       {
@@ -2295,7 +2317,7 @@
         printk(INFO_LEAD "Using asynchronous transfers.\n",
                p->host_no, channel, target, lun);
       }
-      p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR;
+      p->dev_flags[tindex] &= ~DEVICE_PRINT_DTR;
     }
   }
 
@@ -2303,12 +2325,14 @@
   {
     p->transinfo[tindex].goal_period = period;
     p->transinfo[tindex].goal_offset = offset;
+    p->transinfo[tindex].goal_options = options;
   }
 
   if (type & AHC_TRANS_USER)
   {
     p->transinfo[tindex].user_period = period;
     p->transinfo[tindex].user_offset = offset;
+    p->transinfo[tindex].user_options = options;
   }
 }
 
@@ -2325,20 +2349,13 @@
 {
   unsigned char tindex;
   unsigned short target_mask;
-  unsigned int old_width, new_offset;
+  unsigned int old_width;
 
   tindex = target | (channel << 3);
   target_mask = 1 << tindex;
   
   old_width = p->transinfo[tindex].cur_width;
 
-  if (p->features & AHC_ULTRA2)
-    new_offset = MAX_OFFSET_ULTRA2;
-  else if (width == MSG_EXT_WDTR_BUS_16_BIT)
-    new_offset = MAX_OFFSET_16BIT;
-  else
-    new_offset = MAX_OFFSET_8BIT;
-  
   if (type & AHC_TRANS_CUR) 
   {
     unsigned char scsirate;
@@ -2356,12 +2373,12 @@
 
     p->transinfo[tindex].cur_width = width;
 
-    if ((aic7xxx_verbose & VERBOSE_NEGOTIATION2) && 
-        (p->dev_flags[tindex] & DEVICE_PRINT_WDTR))
+    if ( !(type & AHC_TRANS_QUITE) &&
+          (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && 
+          (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
     {
       printk(INFO_LEAD "Using %s transfers\n", p->host_no, channel, target,
         lun, (scsirate & WIDEXFER) ? "Wide(16bit)" : "Narrow(8bit)" );
-      p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
     }
   }
 
@@ -2370,14 +2387,21 @@
   if (type & AHC_TRANS_USER)
     p->transinfo[tindex].user_width = width;
 
-  /*
-   * Having just set the width, the SDTR should come next, and we need a valid
-   * offset for the SDTR.  So, we make sure we put a valid one in here now as
-   * the goal_offset.
-   */
   if (p->transinfo[tindex].goal_offset)
-    p->transinfo[tindex].goal_offset = new_offset;
-
+  {
+    if (p->features & AHC_ULTRA2)
+    {
+      p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+    }
+    else if (width == MSG_EXT_WDTR_BUS_16_BIT)
+    {
+      p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+    }
+    else
+    {
+      p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+    }
+  }
 }
       
 /*+F*************************************************************************
@@ -2543,14 +2567,6 @@
   if (match != 0)
     match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
 
-  if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
-  {
-    printk(KERN_INFO "(scsi%d:%d:%d:%d:tag%d) %s search criteria"
-      " (scsi%d:%d:%d:%d:tag%d)\n", p->host_no, CTL_OF_SCB(scb),
-      scb->hscb->tag, (match) ? "matches" : "doesn't match",
-      p->host_no, channel, target, lun, tag);
-  }
-
   return (match);
 }
 
@@ -2584,14 +2600,13 @@
  *   to the free list.
  *-F*************************************************************************/
 static unsigned char
-aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr)
+aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr,
+                               unsigned char prev)
 {
   unsigned char next;
-  unsigned char prev;
 
   aic_outb(p, scbptr, SCBPTR);
   next = aic_inb(p, SCB_NEXT);
-  prev = aic_inb(p, SCB_PREV);
   aic7xxx_add_curscb_to_free_list(p);
 
   if (prev != SCB_LIST_NULL)
@@ -2604,11 +2619,6 @@
     aic_outb(p, next, DISCONNECTED_SCBH);
   }
 
-  if (next != SCB_LIST_NULL)
-  {
-    aic_outb(p, next, SCBPTR);
-    aic_outb(p, prev, SCB_PREV);
-  }
   return next;
 }
 
@@ -2755,7 +2765,7 @@
          * Place in the scb array; never is removed
          */
         p->scb_data->scb_array[p->scb_data->numscbs++] = scbp;
-        scbq_insert_head(&p->scb_data->free_scbs, scbp);
+        scbq_insert_tail(&p->scb_data->free_scbs, scbp);
       }
       scbp->kmalloc_ptr = scb_ap;
     }
@@ -2796,6 +2806,7 @@
   Scsi_Cmnd *cmd;
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
   unsigned int cpu_flags = 0;
+#endif
   
   DRIVER_LOCK
   while (p->completeq.head != NULL)
@@ -2803,20 +2814,9 @@
     cmd = p->completeq.head;
     p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
     cmd->host_scribble = NULL;
-    sti();
     cmd->scsi_done(cmd);
-    cli();
   }
   DRIVER_UNLOCK
-#else
-  while (p->completeq.head != NULL)
-  {
-    cmd = p->completeq.head;
-    p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
-    cmd->host_scribble = NULL;
-    cmd->scsi_done(cmd);
-  }
-#endif
 }
 
 /*+F*************************************************************************
@@ -2889,16 +2889,13 @@
       }
 #define WIDE_INQUIRY_BITS 0x60
 #define SYNC_INQUIRY_BITS 0x10
+#define SCSI_VERSION_BITS 0x07
       if ( (buffer[7] & WIDE_INQUIRY_BITS) &&
            (p->features & AHC_WIDE) )
       {
         p->needwdtr |= (1<<tindex);
         p->needwdtr_copy |= (1<<tindex);
-        if ( (p->flags & AHC_SEEPROM_FOUND) &&
-             (p->transinfo[tindex].user_width != MSG_EXT_WDTR_BUS_16_BIT) )
-          p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_8_BIT;
-        else
-          p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_16_BIT;
+        p->transinfo[tindex].goal_width = p->transinfo[tindex].user_width;
       }
       else
       {
@@ -2916,28 +2913,10 @@
         p->needsdtr |= (1<<tindex);
         p->needsdtr_copy |= (1<<tindex);
 
-        if (p->flags & AHC_SEEPROM_FOUND)
+        p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period;
+        p->transinfo[tindex].goal_options = p->transinfo[tindex].user_options;
+        if (p->transinfo[tindex].user_offset)
         {
-          p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period;
-          p->transinfo[tindex].goal_offset = p->transinfo[tindex].user_offset;
-        }
-        else
-        {
-          if (p->features & AHC_ULTRA2)
-          {
-            p->transinfo[tindex].goal_period =
-              aic7xxx_syncrates[AHC_SYNCRATE_ULTRA2].period;
-          }
-          else if (p->features & AHC_ULTRA)
-          {
-            p->transinfo[tindex].goal_period =
-              aic7xxx_syncrates[AHC_SYNCRATE_ULTRA].period;
-          }
-          else
-          {
-            p->transinfo[tindex].goal_period =
-              aic7xxx_syncrates[AHC_SYNCRATE_FAST].period;
-          }
           if (p->features & AHC_ULTRA2)
             p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
           else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT)
@@ -2952,14 +2931,57 @@
         p->needsdtr_copy &= ~(1<<tindex);
         p->transinfo[tindex].goal_period = 0;
         p->transinfo[tindex].goal_offset = 0;
+        p->transinfo[tindex].goal_options = 0;
+      }
+      if ( (buffer[2] & SCSI_VERSION_BITS) == 3 )
+      {
+        p->dev_flags[tindex] |= DEVICE_SCSI_3;
+        /*
+         * OK, we are a SCSI 3 device and we are in need of negotiation.
+         * Use PPR messages instead of WDTR and SDTR messages.
+         */
+        if ( (p->needsdtr & (1<<tindex)) ||
+             (p->needwdtr & (1<<tindex)) )
+        {
+          p->needppr |= (1<<tindex);
+          p->needppr_copy |= (1<<tindex);
+        }
+        p->needwdtr &= ~(1<<tindex);
+        p->needwdtr_copy &= ~(1<<tindex);
+        p->needsdtr &= ~(1<<tindex);
+        p->needsdtr_copy &= ~(1<<tindex);
+      }
+      /*
+       * Get the INQUIRY checksum.  We use this on Ultra 160/m
+       * and older devices both.  It allows us to drop speed on any bus type
+       * while at the same time giving us the needed domain validation for
+       * Ultra 160/m
+       *
+       * Note: We only get the checksum and set the SCANNED bit if this is
+       * one of our dtr commands.  If we don't do this, then we end up
+       * getting bad checksum results on the mid-level SCSI code's INQUIRY
+       * commands.
+       */
+      if(p->dev_dtr_cmnd[tindex] == cmd) {
+        unsigned int checksum = 0;
+        int *ibuffer;
+        int i=0;
+
+        ibuffer = (int *)buffer;
+        for( i = 0; i < (cmd->request_bufflen >> 2); i++)
+        {
+          checksum += ibuffer[i];
+        }
+        p->dev_checksum[tindex] = checksum;
+        p->dev_flags[tindex] |= DEVICE_SCANNED;
+        p->dev_flags[tindex] |= DEVICE_PRINT_DTR;
       }
-      p->dev_flags[tindex] |= DEVICE_SCANNED;
-      p->dev_flags[tindex] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR;
 #undef WIDE_INQUIRY_BITS
 #undef SYNC_INQUIRY_BITS
+#undef SCSI_VERSION_BITS
     }
   }
-  else if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
+  else if ((scb->flags & SCB_MSGOUT_BITS) != 0)
   {
     unsigned short mask;
     int message_error = FALSE;
@@ -2979,11 +3001,11 @@
 
     if (scb->flags & SCB_MSGOUT_WDTR)
     {
-      p->wdtr_pending &= ~mask;
+      p->dtr_pending &= ~mask;
       if (message_error)
       {
         if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
-             (p->dev_flags[tindex] & DEVICE_PRINT_WDTR) )
+             (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
         {
           printk(INFO_LEAD "Device failed to complete Wide Negotiation "
             "processing and\n", p->host_no, CTL_OF_SCB(scb));
@@ -2991,7 +3013,6 @@
             "disabling future\n", p->host_no, CTL_OF_SCB(scb));
           printk(INFO_LEAD "Wide negotiation to this device.\n", p->host_no,
             CTL_OF_SCB(scb));
-          p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
         }
         p->needwdtr &= ~mask;
         p->needwdtr_copy &= ~mask;
@@ -2999,11 +3020,11 @@
     }
     if (scb->flags & SCB_MSGOUT_SDTR)
     {
-      p->sdtr_pending &= ~mask;
+      p->dtr_pending &= ~mask;
       if (message_error)
       {
         if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
-             (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
+             (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
         {
           printk(INFO_LEAD "Device failed to complete Sync Negotiation "
             "processing and\n", p->host_no, CTL_OF_SCB(scb));
@@ -3011,12 +3032,38 @@
             "disabling future\n", p->host_no, CTL_OF_SCB(scb));
           printk(INFO_LEAD "Sync negotiation to this device.\n", p->host_no,
             CTL_OF_SCB(scb));
-          p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR;
+          p->dev_flags[tindex] &= ~DEVICE_PRINT_DTR;
         }
         p->needsdtr &= ~mask;
         p->needsdtr_copy &= ~mask;
       }
     }
+    if (scb->flags & SCB_MSGOUT_PPR)
+    {
+      p->dtr_pending &= ~mask;
+      if(message_error)
+      {
+        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+             (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
+        {
+          printk(INFO_LEAD "Device failed to complete Parallel Protocol "
+            "Request processing and\n", p->host_no, CTL_OF_SCB(scb));
+          printk(INFO_LEAD "returned a sense error code for invalid message, "
+            "disabling future\n", p->host_no, CTL_OF_SCB(scb));
+          printk(INFO_LEAD "Parallel Protocol Request negotiation to this "
+            "device.\n", p->host_no, CTL_OF_SCB(scb));
+        }
+        /*
+         * Disable PPR negotiation and revert back to WDTR and SDTR setup
+         */
+        p->needppr &= ~mask;
+        p->needppr_copy &= ~mask;
+        p->needsdtr |= mask;
+        p->needsdtr_copy |= mask;
+        p->needwdtr |= mask;
+        p->needwdtr_copy |= mask;
+      }
+    }
   }
   queue_depth = p->dev_temp_queue_depth[tindex];
   if (queue_depth >= p->dev_active_cmds[tindex])
@@ -3058,16 +3105,6 @@
   p->dev_active_cmds[tindex]--;
   p->activescbs--;
 
-  /*
-   * If this was an untagged I/O, unbusy the target so the sequencer won't
-   * mistake things later
-   */
-  if (aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun, FALSE) ==
-      scb->hscb->tag)
-  {
-    aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun, TRUE);
-  }
-
   {
     int actual;
 
@@ -3122,7 +3159,7 @@
 #endif /* AIC7XXX_PROC_STATS */
       }
 #ifdef AIC7XXX_PROC_STATS
-      x = -10;
+      x = -11;
       while(actual)
       {
         actual >>= 1;
@@ -3429,11 +3466,10 @@
       if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
         printk(INFO_LEAD "Cleaning up status information "
           "and delayed_scbs.\n", p->host_no, channel, i, lun);
-      p->dev_flags[i] &= ~BUS_DEVICE_RESET_PENDING;
+      p->dev_flags[i] &= ~(BUS_DEVICE_RESET_PENDING | DEVICE_PARITY_ERROR);
       if ( tag == SCB_LIST_NULL )
       {
-        p->dev_flags[i] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR |
-                           DEVICE_RESET_DELAY;
+        p->dev_flags[i] |= DEVICE_PRINT_DTR | DEVICE_RESET_DELAY;
         p->dev_expires[i] = jiffies + (4 * HZ);
         p->dev_timer_active |= (0x01 << i);
         p->dev_last_queue_full_count[i] = 0;
@@ -3625,7 +3661,7 @@
   if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
     printk(INFO_LEAD "Cleaning disconnected scbs "
       "list.\n", p->host_no, channel, target, lun);
-  if (p->features & AHC_PAGESCBS)
+  if (p->flags & AHC_PAGESCBS)
   {
     unsigned char next, prev, scb_index;
 
@@ -3641,14 +3677,14 @@
         printk(WARN_LEAD "Disconnected List inconsistency; SCB index=%d, "
           "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
           p->scb_data->numscbs);
-        next = aic7xxx_rem_scb_from_disc_list(p, next);
+        next = aic7xxx_rem_scb_from_disc_list(p, next, prev);
       }
       else
       {
         scbp = p->scb_data->scb_array[scb_index];
         if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
         {
-          next = aic7xxx_rem_scb_from_disc_list(p, next);
+          next = aic7xxx_rem_scb_from_disc_list(p, next, prev);
           if (scbp->flags & SCB_WAITINGQ)
           {
             p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
@@ -3677,7 +3713,7 @@
    * Walk the free list making sure no entries on the free list have
    * a valid SCB_TAG value or SCB_CONTROL byte.
    */
-  if (p->features & AHC_PAGESCBS)
+  if (p->flags & AHC_PAGESCBS)
   {
     unsigned char next;
 
@@ -3734,7 +3770,6 @@
     {
       aic_outb(p, SCB_LIST_NULL, SCB_TAG);
       aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
-      aic_outb(p, SCB_LIST_NULL, SCB_PREV);
       aic_outb(p, 0, SCB_CONTROL);
       aic7xxx_add_curscb_to_free_list(p);
     }
@@ -3863,28 +3898,35 @@
   if (channel == 1)
   {
     p->needsdtr |= (p->needsdtr_copy & 0xFF00);
-    p->sdtr_pending &= 0x00FF;
+    p->dtr_pending &= 0x00FF;
     offset_min = 8;
     offset_max = 16;
   }
   else
   {
-    if (p->features & AHC_WIDE)
+    if (p->features & AHC_TWIN)
     {
-      p->needsdtr = p->needsdtr_copy;
-      p->needwdtr = p->needwdtr_copy;
-      p->sdtr_pending = 0x0;
-      p->wdtr_pending = 0x0;
+      /* Channel A */
+      p->needsdtr |= (p->needsdtr_copy & 0x00FF);
+      p->dtr_pending &= 0xFF00;
       offset_min = 0;
-      offset_max = 16;
+      offset_max = 8;
     }
     else
     {
-      /* Channel A */
-      p->needsdtr |= (p->needsdtr_copy & 0x00FF);
-      p->sdtr_pending &= 0xFF00;
+      p->needppr = p->needppr_copy;
+      p->needsdtr = p->needsdtr_copy;
+      p->needwdtr = p->needwdtr_copy;
+      p->dtr_pending = 0x0;
       offset_min = 0;
-      offset_max = 8;
+      if (p->features & AHC_WIDE)
+      {
+        offset_max = 16;
+      }
+      else
+      {
+        offset_max = 8;
+      }
     }
   }
 
@@ -4188,6 +4230,30 @@
 
 /*+F*************************************************************************
  * Function:
+ *   aic7xxx_construct_ppr
+ *
+ * Description:
+ *   Build up a Parallel Protocol Request message for use with SCSI-3
+ *   devices.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_ppr(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  int tindex = TARGET_INDEX(scb->cmd);
+
+  p->msg_buf[p->msg_index++] = MSG_EXTENDED;
+  p->msg_buf[p->msg_index++] = MSG_EXT_PPR_LEN;
+  p->msg_buf[p->msg_index++] = MSG_EXT_PPR;
+  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_period;
+  p->msg_buf[p->msg_index++] = 0;
+  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_offset;
+  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_width;
+  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_options;
+  p->msg_len += 8;
+}
+
+/*+F*************************************************************************
+ * Function:
  *   aic7xxx_construct_sdtr
  *
  * Description:
@@ -4304,10 +4370,10 @@
   /*
    * Go back to async/narrow transfers and renegotiate.
    */
+  p->needppr |= (p->needppr_copy & targ_mask);
   p->needsdtr |= (p->needsdtr_copy & targ_mask);
   p->needwdtr |= (p->needwdtr_copy & targ_mask);
-  p->sdtr_pending &= ~targ_mask;
-  p->wdtr_pending &= ~targ_mask;
+  p->dtr_pending &= ~targ_mask;
   aic_outb(p, 0, TARG_SCSIRATE + tindex);
   if (p->features & AHC_ULTRA2)
     aic_outb(p, 0, TARG_OFFSET + tindex);
@@ -4315,7 +4381,7 @@
   if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
     printk(INFO_LEAD "Bus Device Reset delivered.\n", p->host_no, channel,
       target, -1);
-  aic7xxx_run_done_queue(p, /*complete*/ FALSE);
+  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
 }
 
 /*+F*************************************************************************
@@ -4360,6 +4426,8 @@
                p->host_no, channel, target, lun,
                aic_inb(p, SAVED_TCL), aic_inb(p, ARG_1),
                (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
+        if (aic7xxx_panic_on_abort)
+          aic7xxx_panic_abort(p, NULL);
       }
       break;
 
@@ -4387,7 +4455,7 @@
             lun, aic_inb(p, LASTPHASE), aic_inb(p, SAVED_TCL));
 
         aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
-        aic7xxx_run_done_queue(p, FALSE);
+        aic7xxx_run_done_queue(p, TRUE);
 
       }
       break;
@@ -4517,7 +4585,7 @@
                   aic7xxx_reset_device(p, target, channel, lun, i);
                   reset++;
                 }
-                aic7xxx_run_done_queue(p, FALSE);
+                aic7xxx_run_done_queue(p, TRUE);
               }
             }
             aic7xxx_verbose = old_verbose;
@@ -4533,6 +4601,51 @@
             aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
           }
         }
+        else if (scb->flags & SCB_MSGOUT_PPR)
+        {
+          /*
+           * As per the draft specs, any device capable of supporting any of
+           * the option values other than 0 are not allowed to reject the
+           * PPR message.  Instead, they must negotiate out what they do
+           * support instead of rejecting our offering.
+           */
+          p->needppr &= ~target_mask;
+          p->needppr_copy &= ~target_mask;
+          aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
+            (AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE));
+          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
+                               AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
+          p->transinfo[tindex].goal_options = 0;
+          p->dtr_pending &= ~target_mask;
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Device is rejecting PPR messages, falling "
+              "back.\n", p->host_no, channel, target, lun);
+          }
+          if ( p->transinfo[tindex].goal_width )
+          {
+            p->needwdtr |= target_mask;
+            p->needwdtr_copy |= target_mask;
+            p->dtr_pending |= target_mask;
+            scb->flags |= SCB_MSGOUT_WDTR;
+          }
+          if ( p->transinfo[tindex].goal_offset )
+          {
+            p->needsdtr |= target_mask;
+            p->needsdtr_copy |= target_mask;
+            if( !(p->dtr_pending & target_mask) )
+            {
+              p->dtr_pending |= target_mask;
+              scb->flags |= SCB_MSGOUT_SDTR;
+            }
+          }
+          if ( p->dtr_pending & target_mask )
+          {
+            aic_outb(p, HOST_MSG, MSG_OUT);
+            aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+          }
+        }
         else if (scb->flags & SCB_MSGOUT_WDTR)
         {
           /*
@@ -4540,20 +4653,18 @@
            */
           p->needwdtr &= ~target_mask;
           p->needwdtr_copy &= ~target_mask;
-          p->wdtr_pending &= ~target_mask;
+          p->dtr_pending &= ~target_mask;
           scb->flags &= ~SCB_MSGOUT_BITS;
           aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
             (AHC_TRANS_ACTIVE|AHC_TRANS_GOAL|AHC_TRANS_CUR));
-          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
                                AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
-          if ( (p->needsdtr_copy & target_mask) &&
-               !(p->sdtr_pending & target_mask) )
+          if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
           {
-            p->sdtr_pending |= target_mask;
-            scb->flags |= SCB_MSGOUT_SDTR;
-            aic_outb(p, HOST_MSG, MSG_OUT);
-            aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+            printk(INFO_LEAD "Device is rejecting WDTR messages, using "
+              "narrow transfers.\n", p->host_no, channel, target, lun);
           }
+          p->needsdtr |= (p->needsdtr_copy & target_mask);
         }
         else if (scb->flags & SCB_MSGOUT_SDTR)
         {
@@ -4562,10 +4673,15 @@
           */
           p->needsdtr &= ~target_mask;
           p->needsdtr_copy &= ~target_mask;
-          p->sdtr_pending &= ~target_mask;
+          p->dtr_pending &= ~target_mask;
           scb->flags &= ~SCB_MSGOUT_SDTR;
-          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
             (AHC_TRANS_CUR|AHC_TRANS_ACTIVE|AHC_TRANS_GOAL));
+          if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Device is rejecting SDTR messages, using "
+              "async transfers.\n", p->host_no, channel, target, lun);
+          }
         }
         else if (aic7xxx_verbose & VERBOSE_SEQINT)
         {
@@ -4681,41 +4797,24 @@
                  * However, if this SCB already was attempting to negotiate,
                  * then we assume this isn't the problem and skip this part.
                  */
-#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
                 if ( (scb->cmd->cmnd[0] != TEST_UNIT_READY) &&
                      (p->dev_flags[tindex] & DEVICE_SCANNED) &&
-                     !(p->wdtr_pending & target_mask) && 
-                     !(p->sdtr_pending & target_mask) )
+                     !(p->dtr_pending & target_mask) ) 
                 {
+                  p->needppr |= (p->needppr_copy & target_mask);
                   p->needwdtr |= (p->needwdtr_copy & target_mask);
                   p->needsdtr |= (p->needsdtr_copy & target_mask);
                 }
-                else if ( (scb->cmd == p->dev_wdtr_cmnd[tindex]) ||
-                          (scb->cmd == p->dev_sdtr_cmnd[tindex]) )
+                else if ( scb->cmd == p->dev_dtr_cmnd[tindex] )
                 {
                   /*
                    * This is already a negotiation command, so we must have
-                   * already done either WDTR or SDTR (or maybe both).  So
-                   * we simply check sdtr_pending and needsdtr to see if we
-                   * should throw out SDTR on this command.
-                   *
-                   * Note: Don't check the needsdtr_copy here, instead just
-                   * check to see if WDTR wiped out our SDTR and set needsdtr.
-                   * Even if WDTR did wipe out SDTR and set needsdtr, if
-                   * parse_msg() then turned around and started our SDTR
-                   * in back to back fasion, then conclusion of that should
-                   * have negated any needsdtr setting.  That's why we only
-                   * check needsdtr and sdtr_pending.
+                   * already done PPR, WDTR or SDTR.  Since our negotiation
+                   * could have gotten rejected, we don't really know the
+                   * full state of things.  Don't do anything here, and allow
+                   * the negotiation_complete() handler to do the right
+                   * thing.
                    */
-                  scb->flags &= ~SCB_MSGOUT_BITS;
-                  if ( (scb->cmd == p->dev_wdtr_cmnd[tindex]) &&
-                       !(p->sdtr_pending & target_mask) &&
-                       (p->needsdtr & target_mask) )
-                  {
-                    p->sdtr_pending |= target_mask;
-                    hscb->control |= MK_MESSAGE;
-                    scb->flags |= SCB_MSGOUT_SDTR;
-                  }
 
                   /*
                    * This is the important part though.  We are getting sense
@@ -4736,43 +4835,13 @@
                     hscb->data_pointer = scb->sg_list[0].address;
                   }
                 }
-#else
-                if (  (scb->cmd->cmnd[0] != TEST_UNIT_READY) &&
-                     !(scb->flags & SCB_MSGOUT_BITS) && 
-                      (scb->cmd->lun == 0) &&
-                      (p->dev_flags[TARGET_INDEX(scb->cmd)] & DEVICE_SCANNED) )
-                {
-                  if ( (p->needwdtr_copy & target_mask) &&
-                      !(p->wdtr_pending & target_mask) &&
-                      !(p->sdtr_pending & target_mask) )
-                  {
-                    p->needwdtr |= target_mask;
-                    p->wdtr_pending |= target_mask;
-                    hscb->control |= MK_MESSAGE;
-                    scb->flags |= SCB_MSGOUT_WDTR;
-                  }
-                  if ( p->needsdtr_copy & target_mask )
-                  {
-                    p->needsdtr |= target_mask;
-                    if ( !(p->wdtr_pending & target_mask) &&
-                         !(p->sdtr_pending & target_mask) )
-                    {
-                      p->sdtr_pending |= target_mask;
-                      hscb->control |= MK_MESSAGE;
-                      scb->flags |= SCB_MSGOUT_SDTR;
-                    }
-                  }
-                }
-                else
-                  scb->flags &= ~SCB_MSGOUT_BITS;
-#endif /* AIC7XXX_FAKE_NEGOTIATION_CMDS */
                 scb->flags |= SCB_SENSE;
                 /*
                  * Ensure the target is busy since this will be an
                  * an untagged request.
                  */
 #ifdef AIC7XXX_VERBOSE_DEBUGGING
-                if (aic7xxx_verbose > 0xffff)
+                if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
                 {
                   if (scb->flags & SCB_MSGOUT_BITS)
                     printk(INFO_LEAD "Requesting SENSE with %s\n", p->host_no,
@@ -4914,7 +4983,8 @@
                 }
               }
 #ifdef AIC7XXX_VERBOSE_DEBUGGING
-              if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+              if( (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ||
+                  (aic7xxx_verbose > 0xffff) )
               {
                 if (queue_flag)
                   printk(INFO_LEAD "Queue full received; queue depth %d, "
@@ -4928,8 +4998,6 @@
 #endif
               if (queue_flag)
               {
-                p->dev_temp_queue_depth[tindex] = 
-                  p->dev_active_cmds[tindex];
                 if ( p->dev_last_queue_full[tindex] !=
                      p->dev_active_cmds[tindex] )
                 {
@@ -4951,10 +5019,28 @@
                       p->dev_active_cmds[tindex];
                   p->dev_last_queue_full[tindex] = 0;
                   p->dev_last_queue_full_count[tindex] = 0;
+                  p->dev_temp_queue_depth[tindex] = 
+                    p->dev_active_cmds[tindex];
+                }
+                else if (p->dev_active_cmds[tindex] == 0)
+                {
+                  if (aic7xxx_verbose & VERBOSE_NEGOTIATION)
+                  {
+                    printk(INFO_LEAD "QUEUE_FULL status received with 0 "
+                           "commands active.\n", p->host_no, CTL_OF_SCB(scb));
+                    printk(INFO_LEAD "Tagged Command Queueing disabled\n",
+                           p->host_no, CTL_OF_SCB(scb));
+                  }
+                  p->dev_max_queue_depth[tindex] = 1;
+                  p->dev_temp_queue_depth[tindex] = 1;
+                  scb->tag_action = 0;
+                  scb->hscb->control &= ~(MSG_ORDERED_Q_TAG|MSG_SIMPLE_Q_TAG);
                 }
                 else
                 {
                   p->dev_flags[tindex] |= DEVICE_WAS_BUSY;
+                  p->dev_temp_queue_depth[tindex] = 
+                    p->dev_active_cmds[tindex];
                 }
               }
               break;
@@ -4989,7 +5075,7 @@
          */
 
         if ( !(scb->flags & SCB_DEVICE_RESET) &&
-              (aic_inb(p, MSG_OUT) == MSG_IDENTIFYFLAG) &&
+              (msg_out == MSG_IDENTIFYFLAG) &&
               (scb->hscb->control & TAG_ENB) )
         {
           p->msg_buf[p->msg_index++] = scb->tag_action;
@@ -5020,34 +5106,68 @@
             printk(INFO_LEAD "Abort message mailed.\n", p->host_no,
               CTL_OF_SCB(scb));
         }
-        else if (scb->flags & SCB_MSGOUT_WDTR)
+        else if (scb->flags & SCB_MSGOUT_PPR)
         {
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-          if (aic7xxx_verbose > 0xffff)
+          unsigned int max_sync, period;
+          unsigned char options = p->transinfo[tindex].goal_options;
+
+          if (p->features & AHC_ULTRA2)
+          {
+            if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+                !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
+            {
+              if( (p->features & AHC_ULTRA3) &&
+                  (p->dev_flags[tindex] & DEVICE_SCSI_3) &&
+                  (p->transinfo[tindex].goal_width ==
+                   MSG_EXT_WDTR_BUS_16_BIT) &&
+                  (options != 0) )
+              {
+                max_sync = AHC_SYNCRATE_ULTRA3;
+              }
+              else
+              {
+                max_sync = AHC_SYNCRATE_ULTRA2;
+              }
+            }
+            else
+            {
+              max_sync = AHC_SYNCRATE_ULTRA;
+            }
+          }
+          else if (p->features & AHC_ULTRA)
+          {
+            max_sync = AHC_SYNCRATE_ULTRA;
+          }
+          else
+          {
+            max_sync = AHC_SYNCRATE_FAST;
+          }
+          period = p->transinfo[tindex].goal_period;
+          aic7xxx_find_syncrate(p, &period, max_sync, &options);
+          p->transinfo[tindex].goal_period = period;
+          p->transinfo[tindex].goal_options = options;
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Sending PPR (%d/%d/%d/%d) message.\n",
+                   p->host_no, CTL_OF_SCB(scb), period,
+                   p->transinfo[tindex].goal_offset,
+                   p->transinfo[tindex].goal_width, options);
+          }
+          aic7xxx_construct_ppr(p, scb);
+        }
+        else if (scb->flags & SCB_MSGOUT_WDTR)
+        {
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
             printk(INFO_LEAD "Sending WDTR message.\n", p->host_no,
                    CTL_OF_SCB(scb));
-#endif
-          aic7xxx_construct_wdtr(p,
-            p->transinfo[TARGET_INDEX(scb->cmd)].goal_width);
+          }
+          aic7xxx_construct_wdtr(p, p->transinfo[tindex].goal_width);
         }
         else if (scb->flags & SCB_MSGOUT_SDTR)
         {
           unsigned int max_sync, period;
-          /*
-           * We need to set an accurate goal_offset instead of
-           * the ridiculously high one we default to.  We should
-           * now know if we are wide.  Plus, the WDTR code will 
-           * set our goal_offset for us as well.
-           */
-          if (p->transinfo[tindex].goal_offset)
-          {
-            if (p->features & AHC_ULTRA2)
-              p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
-            else if (p->transinfo[tindex].cur_width == MSG_EXT_WDTR_BUS_16_BIT)
-              p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
-            else
-              p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
-          }
+          unsigned char options = 0;
           /*
            * Now that the device is selected, use the bits in SBLKCTL and
            * SSTAT2 to determine the max sync rate for this device.
@@ -5073,14 +5193,14 @@
             max_sync = AHC_SYNCRATE_FAST;
           }
           period = p->transinfo[tindex].goal_period;
-          aic7xxx_find_syncrate(p, &period, max_sync);
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-          if (aic7xxx_verbose > 0xffff)
+          aic7xxx_find_syncrate(p, &period, max_sync, &options);
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
             printk(INFO_LEAD "Sending SDTR %d/%d message.\n", p->host_no,
                    CTL_OF_SCB(scb),
                    p->transinfo[tindex].goal_period,
                    p->transinfo[tindex].goal_offset);
-#endif
+          }
           aic7xxx_construct_sdtr(p, period,
             p->transinfo[tindex].goal_offset);
         }
@@ -5154,15 +5274,15 @@
 #if AIC7XXX_NOT_YET 
     case TRACEPOINT:
       {
-        printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no, channel,
-          target, lun);
+        printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no,
+               channel, target, lun);
       }
       break;
 
     case TRACEPOINT2:
       {
-        printk(INFO_LEAD "Tracepoint #2 reached.\n", p->host_no, channel,
-          target, lun);
+        printk(INFO_LEAD "Tracepoint #2 reached.\n", p->host_no,
+               channel, target, lun);
       }
       break;
 
@@ -5237,7 +5357,7 @@
       case MSG_EXT_SDTR:
       {
         unsigned int period, offset;
-        unsigned char maxsync, saved_offset;
+        unsigned char maxsync, saved_offset, options;
         struct aic7xxx_syncrate *syncrate;
         
         if (p->msg_buf[1] != MSG_EXT_SDTR_LEN)
@@ -5253,7 +5373,13 @@
 
         period = p->msg_buf[3];
         saved_offset = offset = p->msg_buf[4];
+        options = 0;
 
+        /*
+         * Even if we are an Ultra3 card, don't allow Ultra3 sync rates when
+         * using the SDTR messages.  We need the PPR messages to enable the
+         * higher speeds that include things like Dual Edge clocking.
+         */
         if (p->features & AHC_ULTRA2)
         {
           if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
@@ -5283,7 +5409,9 @@
         if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
              (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) )
         {
-          if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
+          if (!(p->dev_flags[tindex] & DEVICE_SCANNED) &&
+              !(p->needsdtr_copy & target_mask) &&
+               (p->transinfo[tindex].user_offset) )
           {
             /*
              * Not only is the device starting this up, but it also hasn't
@@ -5295,38 +5423,49 @@
              */
             p->transinfo[tindex].goal_period =
               p->transinfo[tindex].user_period;
-            p->transinfo[tindex].goal_offset =
-              p->transinfo[tindex].user_offset;
+            if(p->features & AHC_ULTRA2)
+            {
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+            }
+            else if (p->transinfo[tindex].cur_width)
+            {
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+            }
+            else
+            {
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+            }
             p->needsdtr_copy |= target_mask;
           }
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Received pre-emptive SDTR message from "
+                   "target.\n", p->host_no, CTL_OF_SCB(scb));
+          }
           if ( !p->transinfo[tindex].goal_offset )
             period = 255;
           if ( p->transinfo[tindex].goal_period > period )
             period = p->transinfo[tindex].goal_period;
         }
   
-        syncrate = aic7xxx_find_syncrate(p, &period, maxsync);
+        syncrate = aic7xxx_find_syncrate(p, &period, maxsync, &options);
         aic7xxx_validate_offset(p, syncrate, &offset,
                                 target_scsirate & WIDEXFER);
         aic7xxx_set_syncrate(p, syncrate, target, channel, period,
-                             offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+                             offset, options, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
 
         /*
-         * Did we drop to async?  If so, are we sending a reply?  If we are,
+         * Did we drop to async?  Or are we sending a reply?  If we are,
          * then we have to make sure that the reply value reflects the proper
          * settings so we need to set the goal values according to what
          * we need to send.
          */
-        if ( (offset == 0) || (offset != saved_offset) ||
+        if ( (offset != saved_offset) ||
              ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
               (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) ) )
         {
-          aic7xxx_set_syncrate(p, syncrate, target, channel, period,
-                               offset, AHC_TRANS_GOAL|AHC_TRANS_QUITE);
-          if ( offset == 0 )
-          {
-            p->needsdtr_copy &= ~target_mask;
-          }
+          aic7xxx_set_syncrate(p, syncrate, target, channel, period, offset,
+                               options, AHC_TRANS_GOAL|AHC_TRANS_QUITE);
         }
         
         /*
@@ -5334,15 +5473,13 @@
          * go async, then send an SDTR back to the target
          */
         p->needsdtr &= ~target_mask;
-        p->sdtr_pending &= ~target_mask;
-        if ( ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) ==
-              (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) &&
-             (offset == saved_offset) )
-        {
-          scb->flags &= ~SCB_MSGOUT_BITS;
-        }
-        else
+        p->dtr_pending &= ~target_mask;
+        if ( ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
+              (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) ||
+             (offset != saved_offset) )
         {
+          reply = TRUE;
+          p->dtr_pending |= target_mask;
           scb->flags &= ~SCB_MSGOUT_BITS;
           scb->flags |= SCB_MSGOUT_SDTR;
           aic_outb(p, HOST_MSG, MSG_OUT);
@@ -5376,12 +5513,11 @@
             {
               reject = TRUE;
               if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
-                   ((p->dev_flags[tindex] & DEVICE_PRINT_WDTR) ||
+                   ((p->dev_flags[tindex] & DEVICE_PRINT_DTR) ||
                     (aic7xxx_verbose > 0xffff)) )
               {
                 printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
                   p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
-                p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
               }
             } /* We fall through on purpose */
             case MSG_EXT_WDTR_BUS_8_BIT:
@@ -5395,15 +5531,11 @@
               break;
             }
           }
-          scb->flags &= ~SCB_MSGOUT_BITS;
-          p->wdtr_pending &= ~target_mask;
+          p->dtr_pending &= ~target_mask;
           p->needwdtr &= ~target_mask;
         }
         else
         {
-          scb->flags &= ~SCB_MSGOUT_BITS;
-          scb->flags |= SCB_MSGOUT_WDTR;
-          reply = TRUE;
           if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) )
           {
             /* 
@@ -5413,13 +5545,33 @@
              */
             p->transinfo[tindex].goal_period =
               p->transinfo[tindex].user_period;
-            p->transinfo[tindex].goal_offset =
-              p->transinfo[tindex].user_offset;
+            if(p->transinfo[tindex].user_offset)
+            {
+              if(p->features & AHC_ULTRA2)
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+              }
+              else if( p->transinfo[tindex].user_width &&
+                       (bus_width == MSG_EXT_WDTR_BUS_16_BIT) &&
+                       p->features & AHC_WIDE )
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+              }
+              else
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+              }
+            }
             p->transinfo[tindex].goal_width =
               p->transinfo[tindex].user_width;
             p->needwdtr_copy |= target_mask;
             p->needsdtr_copy |= target_mask;
           }
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Received pre-emptive WDTR message from "
+                   "target.\n", p->host_no, CTL_OF_SCB(scb));
+          }
           switch(bus_width)
           {
             default:
@@ -5441,8 +5593,11 @@
               break;
             }
           }
+          reply = TRUE;
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          scb->flags |= SCB_MSGOUT_WDTR;
           p->needwdtr &= ~target_mask;
-          p->wdtr_pending &= ~target_mask;
+          p->dtr_pending |= target_mask;
           aic_outb(p, HOST_MSG, MSG_OUT);
           aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
         }
@@ -5456,24 +5611,211 @@
          * supports SDTR at all.  Therefore, we check needsdtr_copy instead
          * of needstr.
          */
-        aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+        aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
                              AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
-        if ( (p->needsdtr_copy & target_mask) &&
-            !(p->sdtr_pending & target_mask))
+        p->needsdtr |= (p->needsdtr_copy & target_mask);
+        done = TRUE;
+        break;
+      }
+      case MSG_EXT_PPR:
+      {
+        unsigned char bus_width, trans_options, new_trans_options;
+        unsigned int period, offset;
+        unsigned char maxsync, saved_offset;
+        struct aic7xxx_syncrate *syncrate;
+        
+        if (p->msg_buf[1] != MSG_EXT_PPR_LEN)
+        {
+          reject = TRUE;
+          break;
+        }
+
+        /*
+         * If we aren't on one of the new Ultra3 cards, then reject any PPR
+         * message since we can't support any option field other than 0
+         */
+        if( !(p->features & AHC_ULTRA3) )
+        {
+          reject = TRUE;
+          break;
+        }
+
+        if (p->msg_len < (MSG_EXT_PPR_LEN + 2))
+        {
+          break;
+        }
+
+        period = p->msg_buf[3];
+        offset = saved_offset = p->msg_buf[5];
+        bus_width = p->msg_buf[6];
+        trans_options = new_trans_options = p->msg_buf[7] & 0xf;
+
+        if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+        {
+          printk(INFO_LEAD "Parsing PPR message (%d/%d/%d/%d)\n",
+                 p->host_no, CTL_OF_SCB(scb), period, offset, bus_width,
+                 trans_options);
+        }
+
+        if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+            !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
         {
-          p->needsdtr |= target_mask;
-          if ( !reject && !reply )
+          if(p->features & AHC_ULTRA3)
+          {
+            maxsync = AHC_SYNCRATE_ULTRA3;
+          }
+          else
           {
-            scb->flags &= ~SCB_MSGOUT_WDTR;
-            if (p->transinfo[tindex].goal_period)
+            maxsync = AHC_SYNCRATE_ULTRA2;
+          }
+        }
+        else
+        {
+          maxsync = AHC_SYNCRATE_ULTRA;
+        }
+        /*
+         * We might have a device that is starting negotiation with us
+         * before we can start up negotiation with it....be prepared to
+         * have a device ask for a higher speed then we want to give it
+         * in that case
+         */
+        if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) !=
+             (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR) )
+        {
+          reply = TRUE;
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          scb->flags |= SCB_MSGOUT_PPR;
+          if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
+          {
+            /*
+             * Not only is the device starting this up, but it also hasn't
+             * been scanned yet, so this would likely be our TUR or our
+             * INQUIRY command at scan time, so we need to use the
+             * settings from the SEEPROM if they existed.  Of course, even
+             * if we didn't find a SEEPROM, we stuffed default values into
+             * the user settings anyway, so use those in all cases.
+             */
+            p->transinfo[tindex].goal_period =
+              p->transinfo[tindex].user_period;
+            if(p->transinfo[tindex].user_offset)
             {
-              p->sdtr_pending |= target_mask;
-              scb->flags |= SCB_MSGOUT_SDTR;
-              aic_outb(p, HOST_MSG, MSG_OUT);
-              aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+              if(p->features & AHC_ULTRA2)
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+              }
+              else if( p->transinfo[tindex].user_width &&
+                       (bus_width == MSG_EXT_WDTR_BUS_16_BIT) &&
+                       p->features & AHC_WIDE )
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+              }
+              else
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+              }
+            }
+            p->transinfo[tindex].goal_width =
+              p->transinfo[tindex].user_width;
+            p->transinfo[tindex].goal_options =
+              p->transinfo[tindex].user_options;
+            p->needppr_copy |= target_mask;
+          }
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Received pre-emptive PPR message from "
+                   "target.\n", p->host_no, CTL_OF_SCB(scb));
+          }
+          if ( !p->transinfo[tindex].goal_offset )
+            period = 255;
+          if ( p->transinfo[tindex].goal_period > period )
+            period = p->transinfo[tindex].goal_period;
+          if ( p->transinfo[tindex].goal_options == 0 )
+            new_trans_options = 0;
+          switch(bus_width)
+          {
+            default:
+            {
+              if ( (p->features & AHC_WIDE) &&
+                   (p->transinfo[tindex].goal_width ==
+                    MSG_EXT_WDTR_BUS_16_BIT) )
+              {
+                bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+                break;
+              }
+            } /* Fall through if we aren't a wide card */
+            case MSG_EXT_WDTR_BUS_8_BIT:
+            {
+              p->needwdtr_copy &= ~target_mask;
+              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+              aic7xxx_set_width(p, target, channel, lun, bus_width,
+                                AHC_TRANS_GOAL|AHC_TRANS_QUITE);
+              break;
+            }
+          }
+        }
+        else
+        {
+          switch(bus_width)
+          {
+            default:
+            {
+              reply = TRUE;
+              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+                   ((p->dev_flags[tindex] & DEVICE_PRINT_DTR) ||
+                    (aic7xxx_verbose > 0xffff)) )
+              {
+                printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
+                  p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
+              }
+            } /* We fall through on purpose */
+            case MSG_EXT_WDTR_BUS_8_BIT:
+            {
+              /*
+               * According to the spec, if we aren't wide, we also can't be
+               * Dual Edge so clear the options byte
+               */
+              new_trans_options = 0;
+              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+              break;
+            }
+            case MSG_EXT_WDTR_BUS_16_BIT:
+            {
+              break;
             }
           }
         }
+
+        aic7xxx_set_width(p, target, channel, lun, bus_width,
+                          AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+        syncrate = aic7xxx_find_syncrate(p, &period, maxsync,
+                                         &new_trans_options);
+        aic7xxx_validate_offset(p, syncrate, &offset, bus_width);
+        aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+                             offset, new_trans_options,
+                             AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+
+        if( (offset != saved_offset) ||
+            (trans_options != new_trans_options) ||
+            ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) !=
+             (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) )
+        {
+          aic7xxx_set_width(p, target, channel, lun, bus_width,
+                            AHC_TRANS_GOAL|AHC_TRANS_QUITE);
+          aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+                               offset, new_trans_options,
+                               AHC_TRANS_GOAL|AHC_TRANS_QUITE);
+          reply = TRUE;
+        }
+        p->dtr_pending &= ~target_mask;
+        p->needppr &= ~target_mask;
+        if(reply)
+        {
+          p->dtr_pending |= target_mask;
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          scb->flags |= SCB_MSGOUT_PPR;
+          aic_outb(p, HOST_MSG, MSG_OUT);
+          aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+        }
         done = TRUE;
         break;
       }
@@ -5485,7 +5827,7 @@
     } /* end of switch(p->msg_type) */
   } /* end of if (!reject && (p->msg_len > 2)) */
 
-  if (reject)
+  if (!reply && reject)
   {
     aic_outb(p, MSG_MESSAGE_REJECT, MSG_OUT);
     aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
@@ -5657,12 +5999,14 @@
     if (aic7xxx_verbose & VERBOSE_RESET)
       printk(WARN_LEAD "Someone else reset the channel!!\n",
            p->host_no, channel, -1, -1);
+    if (aic7xxx_panic_on_abort)
+      aic7xxx_panic_abort(p, NULL);
     /*
      * Go through and abort all commands for the channel, but do not
      * reset the channel again.
      */
     aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
-    aic7xxx_run_done_queue(p, FALSE);
+    aic7xxx_run_done_queue(p, TRUE);
     scb = NULL;
   }
   else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
@@ -5698,7 +6042,7 @@
             CTL_OF_SCB(scb), scb->hscb->tag);
         aic7xxx_reset_device(p, target, channel, ALL_LUNS,
                 (message == MSG_ABORT) ? SCB_LIST_NULL : scb->hscb->tag );
-        aic7xxx_run_done_queue(p, FALSE);
+        aic7xxx_run_done_queue(p, TRUE);
         scb = NULL;
         printerror = 0;
       }
@@ -5709,6 +6053,22 @@
         printerror = 0;
       }
     }
+    if ( (scb != NULL) &&
+         (scb->cmd == p->dev_dtr_cmnd[TARGET_INDEX(scb->cmd)]) )
+    {
+      /*
+       * This might be a SCSI-3 device that is dropping the bus due to
+       * errors and signalling that we should reduce the transfer speed.
+       * All we have to do is complete this command (since it's a negotiation
+       * command already) and the checksum routine should flag an error and
+       * reduce the speed setting and renegotiate.  We call the reset routing
+       * just to clean out the hardware from this scb.
+       */
+      printerror = 0;
+      aic7xxx_reset_device(p, target, channel, ALL_LUNS, scb->hscb->tag);
+      aic7xxx_run_done_queue(p, TRUE);
+      scb = NULL;
+    }
     if (printerror != 0)
     {
       if (scb != NULL)
@@ -5724,7 +6084,12 @@
           tag = SCB_LIST_NULL;
         }
         aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag);
-        aic7xxx_run_done_queue(p, FALSE);
+        aic7xxx_run_done_queue(p, TRUE);
+      }
+      else
+      {
+        aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+        aic7xxx_run_done_queue(p, TRUE);
       }
       printk(INFO_LEAD "Unexpected busfree, LASTPHASE = 0x%x, "
              "SEQADDR = 0x%x\n", p->host_no, channel, target, -1, lastphase,
@@ -5829,12 +6194,26 @@
         cmd->result = 0;
         scb = NULL;
       }
+      if (scb->cmd == p->dev_dtr_cmnd[TARGET_INDEX(scb->cmd)])
+      {
+        /*
+         * Turn off the needsdtr, needwdtr, and needppr bits since this device
+         * doesn't seem to exist.
+         */
+        p->needppr &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needppr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needsdtr &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needsdtr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needwdtr &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needwdtr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd));
+      }
     }
     /*
      * Restarting the sequencer will stop the selection and make sure devices
      * are allowed to reselect in.
      */
     aic_outb(p, 0, SCSISEQ);
+    aic_outb(p, CLRSELINGO, CLRSINT0);
     aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1);
     p->flags &= ~AHC_HANDLING_REQINITS;
     aic_outb(p, CLRSELTIMEO | CLRBUSFREE, CLRSINT1);
@@ -5868,6 +6247,8 @@
     Scsi_Cmnd *cmd;
     unsigned char mesg_out = MSG_NOOP;
     unsigned char lastphase = aic_inb(p, LASTPHASE);
+    unsigned char sstat2 = aic_inb(p, SSTAT2);
+    unsigned char tindex = TARGET_INDEX(scb->cmd);
 
     cmd = scb->cmd;
     switch (lastphase)
@@ -5898,12 +6279,81 @@
         break;
     }
 
-    /*
-     * A parity error has occurred during a data
-     * transfer phase. Flag it and continue.
-     */
-    printk(WARN_LEAD "Parity error during %s phase.\n",
-           p->host_no, CTL_OF_SCB(scb), phase);
+    /*
+     * A parity error has occurred during a data
+     * transfer phase. Flag it and continue.
+     */
+    if( (aic_inb(p, SCSIRATE) & AHC_SYNCRATE_CRC) && (lastphase == P_DATAIN) )
+    {
+      printk(WARN_LEAD "CRC error during %s phase.\n",
+             p->host_no, CTL_OF_SCB(scb), phase);
+      if(sstat2 & CRCVALERR)
+      {
+        printk(WARN_LEAD "  CRC error in intermediate CRC packet.\n",
+               p->host_no, CTL_OF_SCB(scb));
+      }
+      if(sstat2 & CRCENDERR)
+      {
+        printk(WARN_LEAD "  CRC error in ending CRC packet.\n",
+               p->host_no, CTL_OF_SCB(scb));
+      }
+      if(sstat2 & CRCREQERR)
+      {
+        printk(WARN_LEAD "  Target incorrectly requested a CRC packet.\n",
+               p->host_no, CTL_OF_SCB(scb));
+      }
+      if(sstat2 & DUAL_EDGE_ERROR)
+      {
+        printk(WARN_LEAD "  Dual Edge transmission error.\n",
+               p->host_no, CTL_OF_SCB(scb));
+      }
+    }
+    else
+    {
+      printk(WARN_LEAD "Parity error during %s phase.\n",
+             p->host_no, CTL_OF_SCB(scb), phase);
+    }
+
+    if(p->dev_flags[tindex] & DEVICE_PARITY_ERROR)
+    {
+      struct aic7xxx_syncrate *syncrate;
+      unsigned int period = p->transinfo[tindex].cur_period;
+      unsigned char options = p->transinfo[tindex].cur_options;
+      /*
+       * oops, we had a failure, lower the transfer rate and try again.  It's
+       * worth noting here that it might be wise to also check for typical
+       * wide setting on narrow cable type problems and try disabling wide
+       * instead of slowing down if those exist.  That's hard to do with simple
+       * checksums though.
+       */
+      if((syncrate = aic7xxx_find_syncrate(p, &period, 0, &options)) != NULL)
+      {
+        syncrate++;
+        if( (syncrate->rate[0] != NULL) &&
+            (!(p->features & AHC_ULTRA2) || (syncrate->sxfr_ultra2 == 0)) )
+        {
+          p->transinfo[tindex].goal_period = syncrate->period;
+          if( !(syncrate->sxfr_ultra2 & 0x40) )
+          {
+            p->transinfo[tindex].goal_options = 0;
+          }
+        }
+        else
+        {
+          p->transinfo[tindex].goal_offset = 0;
+          p->transinfo[tindex].goal_period = 0;
+          p->transinfo[tindex].goal_options = 0;
+        }
+        p->needppr |= (p->needppr_copy & (1<<tindex));
+        p->needsdtr |= (p->needsdtr_copy & (1<<tindex));
+        p->needwdtr |= (p->needwdtr_copy & (1<<tindex));
+      }
+      p->dev_flags[tindex] &= ~DEVICE_PARITY_ERROR;
+    }
+    else
+    {
+      p->dev_flags[tindex] |= DEVICE_PARITY_ERROR;
+    }
 
     /*
      * We've set the hardware to assert ATN if we get a parity
@@ -6072,13 +6522,6 @@
       printk("HSCB %d bad, SCB_NEXT points to self.\n", i);
       bogus = TRUE;
     }
-    temp = aic_inb(p, SCB_PREV);
-    if ((temp != SCB_LIST_NULL) &&
-        (temp >= p->scb_data->maxhscbs))
-    {
-      printk("HSCB %d bad, SCB_PREV invalid(%d).\n", i, temp);
-      bogus = TRUE;
-    }
     if (scb_status[i] == 0)
       lost++;
     if (lost > 1)
@@ -6163,7 +6606,7 @@
     unsigned char scb_index;
 
 #ifdef AIC7XXX_VERBOSE_DEBUGGING
-    if(aic7xxx_verbose > 0xffff)
+    if( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) )
       printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1);
 #endif
     
@@ -6368,7 +6811,7 @@
  *   Determines the queue depth for a given device.  There are two ways
  *   a queue depth can be obtained for a tagged queueing device.  One
  *   way is the default queue depth which is determined by whether
- *   AIC7XXX_CMDS_PER_LUN is defined.  If it is defined, then it is used
+ *   AIC7XXX_CMDS_PER_DEVICE is defined.  If it is defined, then it is used
  *   as the default queue depth.  Otherwise, we use either 4 or 8 as the
  *   default queue depth (dependent on the number of hardware SCBs).
  *   The other way we determine queue depth is through the use of the
@@ -6396,7 +6839,7 @@
   {
     int tag_enabled = TRUE;
 
-    default_depth = AIC7XXX_CMDS_PER_LUN;
+    default_depth = AIC7XXX_CMDS_PER_DEVICE;
  
     if (!(p->discenable & target_mask))
     {
@@ -6958,7 +7401,7 @@
   }
   printk("\n");
 #endif
-  if (checksum != scarray[len - 1])
+  if ( (checksum != scarray[len - 1]) || (checksum == 0) )
   {
     return (0);
   }
@@ -7371,7 +7814,6 @@
       aic_outb(p, i, SCBPTR);
       aic_outb(p, 0, SCB_CONTROL);   /* Clear the control byte. */
       aic_outb(p, i + 1, SCB_NEXT);  /* Set the next pointer. */
-      aic_outb(p, i - 1, SCB_PREV);  /* Set the prev pointer. */
       aic_outb(p, SCB_LIST_NULL, SCB_TAG);  /* Make the tag invalid. */
       aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS);  /* no busy untagged */
       aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+1);/* targets active yet */
@@ -7840,6 +8282,11 @@
    */
   aic7xxx_loadseq(p);
 
+  /*
+   * Make sure the AUTOFLUSHDIS bit is *not* set in the SBLKCTL register
+   */
+  aic_outb(p, aic_inb(p, SBLKCTL) & ~AUTOFLUSHDIS, SBLKCTL);
+
   if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
   {
     aic_outb(p, ENABLE, BCTL);  /* Enable the boards BUS drivers. */
@@ -8035,6 +8482,7 @@
     {
       p->transinfo[i].goal_period = 0;
       p->transinfo[i].goal_offset = 0;
+      p->transinfo[i].goal_options = 0;
       p->transinfo[i].goal_width = MSG_EXT_WDTR_BUS_8_BIT;
     }
     DRIVER_LOCK_INIT
@@ -8090,10 +8538,14 @@
    */
   for (i = 0; i < MAX_TARGETS; i++)
   {
-    if(p->dev_wdtr_cmnd[i])
-      kfree(p->dev_wdtr_cmnd[i]);
-    if(p->dev_sdtr_cmnd[i])
-      kfree(p->dev_sdtr_cmnd[i]);
+    if(p->dev_dtr_cmnd[i])
+    {
+      if(p->dev_dtr_cmnd[i]->request_buffer)
+      {
+        kfree(p->dev_dtr_cmnd[i]->request_buffer);
+      }
+      kfree(p->dev_dtr_cmnd[i]);
+    }
   }
 
 }
@@ -8184,14 +8636,16 @@
     {
       printk("aic7xxx: Using leftover BIOS values.\n");
     }
-    if ( *sxfrctl1 & STPWEN )
+    if ( ((p->chip & ~AHC_CHIPID_MASK) == AHC_PCI) && (*sxfrctl1 & STPWEN) )
     {
       p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
       sc->adapter_control &= ~CFAUTOTERM;
       sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM;
     }
     if (aic7xxx_extended)
-      p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B;
+      p->flags |= (AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
+    else
+      p->flags &= ~(AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
   }
   else
   {
@@ -8256,8 +8710,7 @@
    * Limit to 16 targets just in case.  The 2842 for one is known to
    * blow the max_targets setting, future cards might also.
    */
-  max_targets = MIN(sc->max_targets & CFMAXTARG,
-                   ((p->features & (AHC_TWIN | AHC_WIDE)) ? 16 : 8));
+  max_targets = ((p->features & (AHC_TWIN | AHC_WIDE)) ? 16 : 8);
 
   if (have_seeprom)
   {
@@ -8279,7 +8732,7 @@
     mask = (0x01 << i);
     if (!have_seeprom)
     {
-      if(aic_inb(p, SCSISEQ) != 0)
+      if (aic_inb(p, SCSISEQ) != 0)
       {
         /*
          * OK...the BIOS set things up and left behind the settings we need.
@@ -8323,7 +8776,9 @@
         sc->device_flags[i] = CFDISC;
         if (p->features & AHC_WIDE)
           sc->device_flags[i] |= CFWIDEB;
-        if (p->features & AHC_ULTRA2)
+        if (p->features & AHC_ULTRA3)
+          sc->device_flags[i] |= 2;
+        else if (p->features & AHC_ULTRA2)
           sc->device_flags[i] |= 3;
         else if (p->features & AHC_ULTRA)
           sc->device_flags[i] |= CFSYNCHISULTRA;
@@ -8339,20 +8794,30 @@
     }
     if (p->flags & AHC_NEWEEPROM_FMT)
     {
-      if (sc->device_flags[i] & CFSYNCHISULTRA)
-      {
-        p->ultraenb |= mask;
-      }
-      else if (sc->device_flags[i] & CFNEWULTRAFORMAT)
+      if ( (sc->device_flags[i] & CFNEWULTRAFORMAT) &&
+          !(p->features & AHC_ULTRA2) )
       {
-        if ( ((sc->device_flags[i] & (CFSYNCHISULTRA | CFXFER)) == 0x03) &&
-             !(p->features & AHC_ULTRA2) )
+        /*
+         * I know of two different Ultra BIOSes that do this differently.
+         * One on the Gigabyte 6BXU mb that wants flags[i] & CFXFER to
+         * be == to 0x03 and SYNCISULTRA to be true to mean 40MByte/s
+         * while on the IBM Netfinity 5000 they want the same thing
+         * to be something else, while flags[i] & CFXFER == 0x03 and
+         * SYNCISULTRA false should be 40MByte/s.  So, we set both to
+         * 40MByte/s and the lower speeds be damned.  People will have
+         * to select around the conversely mapped lower speeds in order
+         * to select lower speeds on these boards.
+         */
+        if ((sc->device_flags[i] & (CFXFER)) == 0x03)
         {
           sc->device_flags[i] &= ~CFXFER;
           sc->device_flags[i] |= CFSYNCHISULTRA;
-          p->ultraenb |= mask;
         }
       }
+      if (sc->device_flags[i] & CFSYNCHISULTRA)
+      {
+        p->ultraenb |= mask;
+      }
     }
     else if (sc->adapter_control & CFULTRAEN)
     {
@@ -8364,18 +8829,54 @@
       p->ultraenb &= ~mask;
       p->transinfo[i].user_offset = 0;
       p->transinfo[i].user_period = 0;
+      p->transinfo[i].user_options = 0;
       p->transinfo[i].cur_offset = 0;
       p->transinfo[i].cur_period = 0;
+      p->transinfo[i].cur_options = 0;
       p->needsdtr_copy &= ~mask;
     }
     else
     {
-      if (p->features & AHC_ULTRA2)
+      if (p->features & AHC_ULTRA3)
+      {
+        p->transinfo[i].user_offset = MAX_OFFSET_ULTRA2;
+        p->transinfo[i].cur_offset = aic_inb(p, TARG_OFFSET + i);
+        if( (sc->device_flags[i] & CFXFER) < 0x03 )
+        {
+          scsirate = (sc->device_flags[i] & CFXFER);
+          p->transinfo[i].user_options = MSG_EXT_PPR_OPTION_DT_CRC;
+          if( (aic_inb(p, TARG_SCSIRATE + i) & CFXFER) < 0x03 )
+          {
+            p->transinfo[i].cur_options = 
+              ((aic_inb(p, TARG_SCSIRATE + i) & 0x40) ?
+                 MSG_EXT_PPR_OPTION_DT_CRC : MSG_EXT_PPR_OPTION_DT_UNITS);
+          }
+          else
+          {
+            p->transinfo[i].cur_options = 0;
+          }
+        }
+        else
+        {
+          scsirate = (sc->device_flags[i] & CFXFER) |
+                     ((p->ultraenb & mask) ? 0x18 : 0x10);
+          p->transinfo[i].user_options = 0;
+          p->transinfo[i].cur_options = 0;
+        }
+        p->transinfo[i].user_period = aic7xxx_find_period(p, scsirate,
+                                       AHC_SYNCRATE_ULTRA3);
+        p->transinfo[i].cur_period = aic7xxx_find_period(p,
+                                       aic_inb(p, TARG_SCSIRATE + i),
+                                       AHC_SYNCRATE_ULTRA3);
+      }
+      else if (p->features & AHC_ULTRA2)
       {
         p->transinfo[i].user_offset = MAX_OFFSET_ULTRA2;
         p->transinfo[i].cur_offset = aic_inb(p, TARG_OFFSET + i);
         scsirate = (sc->device_flags[i] & CFXFER) |
                    ((p->ultraenb & mask) ? 0x18 : 0x10);
+        p->transinfo[i].user_options = 0;
+        p->transinfo[i].cur_options = 0;
         p->transinfo[i].user_period = aic7xxx_find_period(p, scsirate,
                                        AHC_SYNCRATE_ULTRA2);
         p->transinfo[i].cur_period = aic7xxx_find_period(p,
@@ -8385,10 +8886,9 @@
       else
       {
         scsirate = (sc->device_flags[i] & CFXFER) << 4;
-        if (sc->device_flags[i] & CFWIDEB)
-          p->transinfo[i].user_offset = MAX_OFFSET_16BIT;
-        else
-          p->transinfo[i].user_offset = MAX_OFFSET_8BIT;
+        p->transinfo[i].user_options = 0;
+        p->transinfo[i].cur_options = 0;
+        p->transinfo[i].user_offset = MAX_OFFSET_8BIT;
         if (p->features & AHC_ULTRA)
         {
           short ultraenb;
@@ -8427,9 +8927,10 @@
   }
   aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB);
   aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1);
+  p->needppr = p->needppr_copy = p->needdv = 0;
   p->needwdtr = p->needwdtr_copy;
   p->needsdtr = p->needsdtr_copy;
-  p->wdtr_pending = p->sdtr_pending = 0;
+  p->dtr_pending = 0;
 
   /*
    * We set the p->ultraenb from the SEEPROM to begin with, but now we make
@@ -8453,6 +8954,7 @@
     {
       case AHC_AIC7895:
       case AHC_AIC7896:
+      case AHC_AIC7899:
         if (p->adapter_control & CFBPRIMARY)
           p->flags |= AHC_CHANNEL_B_PRIMARY;
       default:
@@ -8783,6 +9285,14 @@
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850,
        AHC_PAGESCBS, AHC_AIC7850_FE,                         6,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7821, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_3860, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7860_FE,                                       7,
@@ -8825,6 +9335,18 @@
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_AIC7880,
        AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7885, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7886, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7887, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7888, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
        AHC_AIC7895_FE,                                      19,
@@ -8833,30 +9355,66 @@
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7890_FE,                                      20,
        32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_78902, AHC_AIC7890,
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890B, AHC_AIC7890,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7890_FE,                                      20,
        32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2930U2, AHC_AIC7890,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7890_FE,                                      21,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      22,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7896_FE,                                      22,
+       AHC_AIC7896_FE,                                      23,
        32, C56_66 },
       {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7896_FE,                                      23,
+       AHC_AIC7896_FE,                                      24,
        32, C56_66 },
       {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3950U2D, AHC_AIC7896,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7896_FE,                                      24,
+       AHC_AIC7896_FE,                                      25,
        32, C56_66 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_1480A, AHC_AIC7860,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7860_FE,                                      25,
+       AHC_AIC7860_FE,                                      26,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892A, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892B, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892D, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892P, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899A, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899B, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899D, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899P, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
     };
 
     unsigned short command;
@@ -8926,11 +9484,11 @@
           }
 #ifdef AIC7XXX_STRICT_PCI_SETUP
           command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
-            PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER |
-            PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+            PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
 #else
           command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
 #endif
+          command &= ~PCI_COMMAND_INVALIDATE;
           if (aic7xxx_pci_parity == 0)
             command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
           pci_write_config_word(pdev, PCI_COMMAND, command);
@@ -8940,15 +9498,7 @@
           {
             printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig);
           }
-          devconfig |= 0x80000000;
-          if ((aic7xxx_pci_parity == 0) || (aic7xxx_pci_parity == -1))
-          {
-            devconfig &= ~(0x00000008);
-          }
-          else
-          {
-            devconfig |= 0x00000008;
-          }
+          devconfig |= 0x80000040;
           pci_write_config_dword(pdev, DEVCONFIG, devconfig);
 #endif /* AIC7XXX_STRICT_PCI_SETUP */
 #else  /* LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) */
@@ -8976,11 +9526,11 @@
           }
 #ifdef AIC7XXX_STRICT_PCI_SETUP
           command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
-            PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER |
-            PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+            PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
 #else
           command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
 #endif
+          command &= ~PCI_COMMAND_INVALIDATE;
           if (aic7xxx_pci_parity == 0)
             command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
           pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND, command);
@@ -8990,15 +9540,7 @@
           {
             printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig);
           }
-          devconfig |= 0x80000000;
-          if ((aic7xxx_pci_parity == 0) || (aic7xxx_pci_parity == -1))
-          {
-            devconfig &= ~(0x00000008);
-          }
-          else
-          {
-            devconfig |= 0x00000008;
-          }
+          devconfig |= 0x80000040;
           pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, devconfig);
 #endif /* AIC7XXX_STRICT_PCI_SETUP */
 #endif /* LINUIX_VERSION_CODE > KERNEL_VERSION(2,1,92) */
@@ -9137,6 +9679,7 @@
 
             case AHC_AIC7895: /* 7895 */
             case AHC_AIC7896: /* 7896/7 */
+            case AHC_AIC7899: /* 7899 */
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               if (PCI_FUNC(temp_p->pdev->devfn) != 0)
               {
@@ -9185,43 +9728,38 @@
            */
           switch (temp_p->chip & AHC_CHIPID_MASK)
           {
-            case AHC_AIC7890:
-            case AHC_AIC7896:
+            case AHC_AIC7892:
+            case AHC_AIC7899:
               aic_outb(temp_p, 0, SCAMCTL);
               /*
-               * We used to set DPARCKEN in this register, but after talking
-               * to a tech from Adaptec, I found out they don't use that
-               * particular bit in their own register settings, and when you
-               * combine that with the fact that I determined that we were
-               * seeing Data-Path Parity Errors on things we shouldn't see
-               * them on, I think there is a bug in the silicon and the way
-               * to work around it is to disable this particular check.  Also
-               * This bug only showed up on certain commands, so it seems to
-               * be pattern related or some such.  The commands we would
-               * typically send as a linux TEST_UNIT_READY or INQUIRY command
-               * could cause it to be triggered, while regular commands that
-               * actually made reasonable use of the SG array capabilities
-               * seemed not to cause the problem.
+               * Switch to the alt mode of the chip...
+               */
+              aic_outb(temp_p, aic_inb(temp_p, SFUNCT) | ALT_MODE, SFUNCT);
+              /*
+               * Set our options...the last two items set our CRC after x byte
+	       * count in target mode...
                */
+              aic_outb(temp_p, AUTO_MSGOUT_DE | DIS_MSGIN_DUALEDGE, OPTIONMODE);
+	      aic_outb(temp_p, 0x00, 0x0b);
+	      aic_outb(temp_p, 0x10, 0x0a);
               /*
-              aic_outb(temp_p, aic_inb(temp_p, DSCOMMAND0) |
-                               CACHETHEN | DPARCKEN | MPARCKEN |
-                               USCBSIZE32 | CIOPARCKEN,
-                               DSCOMMAND0);
+               * switch back to normal mode...
                */
+              aic_outb(temp_p, aic_inb(temp_p, SFUNCT) & ~ALT_MODE, SFUNCT);
+              aic_outb(temp_p, CRCVALCHKEN | CRCENDCHKEN | CRCREQCHKEN |
+			       TARGCRCENDEN | TARGCRCCNTEN,
+                       CRCCONTROL1);
+              aic_outb(temp_p, ((aic_inb(temp_p, DSCOMMAND0) | USCBSIZE32 |
+                                 MPARCKEN | CIOPARCKEN | CACHETHEN) & 
+                               ~DPARCKEN), DSCOMMAND0);
+              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+              break;
+            case AHC_AIC7890:
+            case AHC_AIC7896:
+              aic_outb(temp_p, 0, SCAMCTL);
               aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
                                 CACHETHEN | MPARCKEN | USCBSIZE32 |
                                 CIOPARCKEN) & ~DPARCKEN, DSCOMMAND0);
-              /* FALLTHROUGH */
-            default:
-              /*
-               * We attempt to read a SEEPROM on *everything*.  If we fail,
-               * then we fail, but this covers things like 2910c cards that
-               * now have SEEPROMs with their 7856 chipset that we would
-               * otherwise ignore.  They still don't have a BIOS, but they
-               * have a SEEPROM that the SCSISelect utility on the Adaptec
-               * diskettes can configure.
-               */
               aic7xxx_load_seeprom(temp_p, &sxfrctl1);
               break;
             case AHC_AIC7850:
@@ -9233,14 +9771,13 @@
               aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
                                 CACHETHEN | MPARCKEN) & ~DPARCKEN,
                        DSCOMMAND0);
+              /* FALLTHROUGH */
+            default:
               aic7xxx_load_seeprom(temp_p, &sxfrctl1);
               break;
             case AHC_AIC7880:
               /*
-               * Only set the DSCOMMAND0 register if this is a Rev B.
-               * chipset.  For those, we also enable Ultra mode by
-               * force due to brain-damage on the part of some BIOSes
-               * We overload the devconfig variable here since we can.
+               * Check the rev of the chipset before we change DSCOMMAND0
                */
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
@@ -9272,6 +9809,7 @@
           {
             case AHC_AIC7895:
             case AHC_AIC7896:
+            case AHC_AIC7899:
               current_p = list_p;
               while(current_p != NULL)
               {
@@ -9315,6 +9853,7 @@
               break;
             case AHC_AIC7895:
             case AHC_AIC7896:
+            case AHC_AIC7899:
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
 #else
@@ -9364,7 +9903,7 @@
            */
           if (temp_p->features & AHC_ULTRA2)
           {
-            aic_outb(temp_p, RD_DFTHRSH_75 | WR_DFTHRSH_75, DFF_THRSH);
+            aic_outb(temp_p, RD_DFTHRSH_MAX | WR_DFTHRSH_MAX, DFF_THRSH);
           }
           else
           {
@@ -9512,7 +10051,7 @@
               }
             }
             /*
-             * Are we dealing with a 7985 where we need to sort the
+             * Are we dealing with a 7895/6/7/9 where we need to sort the
              * channels as well, if so, the bios_address values should
              * be the same
              */
@@ -9603,7 +10142,54 @@
   return (found);
 }
 
-#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
+static void aic7xxx_build_negotiation_cmnd(struct aic7xxx_host *p,
+                                           Scsi_Cmnd *old_cmd, int tindex);
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_allocate_negotiation_command
+ *
+ * Description:
+ *   allocate the actual command struct and fill in the gaps...
+ *-F*************************************************************************/
+static Scsi_Cmnd *
+aic7xxx_allocate_negotiation_command(struct aic7xxx_host *p,
+                                     Scsi_Cmnd *old_cmd, int tindex)
+{
+  Scsi_Cmnd *cmd;
+  char *buffer;
+
+  if (!(p->dev_dtr_cmnd[tindex] = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC)) )
+  {
+    return(NULL);
+  }
+  if (!(buffer = kmalloc(256, GFP_ATOMIC)))
+  {
+    kfree(p->dev_dtr_cmnd[tindex]);
+    p->dev_dtr_cmnd[tindex] = NULL;
+    return(NULL);
+  }
+  cmd = p->dev_dtr_cmnd[tindex];
+  memset(cmd, 0, sizeof(Scsi_Cmnd));
+  memcpy(cmd, old_cmd, sizeof(Scsi_Cmnd));
+  memset(&cmd->cmnd[0], 0, sizeof(cmd->cmnd));
+  memset(&cmd->data_cmnd[0], 0, sizeof(cmd->data_cmnd));
+  cmd->lun = 0;
+  cmd->request_bufflen = 255;
+  cmd->request_buffer = buffer;
+  cmd->use_sg = cmd->old_use_sg = cmd->sglist_len = 0;
+  cmd->bufflen = 0;
+  cmd->buffer = NULL;
+  cmd->underflow = 0;
+  cmd->cmd_len = 6;
+  cmd->cmnd[0] = cmd->data_cmnd[0] = INQUIRY;
+  cmd->cmnd[1] = cmd->data_cmnd[1] = 0;
+  cmd->cmnd[2] = cmd->data_cmnd[2] = 0;
+  cmd->cmnd[3] = cmd->data_cmnd[3] = 0;
+  cmd->cmnd[4] = cmd->data_cmnd[4] = 255; /* match what scsi.c does here */
+  cmd->cmnd[5] = cmd->data_cmnd[5] = 0;
+  return(cmd);
+}
 
 /*+F*************************************************************************
  * Function:
@@ -9616,6 +10202,117 @@
 static void
 aic7xxx_negotiation_complete(Scsi_Cmnd *cmd)
 {
+  unsigned int checksum;
+  int i;
+  int *ibuffer;
+  struct aic7xxx_host *p = (struct aic7xxx_host *)cmd->host->hostdata;
+  int tindex = TARGET_INDEX(cmd);
+  struct aic7xxx_syncrate *syncrate;
+
+  /*
+   * perform our minimalistic domain validation
+   */
+  if(p->dev_flags[tindex] & DEVICE_SCANNED)
+  {
+    ibuffer = (int *)cmd->request_buffer;
+    checksum = 0;
+    for(i = 0; i < (cmd->request_bufflen >> 2); i++)
+    {
+      checksum += ibuffer[i];
+    }
+    if( (checksum != p->dev_checksum[tindex]) &&
+        (p->transinfo[tindex].cur_offset != 0) )
+    {
+      unsigned int period = p->transinfo[tindex].cur_period;
+      unsigned char options = p->transinfo[tindex].cur_options;
+
+      if (p->needdv & (1<<tindex))
+      {
+        /*
+         * oops, we had a failure, lower the transfer rate and try again.  It's
+         * worth noting here that it might be wise to also check for typical
+         * wide setting on narrow cable type problems and try disabling wide
+         * instead of slowing down if those exist.  That's hard to do with simple
+         * checksums though.
+         */
+        if(aic7xxx_verbose & VERBOSE_NEGOTIATION) 
+        {
+          printk(INFO_LEAD "reducing SCSI transfer speed due to Domain "
+                 "validation failure.\n", p->host_no, CTL_OF_CMD(cmd));
+        }
+        if((syncrate = aic7xxx_find_syncrate(p, &period, 0, &options)) != NULL)
+        {
+          syncrate++;
+          if( (syncrate->rate[0] != NULL) &&
+              (!(p->features & AHC_ULTRA2) || (syncrate->sxfr_ultra2 == 0)) )
+          {
+            p->transinfo[tindex].goal_period = syncrate->period;
+            if( !(syncrate->sxfr_ultra2 & 0x40) )
+            {
+              p->transinfo[tindex].goal_options = 0;
+            }
+          }
+          else
+          {
+            p->transinfo[tindex].goal_offset = 0;
+            p->transinfo[tindex].goal_period = 0;
+            p->transinfo[tindex].goal_options = 0;
+          }
+          p->needppr |= (p->needppr_copy & (1<<tindex));
+          p->needsdtr |= (p->needsdtr_copy & (1<<tindex));
+          p->needwdtr |= (p->needwdtr_copy & (1<<tindex));
+        }
+        p->needdv &= ~(1<<tindex);
+      }
+      else
+      {
+        if(aic7xxx_verbose & VERBOSE_NEGOTIATION) 
+        {
+          printk(INFO_LEAD "Performing Domain validation.\n",
+                 p->host_no, CTL_OF_CMD(cmd));
+        }
+        /*
+         * Update the checksum in case the INQUIRY data has changed, maybe
+         * in relation to a change in the mode pages, or whatever.
+         */
+        p->dev_checksum[tindex] = checksum;
+        /*
+         * Signal that we are trying out the domain validation
+         */
+        p->needdv |= (1<<tindex);
+        /*
+         * Signal that we need to re-negotiate things, this also gets us our
+         * INQUIRY command to re-checksum off of.
+         */
+        p->needppr |= (p->needppr_copy & (1<<tindex));
+        p->needsdtr |= (p->needsdtr_copy & (1<<tindex));
+        p->needwdtr |= (p->needwdtr_copy & (1<<tindex));
+      }
+    } 
+    else
+    {
+      if( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+          (p->needdv & (1<<tindex)) )
+      {
+        printk(INFO_LEAD "Successfully completed Domain validation.\n",
+               p->host_no, CTL_OF_CMD(cmd));
+      }
+      /*
+       * We successfully did our checksum, so don't leave the needdv flag set
+       * in case we might have set it last time through.
+       */
+      p->needdv &= ~(1<<tindex);
+    }
+  }
+
+  p->dtr_pending &= ~(0x01 << tindex);
+  /*
+   * This looks recursive in the extreme, but if this was a WDTR negotiation
+   * and we didn't follow up with SDTR yet, then this will get it started.
+   * For all other cases, this should work out to be a no-op, unless we are
+   * doing domain validation and happen to need a new negotiation command.
+   */
+  aic7xxx_build_negotiation_cmnd(p, cmd->next, tindex);
   return;
 }
 
@@ -9632,81 +10329,63 @@
   int tindex)
 {
 
-  if ( (p->needwdtr & (1<<tindex)) && !(p->wdtr_pending & (1<<tindex)) )
+  if ( !(p->dtr_pending & (1<<tindex)) &&
+       ( (p->needppr & (1<<tindex)) ||
+         (p->needwdtr & (1<<tindex)) ||
+         (p->needsdtr & (1<<tindex)) ) )
   {
-    if(p->dev_wdtr_cmnd[tindex] == NULL)
+    if ( (p->dev_dtr_cmnd[tindex] == NULL) &&
+         (aic7xxx_allocate_negotiation_command(p, old_cmd, tindex) == NULL) )
     {
-      Scsi_Cmnd *cmd;
-
-      if (!(p->dev_wdtr_cmnd[tindex] = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC)) )
-      {
-        return;
-      }
-      cmd = p->dev_wdtr_cmnd[tindex];
-      memset(cmd, 0, sizeof(Scsi_Cmnd));
-      memcpy(cmd, old_cmd, sizeof(Scsi_Cmnd));
-      memset(&cmd->cmnd[0], 0, sizeof(cmd->cmnd));
-      memset(&cmd->data_cmnd[0], 0, sizeof(cmd->data_cmnd));
-      cmd->lun = 0;
-      cmd->request_bufflen = 0;
-      cmd->request_buffer = NULL;
-      cmd->use_sg = cmd->old_use_sg = cmd->sglist_len = 0;
-      cmd->bufflen = 0;
-      cmd->buffer = NULL;
-      cmd->underflow = 0;
-      cmd->cmd_len = 6;
+      return;
     }
     /*
-     * Before sending this thing out, we also amke the cmd->next pointer
+     * Before sending this thing out, we also make the cmd->next pointer
      * point to the real command so we can stuff any possible SENSE data
-     * intp the real command instead of this fake command.  This has to be
+     * into the real command instead of this fake command.  This has to be
      * done each time the command is built, not just the first time, hence
      * it's outside of the above if()...
      */
-    p->dev_wdtr_cmnd[tindex]->next = old_cmd;
-    aic7xxx_queue(p->dev_wdtr_cmnd[tindex], 
-                  aic7xxx_negotiation_complete);
-  }
-  else if ( (p->needsdtr & (1<<tindex)) && !(p->sdtr_pending & (1<<tindex)) &&
-            !(p->wdtr_pending & (1<<tindex)) )
-  {
-    if(p->dev_sdtr_cmnd[tindex] == NULL)
+    p->dev_dtr_cmnd[tindex]->next = old_cmd;
+    /*
+     * Clear the buffer so checksums come out right....
+     */
+    memset(p->dev_dtr_cmnd[tindex]->request_buffer, 0,
+           p->dev_dtr_cmnd[tindex]->request_bufflen);
+    /*
+     * Remove any commands for this particular device that might be on the
+     * waiting_scbs queue or qinfifo so that this command goes out first.
+     * This is vital for our implementation of domain validation.
+     */
+    pause_sequencer(p);
+    aic7xxx_search_qinfifo(p, old_cmd->target, old_cmd->channel, ALL_LUNS,
+                SCB_LIST_NULL, 0, TRUE, &p->delayed_scbs[tindex]);
+    unpause_sequencer(p, FALSE);
     {
-      Scsi_Cmnd *cmd;
+      struct aic7xxx_scb *scb, *next;
 
-      if (!(p->dev_sdtr_cmnd[tindex] = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC)) )
+      scb = p->waiting_scbs.head;
+      while(scb != NULL)
       {
-        return;
+        if( aic7xxx_match_scb(p, scb, old_cmd->target, old_cmd->channel,
+                              ALL_LUNS, SCB_LIST_NULL) )
+        {
+          next = scb->q_next;
+          scbq_remove(&p->waiting_scbs, scb);
+          scbq_insert_tail(&p->delayed_scbs[tindex], scb);
+          scb = next;
+        }
+        else
+        {
+          scb = scb->q_next;
+        }
       }
-      cmd = p->dev_sdtr_cmnd[tindex];
-      memset(cmd, 0, sizeof(Scsi_Cmnd));
-      memcpy(cmd, old_cmd, sizeof(Scsi_Cmnd));
-      memset(&cmd->cmnd[0], 0, sizeof(cmd->cmnd));
-      memset(&cmd->data_cmnd[0], 0, sizeof(cmd->data_cmnd));
-      cmd->lun = 0;
-      cmd->request_bufflen = 0;
-      cmd->request_buffer = NULL;
-      cmd->use_sg = cmd->old_use_sg = cmd->sglist_len = 0;
-      cmd->bufflen = 0;
-      cmd->buffer = NULL;
-      cmd->underflow = 0;
-      cmd->cmd_len = 6;
     }
-    /*
-     * Before sending this thing out, we also amke the cmd->next pointer
-     * point to the real command so we can stuff any possible SENSE data
-     * intp the real command instead of this fake command.  This has to be
-     * done each time the command is built, not just the first time, hence
-     * it's outside of the above if()...
-     */
-    p->dev_sdtr_cmnd[tindex]->next = old_cmd;
-    aic7xxx_queue(p->dev_sdtr_cmnd[tindex], 
+    aic7xxx_queue(p->dev_dtr_cmnd[tindex], 
                   aic7xxx_negotiation_complete);
   }
 }
 
-#endif
-
 #ifdef AIC7XXX_VERBOSE_DEBUGGING
 /*+F*************************************************************************
  * Function:
@@ -9744,8 +10423,9 @@
 {
   unsigned short mask;
   struct aic7xxx_hwscb *hscb;
+  unsigned char tindex = TARGET_INDEX(cmd);
 
-  mask = (0x01 << TARGET_INDEX(cmd));
+  mask = (0x01 << tindex);
   hscb = scb->hscb;
 
   /*
@@ -9757,11 +10437,12 @@
   if (p->discenable & mask)
   {
     hscb->control |= DISCENB;
-    if (p->tagenable & mask)
+    if ( (p->tagenable & mask) &&
+         (cmd->cmnd[0] != TEST_UNIT_READY) )
     {
       cmd->tag = hscb->tag;
-      p->dev_commands_sent[TARGET_INDEX(cmd)]++;
-      if (p->dev_commands_sent[TARGET_INDEX(cmd)] < 200)
+      p->dev_commands_sent[tindex]++;
+      if (p->dev_commands_sent[tindex] < 200)
       {
         hscb->control |= MSG_SIMPLE_Q_TAG;
         scb->tag_action = MSG_SIMPLE_Q_TAG;
@@ -9778,74 +10459,38 @@
           hscb->control |= MSG_SIMPLE_Q_TAG;
           scb->tag_action = MSG_SIMPLE_Q_TAG;
         }
-        p->dev_commands_sent[TARGET_INDEX(cmd)] = 0;
+        p->dev_commands_sent[tindex] = 0;
       }
     }
   }
-  if (p->dev_flags[TARGET_INDEX(cmd)] & DEVICE_SCANNED)
+  if ( cmd == p->dev_dtr_cmnd[tindex] )
   {
-#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
-    if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) )
+    p->dtr_pending |= mask;
+    scb->tag_action = 0;
+    if (p->dev_flags[tindex] & DEVICE_SCANNED)
     {
-      if (cmd == p->dev_wdtr_cmnd[TARGET_INDEX(cmd)])
+      hscb->control &= DISCENB;
+      hscb->control |= MK_MESSAGE;
+      if(p->needppr & mask)
       {
-        p->wdtr_pending |= mask;
-        scb->flags |= SCB_MSGOUT_WDTR;
-        hscb->control &= DISCENB;
-        hscb->control |= MK_MESSAGE;
-        scb->tag_action = 0;
+        scb->flags |= SCB_MSGOUT_PPR;
       }
-      else
+      else if(p->needwdtr & mask)
       {
-        aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd));
+        scb->flags |= SCB_MSGOUT_WDTR;
       }
-    }
-    else if ( (p->needsdtr & mask) && !(p->sdtr_pending & mask) &&
-              !(p->wdtr_pending & mask) )
-    {
-      if (cmd == p->dev_sdtr_cmnd[TARGET_INDEX(cmd)])
+      else if(p->needsdtr & mask)
       {
-        p->sdtr_pending |= mask;
         scb->flags |= SCB_MSGOUT_SDTR;
-        hscb->control &= DISCENB;
-        hscb->control |= MK_MESSAGE;
-        scb->tag_action = 0;
-      }
-      else if (cmd != p->dev_wdtr_cmnd[TARGET_INDEX(cmd)])
-      {
-        aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd));
       }
     }
-#else
-    if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) &&
-         !(p->sdtr_pending & mask) && (cmd->lun == 0) )
-    {
-      p->wdtr_pending |= mask;
-      scb->flags |= SCB_MSGOUT_WDTR;
-      hscb->control &= DISCENB;
-      hscb->control |= MK_MESSAGE;
-      scb->tag_action = 0;
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-      if (aic7xxx_verbose > 0xffff)
-        printk(INFO_LEAD "Building WDTR command.\n", p->host_no,
-               CTL_OF_CMD(cmd));
-#endif
-    }
-    else if ( (p->needsdtr & mask) && !(p->wdtr_pending & mask) &&
-              !(p->sdtr_pending & mask) && (cmd->lun == 0) )
-    {
-      p->sdtr_pending |= mask;
-      scb->flags |= SCB_MSGOUT_SDTR;
-      hscb->control &= DISCENB;
-      hscb->control |= MK_MESSAGE;
-      scb->tag_action = 0;
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-      if (aic7xxx_verbose > 0xffff)
-        printk(INFO_LEAD "Building SDTR command.\n", p->host_no,
-               CTL_OF_CMD(cmd));
-#endif
-    }
-#endif
+  }
+  if ( !(p->dtr_pending & mask) &&
+        ( (p->needppr & mask) ||
+          (p->needwdtr & mask) ||
+          (p->needsdtr & mask) ) )
+  {
+    aic7xxx_build_negotiation_cmnd(p, cmd, tindex);
   }
   hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
         ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
@@ -9897,7 +10542,6 @@
     scb->sg_count = cmd->use_sg;
     hscb->SG_segment_count = cmd->use_sg;
     hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[1]));
-
   }
   else
   {
@@ -9922,12 +10566,6 @@
       hscb->data_pointer = 0;
     }
   }
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-  if((cmd->cmnd[0] == TEST_UNIT_READY) && (aic7xxx_verbose & VERBOSE_PROBE2))
-  {
-    aic7xxx_print_scb(p, scb);
-  }
-#endif
 }
 
 /*+F*************************************************************************
@@ -10262,13 +10900,14 @@
     if(p->dev_flags[i] & DEVICE_PRESENT)
     {
       mask = (0x01 << i);
-      printk(INFO_LEAD "dev_flags=0x%x, WDTR:%c/%c/%c, SDTR:%c/%c/%c,"
-             " q_depth=%d:%d\n",
+      printk(INFO_LEAD "dev_flags=0x%x, Pending:%c, PPR:%c/%c, WDTR:%c/%c, "
+             "SDTR:%c/%c, q_depth=%d:%d\n",
         p->host_no, 0, i, 0, p->dev_flags[i],
-        (p->wdtr_pending & mask) ? 'Y' : 'N',
+        (p->dtr_pending & mask) ? 'Y' : 'N',
+        (p->needppr & mask) ? 'Y' : 'N',
+        (p->needppr_copy & mask) ? 'Y' : 'N',
         (p->needwdtr & mask) ? 'Y' : 'N',
         (p->needwdtr_copy & mask) ? 'Y' : 'N',
-        (p->sdtr_pending & mask) ? 'Y' : 'N',
         (p->needsdtr & mask) ? 'Y' : 'N',
         (p->needsdtr_copy & mask) ? 'Y' : 'N',
         p->dev_active_cmds[i],
@@ -10347,13 +10986,13 @@
      * We haven't found the offending SCB yet, and it should be around
      * somewhere, so go look for it in the cards SCBs.
      */
-    printk("SCBPTR CONTROL TAG PREV NEXT\n");
+    printk("SCBPTR CONTROL TAG NEXT\n");
     for(i=0; i<p->scb_data->maxhscbs; i++)
     {
       aic_outb(p, i, SCBPTR);
-      printk("   %3d      %02x  %02x   %02x   %02x\n", i,
+      printk("   %3d      %02x  %02x   %02x\n", i,
              aic_inb(p, SCB_CONTROL), aic_inb(p, SCB_TAG),
-             aic_inb(p, SCB_PREV), aic_inb(p, SCB_NEXT));
+             aic_inb(p, SCB_NEXT));
     }
   }
   
@@ -10569,21 +11208,13 @@
   if ((found == 0) && (scb->flags & SCB_WAITINGQ))
   {
     int tindex = TARGET_INDEX(cmd);
-#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
     unsigned short mask;
 
     mask = (1 << tindex);
 
-    if (p->wdtr_pending & mask)
-    {
-      if (p->dev_wdtr_cmnd[tindex]->next != cmd)
-        found = 1;
-      else
-        found = 0;
-    }
-    else if (p->sdtr_pending & mask)
+    if (p->dtr_pending & mask)
     {
-      if (p->dev_sdtr_cmnd[tindex]->next != cmd)
+      if (p->dev_dtr_cmnd[tindex]->next != cmd)
         found = 1;
       else
         found = 0;
@@ -10606,7 +11237,6 @@
       DRIVER_UNLOCK
       return(SCSI_ABORT_PENDING);
     }
-#endif
     if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) 
       printk(INFO_LEAD "SCB found on waiting list and "
           "aborted.\n", p->host_no, CTL_OF_SCB(scb));
@@ -10864,6 +11494,8 @@
       if(aic7xxx_verbose & VERBOSE_RESET_RETURN)
         printk(INFO_LEAD "SCB on qoutfifo, returning.\n", p->host_no,
           CTL_OF_SCB(scb));
+      aic7xxx_run_done_queue(p, TRUE);
+      aic7xxx_run_waiting_queues(p);
       unpause_sequencer(p, FALSE);
       DRIVER_UNLOCK
       return(SCSI_RESET_NOT_RUNNING);
@@ -11034,16 +11666,21 @@
 int
 aic7xxx_biosparam(Disk *disk, kdev_t dev, int geom[])
 {
-  int heads, sectors, cylinders;
+  int heads, sectors, cylinders, ret;
   struct aic7xxx_host *p;
+  struct buffer_head *bh;
 
   p = (struct aic7xxx_host *) disk->device->host->hostdata;
+  bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, 1024);
 
-  /*
-   * XXX - if I could portably find the card's configuration
-   *       information, then this could be autodetected instead
-   *       of left to a boot-time switch.
-   */
+  if ( bh )
+  {
+    ret = scsi_partsize(bh, disk->capacity, &geom[2], &geom[0], &geom[1]);
+    brelse(bh);
+    if ( ret != -1 )
+      return(ret);
+  }
+  
   heads = 64;
   sectors = 32;
   cylinders = disk->capacity / (heads * sectors);
@@ -11150,6 +11787,12 @@
           0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
           0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
           0xfe, 0xff} },
+    {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7892*/
+          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9c, 0x9f,
+          0xe0, 0xf1, 0xf4, 0xfc} },
+    {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7899*/
+          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9c, 0x9f,
+          0xe0, 0xf1, 0xf4, 0xfc} },
   };
 #ifdef CONFIG_PCI
   static struct register_ranges cards_ns[] = {
@@ -11164,6 +11807,10 @@
     { 5, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3} },
     { 6, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47,
           0xdc, 0xe3} },
+    { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3,
+          0xff, 0xff} },
+    { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3,
+          0xff, 0xff} },
     { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3,
           0xff, 0xff} }
   };
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c
--- v2.3.5/linux/drivers/scsi/aic7xxx_proc.c	Mon Jan  4 12:57:44 1999
+++ linux/drivers/scsi/aic7xxx_proc.c	Wed Jun  9 16:59:15 1999
@@ -160,21 +160,17 @@
   size += sprintf(BLS, "%s", AIC7XXX_H_VERSION);
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Compile Options:\n");
-#ifdef AIC7XXX_RESET_DELAY
-  size += sprintf(BLS, "  AIC7XXX_RESET_DELAY    : %d\n", AIC7XXX_RESET_DELAY);
+#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+  size += sprintf(BLS, "  TCQ Enabled By Default : Enabled\n");
+#else
+  size += sprintf(BLS, "  TCQ Enabled By Default : Disabled\n");
 #endif
-  size += sprintf(BLS, "  AIC7XXX_TAGGED_QUEUEING: Adapter Support Enabled\n");
-  size += sprintf(BLS, "                             Check below to see "
-                       "which\n"
-                       "                             devices use tagged "
-                       "queueing\n");
-  size += sprintf(BLS, "  AIC7XXX_PAGE_ENABLE    : Enabled (This is no longer "
-                       "an option)\n");
 #ifdef AIC7XXX_PROC_STATS
   size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Enabled\n");
 #else
   size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Disabled\n");
 #endif
+  size += sprintf(BLS, "  AIC7XXX_RESET_DELAY    : %d\n", AIC7XXX_RESET_DELAY);
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Adapter Configuration:\n");
   size += sprintf(BLS, "           SCSI Adapter: %s\n",
@@ -194,8 +190,21 @@
     }
     if (p->features & AHC_WIDE)
       wide = "Wide ";
-    if (p->features & AHC_ULTRA2)
-      ultra = "Ultra2-LVD/SE ";
+    if (p->features & AHC_ULTRA3)
+    {
+      switch(p->chip & AHC_CHIPID_MASK)
+      {
+        case AHC_AIC7892:
+        case AHC_AIC7899:
+          ultra = "Ultra-160/m LVD/SE ";
+          break;
+        default:
+          ultra = "Ultra-3 LVD/SE ";
+          break;
+      }
+    }
+    else if (p->features & AHC_ULTRA2)
+      ultra = "Ultra-2 LVD/SE ";
     else if (p->features & AHC_ULTRA)
       ultra = "Ultra ";
     size += sprintf(BLS, "                           %s%sController%s\n",
@@ -250,11 +259,7 @@
   }
   size += sprintf(BLS, " Tag Queue Enable Flags: 0x%04x\n", p->tagenable);
   size += sprintf(BLS, "Ordered Queue Tag Flags: 0x%04x\n", p->orderedtag);
-#ifdef AIC7XXX_CMDS_PER_LUN
-  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_LUN);
-#else
-  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", 8);
-#endif
+  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_DEVICE);
   size += sprintf(BLS, "    Tagged Queue By Device array for aic7xxx host "
                        "instance %d:\n", p->instance);
   size += sprintf(BLS, "      {");
@@ -295,11 +300,12 @@
     if (p->transinfo[target].cur_offset != 0)
     {
       struct aic7xxx_syncrate *sync_rate;
+      unsigned char options = p->transinfo[target].cur_options;
       int period = p->transinfo[target].cur_period;
       int rate = (p->transinfo[target].cur_width ==
                   MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
 
-      sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2);
+      sync_rate = aic7xxx_find_syncrate(p, &period, 0, &options);
       if (sync_rate != NULL)
       {
         size += sprintf(BLS, "%s MByte/sec, offset %d\n",
@@ -313,18 +319,21 @@
       }
     }
     size += sprintf(BLS, "  Transinfo settings: ");
-    size += sprintf(BLS, "current(%d/%d/%d), ",
+    size += sprintf(BLS, "current(%d/%d/%d/%d), ",
                     p->transinfo[target].cur_period,
                     p->transinfo[target].cur_offset,
-                    p->transinfo[target].cur_width);
-    size += sprintf(BLS, "goal(%d/%d/%d), ",
+                    p->transinfo[target].cur_width,
+                    p->transinfo[target].cur_options);
+    size += sprintf(BLS, "goal(%d/%d/%d/%d), ",
                     p->transinfo[target].goal_period,
                     p->transinfo[target].goal_offset,
-                    p->transinfo[target].goal_width);
-    size += sprintf(BLS, "user(%d/%d/%d)\n",
+                    p->transinfo[target].goal_width,
+                    p->transinfo[target].goal_options);
+    size += sprintf(BLS, "user(%d/%d/%d/%d)\n",
                     p->transinfo[target].user_period,
                     p->transinfo[target].user_offset,
-                    p->transinfo[target].user_width);
+                    p->transinfo[target].user_width,
+                    p->transinfo[target].user_options);
 #ifdef AIC7XXX_PROC_STATS
     size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
         sp->r_total + sp->w_total, sp->r_total, sp->w_total);
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/aic7xxx_reg.h linux/drivers/scsi/aic7xxx_reg.h
--- v2.3.5/linux/drivers/scsi/aic7xxx_reg.h	Thu Oct  8 08:07:34 1998
+++ linux/drivers/scsi/aic7xxx_reg.h	Wed Jun  9 16:59:15 1999
@@ -63,6 +63,16 @@
 
 #define	STCNT           		0x08
 
+#define	OPTIONMODE      		0x08
+#define		AUTORATEEN      	0x80
+#define		AUTOACKEN       	0x40
+#define		ATNMGMNTEN      	0x20
+#define		BUSFREEREV      	0x10
+#define		EXPPHASEDIS     	0x08
+#define		SCSIDATL_IMGEN  	0x04
+#define		AUTO_MSGOUT_DE  	0x02
+#define		DIS_MSGIN_DUALEDGE	0x01
+
 #define	CLRSINT0        		0x0b
 #define		CLRSELDO        	0x40
 #define		CLRSELDI        	0x20
@@ -102,8 +112,14 @@
 
 #define	SSTAT2          		0x0d
 #define		OVERRUN         	0x80
+#define		SHVALID         	0x40
+#define		WIDE_RES        	0x20
 #define		SFCNT           	0x1f
 #define		EXP_ACTIVE      	0x10
+#define		CRCVALERR       	0x08
+#define		CRCENDERR       	0x04
+#define		CRCREQERR       	0x02
+#define		DUAL_EDGE_ERROR 	0x01
 
 #define	SSTAT3          		0x0e
 #define		SCSICNT         	0xf0
@@ -412,6 +428,7 @@
 #define		DPARERR         	0x10
 #define		SQPARERR        	0x08
 #define		ILLOPCODE       	0x04
+#define		DSCTMOUT        	0x02
 #define		ILLSADDR        	0x02
 #define		ILLHADDR        	0x01
 
@@ -436,11 +453,30 @@
 
 #define	QINCNT          		0x9c
 
+#define	SCSIDATL_IMG    		0x9c
+
 #define	QOUTFIFO        		0x9d
 
+#define	CRCCONTROL1     		0x9d
+#define		CRCONSEEN       	0x80
+#define		TARGCRCCNTEN    	0x40
+#define		CRCVALCHKEN     	0x40
+#define		CRCENDCHKEN     	0x20
+#define		CRCREQCHKEN     	0x10
+#define		TARGCRCENDEN    	0x08
+
+#define	SCSIPHASE       		0x9e
+#define		SP_STATUS       	0x20
+#define		SP_COMMAND      	0x10
+#define		SP_MSG_IN       	0x08
+#define		SP_MSG_OUT      	0x04
+#define		SP_DATA_IN      	0x02
+#define		SP_DATA_OUT     	0x01
+
 #define	QOUTCNT         		0x9e
 
 #define	SFUNCT          		0x9f
+#define		ALT_MODE        	0x80
 
 #define	SCB_CONTROL     		0xa0
 #define		MK_MESSAGE      	0x80
@@ -525,14 +561,20 @@
 
 #define	HNSCB_QOFF      		0xf4
 
+#define	HESCB_QOFF      		0xf5
+
 #define	SNSCB_QOFF      		0xf6
 
+#define	SESCB_QOFF      		0xf7
+
 #define	SDSCB_QOFF      		0xf8
 
 #define	QOFF_CTLSTA     		0xfa
+#define		ESTABLISH_SCB_AVAIL	0x80
 #define		SCB_AVAIL       	0x40
 #define		SNSCB_ROLLOVER  	0x20
 #define		SDSCB_ROLLOVER  	0x10
+#define		SESCB_ROLLOVER  	0x08
 #define		SCB_QSIZE       	0x07
 #define		SCB_QSIZE_256   	0x06
 
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/aic7xxx_seq.c linux/drivers/scsi/aic7xxx_seq.c
--- v2.3.5/linux/drivers/scsi/aic7xxx_seq.c	Thu Oct  8 08:07:34 1998
+++ linux/drivers/scsi/aic7xxx_seq.c	Wed Jun  9 16:59:16 1999
@@ -3,38 +3,44 @@
   */
 static unsigned char seqprog[] = {
 	0xff, 0x6a, 0x06, 0x08,
+	0x7f, 0x02, 0x04, 0x08,
 	0x32, 0x6a, 0x00, 0x00,
 	0x12, 0x6a, 0x00, 0x00,
 	0xff, 0x6a, 0xd6, 0x09,
 	0xff, 0x6a, 0xdc, 0x09,
-	0x00, 0x65, 0x38, 0x59,
+	0x00, 0x65, 0x42, 0x59,
 	0xf7, 0x01, 0x02, 0x08,
 	0xff, 0x4e, 0xc8, 0x08,
 	0xbf, 0x60, 0xc0, 0x08,
-	0x60, 0x0b, 0x7c, 0x68,
+	0x60, 0x0b, 0x86, 0x68,
 	0x40, 0x00, 0x0e, 0x68,
 	0x08, 0x1f, 0x3e, 0x10,
-	0x60, 0x0b, 0x7c, 0x68,
+	0x60, 0x0b, 0x86, 0x68,
 	0x40, 0x00, 0x0e, 0x68,
 	0x08, 0x1f, 0x3e, 0x10,
-	0xff, 0x3e, 0x3e, 0x60,
-	0x40, 0xfa, 0x10, 0x78,
+	0xff, 0x3e, 0x4a, 0x60,
+	0x40, 0xfa, 0x12, 0x78,
 	0xff, 0xf6, 0xd4, 0x08,
 	0x01, 0x4e, 0x9c, 0x18,
 	0x40, 0x60, 0xc0, 0x00,
-	0x00, 0x4d, 0x10, 0x70,
+	0x00, 0x4d, 0x12, 0x70,
 	0x01, 0x4e, 0x9c, 0x18,
 	0xbf, 0x60, 0xc0, 0x08,
-	0x00, 0x6a, 0x72, 0x5c,
+	0x00, 0x6a, 0x92, 0x5c,
 	0xff, 0x4e, 0xc8, 0x18,
-	0x02, 0x6a, 0x88, 0x5b,
+	0x02, 0x6a, 0xa8, 0x5b,
 	0xff, 0x52, 0x20, 0x09,
 	0x0d, 0x6a, 0x6a, 0x00,
-	0x00, 0x52, 0xfe, 0x5b,
+	0x00, 0x52, 0x1e, 0x5c,
+	0x03, 0xb0, 0x52, 0x31,
+	0xff, 0xb0, 0x52, 0x09,
+	0xff, 0xb1, 0x54, 0x09,
+	0xff, 0xb2, 0x56, 0x09,
+	0xff, 0xa3, 0x50, 0x09,
 	0xff, 0x3e, 0x74, 0x09,
 	0xff, 0x90, 0x7c, 0x08,
 	0xff, 0x3e, 0x20, 0x09,
-	0x00, 0x65, 0x44, 0x58,
+	0x00, 0x65, 0x50, 0x58,
 	0x00, 0x65, 0x0e, 0x40,
 	0xf7, 0x1f, 0xca, 0x08,
 	0x08, 0xa1, 0xc8, 0x08,
@@ -47,51 +53,50 @@
 	0x0f, 0x05, 0x0a, 0x08,
 	0x00, 0x05, 0x0a, 0x00,
 	0x5a, 0x6a, 0x00, 0x04,
-	0x12, 0x65, 0xc8, 0x00,
-	0x00, 0x01, 0x02, 0x00,
+	0x12, 0x65, 0x02, 0x00,
 	0x31, 0x6a, 0xca, 0x00,
-	0x80, 0x37, 0x64, 0x68,
+	0x80, 0x37, 0x6e, 0x68,
 	0xff, 0x65, 0xca, 0x18,
 	0xff, 0x37, 0xdc, 0x08,
 	0xff, 0x6e, 0xc8, 0x08,
-	0x00, 0x6c, 0x6c, 0x78,
+	0x00, 0x6c, 0x76, 0x78,
 	0x20, 0x01, 0x02, 0x00,
 	0x4c, 0x37, 0xc8, 0x28,
-	0x08, 0x1f, 0x74, 0x78,
+	0x08, 0x1f, 0x7e, 0x78,
 	0x08, 0x37, 0x6e, 0x00,
 	0x08, 0x64, 0xc8, 0x00,
 	0x70, 0x64, 0xca, 0x18,
 	0xff, 0x6c, 0x0a, 0x08,
 	0x20, 0x64, 0xca, 0x18,
 	0xff, 0x6c, 0x08, 0x0c,
-	0x40, 0x0b, 0x04, 0x69,
-	0x80, 0x0b, 0xf6, 0x78,
+	0x40, 0x0b, 0x0e, 0x69,
+	0x80, 0x0b, 0x00, 0x79,
 	0xa4, 0x6a, 0x06, 0x00,
 	0x40, 0x6a, 0x16, 0x00,
-	0x10, 0x03, 0xf2, 0x78,
+	0x10, 0x03, 0xfc, 0x78,
 	0xff, 0x50, 0xc8, 0x08,
 	0x88, 0x6a, 0xcc, 0x00,
-	0x49, 0x6a, 0xee, 0x5b,
+	0x49, 0x6a, 0x0e, 0x5c,
 	0x01, 0x6a, 0x26, 0x01,
 	0xff, 0x6a, 0xca, 0x08,
 	0x08, 0x01, 0x02, 0x00,
-	0x02, 0x0b, 0x92, 0x78,
+	0x02, 0x0b, 0x9c, 0x78,
 	0xf7, 0x01, 0x02, 0x08,
 	0xff, 0x06, 0xcc, 0x08,
 	0xff, 0x66, 0x32, 0x09,
 	0x01, 0x65, 0xca, 0x18,
-	0x80, 0x66, 0xa0, 0x78,
+	0x80, 0x66, 0xaa, 0x78,
 	0xff, 0x66, 0xa2, 0x08,
-	0x10, 0x03, 0x90, 0x68,
+	0x10, 0x03, 0x9a, 0x68,
 	0xfc, 0x65, 0xc8, 0x18,
-	0x00, 0x65, 0xa8, 0x48,
+	0x00, 0x65, 0xb2, 0x48,
 	0xff, 0x6a, 0x32, 0x01,
 	0x01, 0x64, 0x18, 0x19,
 	0xff, 0x6a, 0x1a, 0x09,
 	0xff, 0x6a, 0x1c, 0x09,
 	0x84, 0x6a, 0x06, 0x00,
 	0x08, 0x01, 0x02, 0x00,
-	0x02, 0x0b, 0xb2, 0x78,
+	0x02, 0x0b, 0xbc, 0x78,
 	0xff, 0x06, 0xc8, 0x08,
 	0xff, 0x64, 0x32, 0x09,
 	0xff, 0x6a, 0xca, 0x08,
@@ -105,33 +110,33 @@
 	0x0b, 0x65, 0xca, 0x18,
 	0xff, 0x65, 0xc8, 0x08,
 	0x00, 0x8c, 0x18, 0x19,
-	0x02, 0x0b, 0xce, 0x78,
-	0x01, 0x65, 0xd4, 0x60,
+	0x02, 0x0b, 0xd8, 0x78,
+	0x01, 0x65, 0xde, 0x60,
 	0xf7, 0x01, 0x02, 0x08,
 	0xff, 0x06, 0x32, 0x09,
 	0xff, 0x65, 0xca, 0x18,
-	0xff, 0x65, 0xce, 0x68,
+	0xff, 0x65, 0xd8, 0x68,
 	0x0a, 0x93, 0x26, 0x01,
-	0x00, 0x65, 0x64, 0x5c,
-	0x40, 0x51, 0xe6, 0x78,
+	0x00, 0x65, 0x84, 0x5c,
+	0x40, 0x51, 0xf0, 0x78,
 	0xe4, 0x6a, 0x06, 0x00,
 	0x08, 0x01, 0x02, 0x00,
-	0x04, 0x6a, 0x18, 0x5b,
+	0x04, 0x6a, 0x40, 0x5b,
 	0x01, 0x50, 0xa0, 0x18,
-	0x00, 0x50, 0xec, 0xe0,
+	0x00, 0x50, 0xf6, 0xe0,
 	0xff, 0x6a, 0xa0, 0x08,
 	0xff, 0x6a, 0x3a, 0x01,
 	0x02, 0x6a, 0x22, 0x01,
-	0x40, 0x51, 0xf2, 0x68,
+	0x40, 0x51, 0xfc, 0x68,
 	0xff, 0x6a, 0x06, 0x08,
 	0x00, 0x65, 0x0e, 0x40,
 	0x20, 0x6a, 0x16, 0x00,
 	0xf0, 0x19, 0x6e, 0x08,
 	0x08, 0x6a, 0x18, 0x00,
 	0x08, 0x11, 0x22, 0x00,
-	0x08, 0x6a, 0x5a, 0x58,
+	0x08, 0x6a, 0x66, 0x58,
 	0x08, 0x6a, 0x68, 0x00,
-	0x00, 0x65, 0x18, 0x41,
+	0x00, 0x65, 0x22, 0x41,
 	0x12, 0x6a, 0x00, 0x00,
 	0x40, 0x6a, 0x16, 0x00,
 	0xff, 0x3e, 0x20, 0x09,
@@ -139,362 +144,373 @@
 	0xff, 0xa1, 0x6e, 0x08,
 	0x08, 0x6a, 0x18, 0x00,
 	0x08, 0x11, 0x22, 0x00,
-	0x08, 0x6a, 0x5a, 0x58,
+	0x08, 0x6a, 0x66, 0x58,
 	0x80, 0x6a, 0x68, 0x00,
 	0x80, 0x36, 0x6c, 0x00,
-	0x00, 0x65, 0xd2, 0x5b,
+	0x00, 0x65, 0xf2, 0x5b,
 	0xff, 0x3d, 0xc8, 0x08,
-	0xbf, 0x64, 0x48, 0x79,
-	0x80, 0x64, 0xf0, 0x71,
-	0xa0, 0x64, 0x0e, 0x72,
-	0xc0, 0x64, 0x08, 0x72,
-	0xe0, 0x64, 0x52, 0x72,
+	0xbf, 0x64, 0x58, 0x79,
+	0x80, 0x64, 0x0e, 0x72,
+	0xa0, 0x64, 0x3a, 0x72,
+	0xc0, 0x64, 0x32, 0x72,
+	0xe0, 0x64, 0x7a, 0x72,
 	0x01, 0x6a, 0x22, 0x01,
-	0x00, 0x65, 0x18, 0x41,
+	0x00, 0x65, 0x22, 0x41,
 	0xf7, 0x11, 0x22, 0x08,
-	0x00, 0x65, 0x38, 0x59,
+	0x00, 0x65, 0x42, 0x59,
 	0xff, 0x06, 0xd4, 0x08,
 	0xf7, 0x01, 0x02, 0x08,
-	0x09, 0x0c, 0x32, 0x79,
+	0x09, 0x0c, 0x3c, 0x79,
 	0x08, 0x0c, 0x0e, 0x68,
 	0x01, 0x6a, 0x22, 0x01,
 	0xff, 0x6a, 0x26, 0x09,
+	0x02, 0x6a, 0x08, 0x30,
 	0xff, 0x6a, 0x08, 0x08,
 	0xdf, 0x01, 0x02, 0x08,
 	0x01, 0x6a, 0x7a, 0x00,
-	0x03, 0x36, 0x6c, 0x0c,
+	0xff, 0x6a, 0x6c, 0x0c,
+	0x03, 0xa9, 0x18, 0x31,
+	0x03, 0xa9, 0x10, 0x30,
 	0x08, 0x6a, 0xcc, 0x00,
-	0xa9, 0x6a, 0xe8, 0x5b,
-	0x00, 0x65, 0x66, 0x41,
+	0xa9, 0x6a, 0x08, 0x5c,
+	0x00, 0x65, 0x78, 0x41,
 	0xa8, 0x6a, 0x6a, 0x00,
 	0x79, 0x6a, 0x6a, 0x00,
-	0x40, 0x3d, 0x50, 0x69,
+	0x40, 0x3d, 0x60, 0x69,
 	0x04, 0x35, 0x6a, 0x00,
-	0x00, 0x65, 0x3a, 0x5b,
+	0x00, 0x65, 0x62, 0x5b,
 	0x80, 0x6a, 0xd4, 0x01,
-	0x10, 0x36, 0x42, 0x69,
+	0x10, 0x36, 0x4e, 0x69,
 	0x10, 0x36, 0x6c, 0x00,
 	0x07, 0xac, 0x10, 0x31,
+	0x03, 0x8c, 0x10, 0x30,
+	0x05, 0xa3, 0x70, 0x30,
 	0x88, 0x6a, 0xcc, 0x00,
-	0xac, 0x6a, 0xe0, 0x5b,
-	0x00, 0x65, 0xda, 0x5b,
-	0xff, 0xa3, 0x70, 0x08,
-	0x39, 0x6a, 0xcc, 0x00,
-	0xa4, 0x6a, 0xe6, 0x5b,
-	0xff, 0x38, 0x74, 0x69,
+	0xac, 0x6a, 0x00, 0x5c,
+	0x00, 0x65, 0xfa, 0x5b,
+	0x38, 0x6a, 0xcc, 0x00,
+	0xa3, 0x6a, 0x04, 0x5c,
+	0xff, 0x38, 0x88, 0x69,
 	0x80, 0x02, 0x04, 0x00,
 	0xe7, 0x35, 0x6a, 0x08,
 	0x03, 0x69, 0x18, 0x31,
+	0x03, 0x69, 0x10, 0x30,
 	0xff, 0x6a, 0x10, 0x00,
 	0xff, 0x6a, 0x12, 0x00,
 	0xff, 0x6a, 0x14, 0x00,
-	0x01, 0x38, 0x7a, 0x61,
-	0x02, 0xfc, 0xf8, 0x01,
+	0x01, 0x38, 0x8c, 0x61,
 	0xbf, 0x35, 0x6a, 0x08,
 	0xff, 0x69, 0xca, 0x08,
 	0xff, 0x35, 0x26, 0x09,
-	0x04, 0x0b, 0x7e, 0x69,
-	0x04, 0x0b, 0x8a, 0x69,
-	0x10, 0x0c, 0x80, 0x79,
-	0x04, 0x0b, 0x88, 0x69,
+	0x04, 0x0b, 0x90, 0x69,
+	0x04, 0x0b, 0x9c, 0x69,
+	0x10, 0x0c, 0x92, 0x79,
+	0x04, 0x0b, 0x9a, 0x69,
 	0xff, 0x6a, 0xca, 0x08,
-	0x00, 0x35, 0x22, 0x5b,
-	0x80, 0x02, 0xd6, 0x69,
-	0xff, 0x65, 0xc8, 0x79,
+	0x00, 0x35, 0x4a, 0x5b,
+	0x80, 0x02, 0xf0, 0x69,
+	0xff, 0x65, 0xe0, 0x79,
 	0xff, 0x38, 0x70, 0x18,
-	0xff, 0x38, 0xc8, 0x79,
-	0x80, 0xea, 0xaa, 0x61,
+	0xff, 0x38, 0xe0, 0x79,
+	0x80, 0xea, 0xbc, 0x61,
 	0xef, 0x38, 0xc8, 0x18,
 	0x80, 0x6a, 0xc8, 0x00,
-	0x00, 0x65, 0x9c, 0x49,
+	0x00, 0x65, 0xae, 0x49,
 	0x33, 0x38, 0xc8, 0x28,
 	0xff, 0x64, 0xd0, 0x09,
 	0x04, 0x39, 0xc0, 0x31,
 	0x09, 0x6a, 0xd6, 0x01,
-	0x80, 0xeb, 0xa2, 0x79,
+	0x80, 0xeb, 0xb4, 0x79,
 	0xf7, 0xeb, 0xd6, 0x09,
-	0x08, 0xeb, 0xa6, 0x69,
+	0x08, 0xeb, 0xb8, 0x69,
 	0x01, 0x6a, 0xd6, 0x01,
 	0x08, 0xe9, 0x10, 0x31,
+	0x03, 0x8c, 0x10, 0x30,
 	0x88, 0x6a, 0xcc, 0x00,
-	0x39, 0x6a, 0xe6, 0x5b,
+	0x39, 0x6a, 0x06, 0x5c,
 	0x08, 0x6a, 0x18, 0x01,
 	0xff, 0x6a, 0x1a, 0x09,
 	0xff, 0x6a, 0x1c, 0x09,
 	0x0d, 0x93, 0x26, 0x01,
-	0x00, 0x65, 0x64, 0x5c,
-	0x88, 0x6a, 0x54, 0x5c,
-	0x00, 0x65, 0xda, 0x5b,
+	0x00, 0x65, 0x84, 0x5c,
+	0x88, 0x6a, 0x74, 0x5c,
+	0x00, 0x65, 0xfa, 0x5b,
 	0xff, 0x6a, 0xc8, 0x08,
 	0x08, 0x39, 0x72, 0x18,
 	0x00, 0x3a, 0x74, 0x20,
-	0x10, 0x0c, 0x66, 0x79,
-	0x80, 0x93, 0x26, 0x01,
-	0x00, 0x65, 0xe0, 0x59,
+	0x01, 0x0c, 0xd8, 0x79,
+	0x10, 0x0c, 0x78, 0x79,
+	0xff, 0x35, 0x26, 0x09,
+	0x04, 0x0b, 0xde, 0x69,
+	0x00, 0x65, 0xf8, 0x59,
+	0x03, 0x08, 0x52, 0x31,
+	0xff, 0x38, 0x50, 0x09,
 	0xff, 0x08, 0x52, 0x09,
 	0xff, 0x09, 0x54, 0x09,
 	0xff, 0x0a, 0x56, 0x09,
 	0xff, 0x38, 0x50, 0x09,
-	0x12, 0x01, 0x02, 0x00,
-	0x00, 0x65, 0x18, 0x41,
-	0x00, 0x65, 0xe0, 0x59,
-	0x12, 0x01, 0x02, 0x00,
+	0x00, 0x65, 0x22, 0x41,
+	0x00, 0x65, 0xf8, 0x59,
 	0x7f, 0x02, 0x04, 0x08,
 	0xe1, 0x6a, 0x22, 0x01,
-	0x00, 0x65, 0x18, 0x41,
-	0x04, 0x93, 0xea, 0x69,
+	0x00, 0x65, 0x22, 0x41,
+	0x04, 0x93, 0x02, 0x6a,
 	0xdf, 0x93, 0x26, 0x09,
-	0x20, 0x93, 0xe4, 0x69,
+	0x20, 0x93, 0xfc, 0x69,
 	0x02, 0x93, 0x26, 0x01,
-	0x01, 0x94, 0xe6, 0x79,
+	0x01, 0x94, 0xfe, 0x79,
 	0xd7, 0x93, 0x26, 0x09,
-	0x08, 0x93, 0xec, 0x69,
+	0x08, 0x93, 0x04, 0x6a,
+	0x03, 0x08, 0x52, 0x31,
+	0xff, 0x38, 0x50, 0x09,
+	0x12, 0x01, 0x02, 0x00,
 	0xff, 0x6a, 0xd4, 0x0c,
-	0x00, 0x65, 0x3a, 0x5b,
-	0x02, 0xfc, 0xf8, 0x01,
+	0x00, 0x65, 0x62, 0x5b,
 	0x05, 0xb4, 0x10, 0x31,
 	0x02, 0x6a, 0x1a, 0x31,
+	0x03, 0x8c, 0x10, 0x30,
 	0x88, 0x6a, 0xcc, 0x00,
-	0xb4, 0x6a, 0xe4, 0x5b,
+	0xb4, 0x6a, 0x04, 0x5c,
 	0xff, 0x6a, 0x1a, 0x09,
 	0xff, 0x6a, 0x1c, 0x09,
-	0x00, 0x65, 0xda, 0x5b,
-	0x3d, 0x6a, 0x22, 0x5b,
-	0xac, 0x6a, 0x22, 0x5b,
-	0x00, 0x65, 0x18, 0x41,
-	0x00, 0x65, 0x3a, 0x5b,
+	0x00, 0x65, 0xfa, 0x5b,
+	0x3d, 0x6a, 0x4a, 0x5b,
+	0xac, 0x6a, 0x26, 0x01,
+	0x04, 0x0b, 0x24, 0x6a,
+	0x01, 0x0b, 0x2a, 0x6a,
+	0x10, 0x0c, 0x26, 0x7a,
+	0xd7, 0x93, 0x26, 0x09,
+	0x08, 0x93, 0x2c, 0x6a,
+	0x12, 0x01, 0x02, 0x00,
+	0x00, 0x65, 0x22, 0x41,
+	0x00, 0x65, 0x62, 0x5b,
 	0xff, 0x06, 0x44, 0x09,
-	0x00, 0x65, 0x18, 0x41,
+	0x00, 0x65, 0x22, 0x41,
+	0x10, 0x3d, 0x06, 0x00,
 	0xff, 0x34, 0xca, 0x08,
-	0x80, 0x65, 0x32, 0x62,
+	0x80, 0x65, 0x5e, 0x62,
 	0x0f, 0xa1, 0xca, 0x08,
 	0x07, 0xa1, 0xca, 0x08,
 	0x40, 0xa0, 0xc8, 0x08,
 	0x00, 0x65, 0xca, 0x00,
 	0x80, 0x65, 0xca, 0x00,
-	0x80, 0xa0, 0x22, 0x7a,
+	0x80, 0xa0, 0x4e, 0x7a,
 	0xff, 0x65, 0x0c, 0x08,
-	0x00, 0x65, 0x34, 0x42,
-	0x20, 0xa0, 0x3a, 0x7a,
+	0x00, 0x65, 0x60, 0x42,
+	0x20, 0xa0, 0x66, 0x7a,
 	0xff, 0x65, 0x0c, 0x08,
-	0x00, 0x65, 0xd2, 0x5b,
-	0xa0, 0x3d, 0x46, 0x62,
+	0x00, 0x65, 0xf2, 0x5b,
+	0xa0, 0x3d, 0x6e, 0x62,
 	0x23, 0xa0, 0x0c, 0x08,
-	0x00, 0x65, 0xd2, 0x5b,
-	0xa0, 0x3d, 0x46, 0x62,
-	0x00, 0xb9, 0x3a, 0x42,
-	0xff, 0x65, 0x3a, 0x62,
+	0x00, 0x65, 0xf2, 0x5b,
+	0xa0, 0x3d, 0x6e, 0x62,
+	0x00, 0xb9, 0x66, 0x42,
+	0xff, 0x65, 0x66, 0x62,
 	0xa1, 0x6a, 0x22, 0x01,
 	0xff, 0x6a, 0xd4, 0x08,
-	0x10, 0x51, 0x46, 0x72,
+	0x10, 0x51, 0x6e, 0x72,
 	0x40, 0x6a, 0x18, 0x00,
 	0xff, 0x65, 0x0c, 0x08,
-	0x00, 0x65, 0xd2, 0x5b,
-	0xa0, 0x3d, 0x46, 0x62,
-	0x10, 0x3d, 0x06, 0x00,
-	0x00, 0x65, 0x0e, 0x42,
+	0x00, 0x65, 0xf2, 0x5b,
+	0xa0, 0x3d, 0x38, 0x72,
 	0x40, 0x6a, 0x18, 0x00,
 	0xff, 0x34, 0xa6, 0x08,
-	0x80, 0x34, 0x4e, 0x62,
+	0x80, 0x34, 0x76, 0x62,
 	0x7f, 0xa0, 0x40, 0x09,
 	0x08, 0x6a, 0x68, 0x00,
-	0x00, 0x65, 0x18, 0x41,
-	0x64, 0x6a, 0x12, 0x5b,
-	0x80, 0x64, 0xbe, 0x6a,
-	0x04, 0x64, 0xa4, 0x72,
-	0x02, 0x64, 0xaa, 0x72,
-	0x00, 0x6a, 0x6c, 0x72,
-	0x03, 0x64, 0xba, 0x72,
-	0x01, 0x64, 0xa0, 0x72,
-	0x07, 0x64, 0x00, 0x73,
-	0x08, 0x64, 0x68, 0x72,
+	0x00, 0x65, 0x22, 0x41,
+	0x64, 0x6a, 0x3a, 0x5b,
+	0x80, 0x64, 0xea, 0x6a,
+	0x04, 0x64, 0xcc, 0x72,
+	0x02, 0x64, 0xd2, 0x72,
+	0x00, 0x6a, 0x94, 0x72,
+	0x03, 0x64, 0xe6, 0x72,
+	0x01, 0x64, 0xc8, 0x72,
+	0x07, 0x64, 0x28, 0x73,
+	0x08, 0x64, 0x90, 0x72,
 	0x11, 0x6a, 0x22, 0x01,
-	0x07, 0x6a, 0x04, 0x5b,
+	0x07, 0x6a, 0x2c, 0x5b,
 	0xff, 0x06, 0xd4, 0x08,
-	0x00, 0x65, 0x18, 0x41,
-	0xff, 0xa8, 0x70, 0x6a,
-	0xff, 0xa2, 0x88, 0x7a,
+	0x00, 0x65, 0x22, 0x41,
+	0xff, 0xa8, 0x98, 0x6a,
+	0xff, 0xa2, 0xb0, 0x7a,
 	0x01, 0x6a, 0x6a, 0x00,
-	0x00, 0xb9, 0xfe, 0x5b,
-	0xff, 0xa2, 0x88, 0x7a,
+	0x00, 0xb9, 0x1e, 0x5c,
+	0xff, 0xa2, 0xb0, 0x7a,
 	0x71, 0x6a, 0x22, 0x01,
 	0xff, 0x6a, 0xd4, 0x08,
-	0x40, 0x51, 0x88, 0x62,
+	0x40, 0x51, 0xb0, 0x62,
 	0x0d, 0x6a, 0x6a, 0x00,
-	0x00, 0xb9, 0xfe, 0x5b,
+	0x00, 0xb9, 0x1e, 0x5c,
 	0xff, 0x3e, 0x74, 0x09,
 	0xff, 0x90, 0x7c, 0x08,
-	0x00, 0x65, 0x44, 0x58,
-	0x00, 0x65, 0x2a, 0x41,
-	0x20, 0xa0, 0x90, 0x6a,
+	0x00, 0x65, 0x50, 0x58,
+	0x00, 0x65, 0x34, 0x41,
+	0x20, 0xa0, 0xb8, 0x6a,
 	0xff, 0x37, 0xc8, 0x08,
-	0x00, 0x6a, 0xa8, 0x5b,
-	0xff, 0x6a, 0xbe, 0x5b,
+	0x00, 0x6a, 0xc8, 0x5b,
+	0xff, 0x6a, 0xde, 0x5b,
 	0xff, 0xf8, 0xc8, 0x08,
 	0xff, 0x4f, 0xc8, 0x08,
-	0x01, 0x6a, 0xa8, 0x5b,
-	0x00, 0xb9, 0xbe, 0x5b,
+	0x01, 0x6a, 0xc8, 0x5b,
+	0x00, 0xb9, 0xde, 0x5b,
 	0x01, 0x4f, 0x9e, 0x18,
 	0x02, 0x6a, 0x22, 0x01,
-	0x00, 0x65, 0x6c, 0x5c,
-	0x00, 0x65, 0x2a, 0x41,
+	0x00, 0x65, 0x8c, 0x5c,
+	0x00, 0x65, 0x34, 0x41,
 	0x41, 0x6a, 0x22, 0x01,
-	0x00, 0x65, 0x18, 0x41,
+	0x00, 0x65, 0x22, 0x41,
 	0x04, 0xa0, 0x40, 0x01,
-	0x00, 0x65, 0x84, 0x5c,
-	0x00, 0x65, 0x2a, 0x41,
-	0x10, 0x36, 0x68, 0x7a,
-	0xff, 0x38, 0x46, 0x09,
-	0xa4, 0x6a, 0xcc, 0x00,
-	0x39, 0x6a, 0xe6, 0x5b,
+	0x00, 0x65, 0xa4, 0x5c,
+	0x00, 0x65, 0x34, 0x41,
+	0x10, 0x36, 0x90, 0x7a,
+	0x05, 0x38, 0x46, 0x31,
+	0x04, 0x14, 0x58, 0x31,
+	0x03, 0xa9, 0x60, 0x31,
+	0xa3, 0x6a, 0xcc, 0x00,
+	0x38, 0x6a, 0x04, 0x5c,
 	0xac, 0x6a, 0xcc, 0x00,
-	0x14, 0x6a, 0xe6, 0x5b,
-	0xa9, 0x6a, 0xe8, 0x5b,
-	0x00, 0x65, 0x68, 0x42,
+	0x14, 0x6a, 0x06, 0x5c,
+	0xa9, 0x6a, 0x08, 0x5c,
+	0x00, 0x65, 0x90, 0x42,
 	0xef, 0x36, 0x6c, 0x08,
-	0x00, 0x65, 0x68, 0x42,
+	0x00, 0x65, 0x90, 0x42,
 	0x0f, 0x64, 0xc8, 0x08,
 	0x07, 0x64, 0xc8, 0x08,
 	0x00, 0x37, 0x6e, 0x00,
-	0x00, 0x65, 0x78, 0x5b,
-	0xff, 0x51, 0xce, 0x72,
-	0x20, 0x36, 0xde, 0x7a,
-	0x00, 0x90, 0x5c, 0x5b,
-	0x00, 0x65, 0xe0, 0x42,
+	0xff, 0x6a, 0xa4, 0x00,
+	0x00, 0x65, 0x98, 0x5b,
+	0xff, 0x51, 0xfc, 0x72,
+	0x20, 0x36, 0x06, 0x7b,
+	0x00, 0x90, 0x86, 0x5b,
+	0x00, 0x65, 0x08, 0x43,
 	0xff, 0x06, 0xd4, 0x08,
-	0x00, 0x65, 0xd2, 0x5b,
-	0xe0, 0x3d, 0xfa, 0x62,
-	0x20, 0x12, 0xfa, 0x62,
-	0x51, 0x6a, 0x08, 0x5b,
-	0xff, 0x51, 0x20, 0x09,
-	0x20, 0xa0, 0xfa, 0x7a,
-	0x00, 0x90, 0x5c, 0x5b,
-	0x00, 0x65, 0x56, 0x5b,
+	0x00, 0x65, 0xf2, 0x5b,
+	0xe0, 0x3d, 0x22, 0x63,
+	0x20, 0x12, 0x22, 0x63,
+	0x51, 0x6a, 0x30, 0x5b,
+	0x00, 0x65, 0x80, 0x5b,
 	0xff, 0x37, 0xc8, 0x08,
-	0x00, 0xa1, 0xf2, 0x62,
-	0x04, 0xa0, 0xf2, 0x7a,
+	0x00, 0xa1, 0x1a, 0x63,
+	0x04, 0xa0, 0x1a, 0x7b,
 	0xfb, 0xa0, 0x40, 0x09,
 	0x80, 0x36, 0x6c, 0x00,
-	0x80, 0xa0, 0x68, 0x7a,
+	0x80, 0xa0, 0x90, 0x7a,
 	0x7f, 0xa0, 0x40, 0x09,
-	0xff, 0x6a, 0x04, 0x5b,
-	0x00, 0x65, 0x68, 0x42,
-	0x04, 0xa0, 0xf8, 0x7a,
-	0x00, 0x65, 0x84, 0x5c,
-	0x00, 0x65, 0xfa, 0x42,
-	0x00, 0x65, 0x6c, 0x5c,
+	0xff, 0x6a, 0x2c, 0x5b,
+	0x00, 0x65, 0x90, 0x42,
+	0x04, 0xa0, 0x20, 0x7b,
+	0x00, 0x65, 0xa4, 0x5c,
+	0x00, 0x65, 0x22, 0x43,
+	0x00, 0x65, 0x8c, 0x5c,
 	0x31, 0x6a, 0x22, 0x01,
-	0x0c, 0x6a, 0x04, 0x5b,
-	0x00, 0x65, 0x68, 0x42,
+	0x0c, 0x6a, 0x2c, 0x5b,
+	0x00, 0x65, 0x90, 0x42,
 	0x61, 0x6a, 0x22, 0x01,
-	0x00, 0x65, 0x68, 0x42,
+	0x00, 0x65, 0x90, 0x42,
 	0x10, 0x3d, 0x06, 0x00,
 	0xff, 0x65, 0x68, 0x0c,
 	0xff, 0x06, 0xd4, 0x08,
-	0x01, 0x0c, 0x0a, 0x7b,
-	0x04, 0x0c, 0x0a, 0x6b,
+	0x01, 0x0c, 0x32, 0x7b,
+	0x04, 0x0c, 0x32, 0x6b,
 	0xe0, 0x03, 0x7a, 0x08,
-	0xe0, 0x3d, 0x1e, 0x63,
+	0xe0, 0x3d, 0x46, 0x63,
 	0xff, 0x65, 0xcc, 0x08,
 	0xff, 0x12, 0xda, 0x0c,
 	0xff, 0x06, 0xd4, 0x0c,
 	0xff, 0x65, 0x0c, 0x08,
-	0x02, 0x0b, 0x1a, 0x7b,
+	0x02, 0x0b, 0x42, 0x7b,
 	0xff, 0x6a, 0xd4, 0x0c,
 	0xd1, 0x6a, 0x22, 0x01,
-	0x00, 0x65, 0x18, 0x41,
+	0x00, 0x65, 0x22, 0x41,
 	0xff, 0x65, 0x26, 0x09,
-	0x01, 0x0b, 0x32, 0x6b,
-	0x10, 0x0c, 0x24, 0x7b,
-	0x04, 0x0b, 0x2c, 0x6b,
+	0x01, 0x0b, 0x5a, 0x6b,
+	0x10, 0x0c, 0x4c, 0x7b,
+	0x04, 0x0b, 0x54, 0x6b,
 	0xff, 0x6a, 0xca, 0x08,
-	0x04, 0x93, 0x30, 0x6b,
-	0x01, 0x94, 0x2e, 0x7b,
-	0x10, 0x94, 0x30, 0x6b,
+	0x04, 0x93, 0x58, 0x6b,
+	0x01, 0x94, 0x56, 0x7b,
+	0x10, 0x94, 0x58, 0x6b,
 	0xc7, 0x93, 0x26, 0x09,
 	0xff, 0x99, 0xd4, 0x08,
-	0x08, 0x93, 0x34, 0x6b,
+	0x38, 0x93, 0x5c, 0x6b,
 	0xff, 0x6a, 0xd4, 0x0c,
-	0x80, 0x36, 0x38, 0x6b,
+	0x80, 0x36, 0x60, 0x6b,
 	0x21, 0x6a, 0x22, 0x05,
 	0xff, 0x65, 0x20, 0x09,
-	0xff, 0x51, 0x46, 0x63,
+	0xff, 0x51, 0x6e, 0x63,
 	0xff, 0x37, 0xc8, 0x08,
-	0xa1, 0x6a, 0x50, 0x43,
+	0xa1, 0x6a, 0x7a, 0x43,
 	0xff, 0x51, 0xc8, 0x08,
-	0xb9, 0x6a, 0x50, 0x43,
-	0xff, 0xba, 0x54, 0x73,
+	0xb9, 0x6a, 0x7a, 0x43,
+	0xff, 0x90, 0xa4, 0x08,
+	0xff, 0xba, 0x7e, 0x73,
 	0xff, 0xba, 0x20, 0x09,
 	0xff, 0x65, 0xca, 0x18,
-	0x00, 0x6c, 0x4a, 0x63,
+	0x00, 0x6c, 0x72, 0x63,
 	0xff, 0x90, 0xca, 0x0c,
 	0xff, 0x6a, 0xca, 0x04,
-	0x20, 0x36, 0x72, 0x7b,
-	0x00, 0x90, 0x3e, 0x5b,
-	0xff, 0x65, 0x72, 0x73,
-	0xff, 0xba, 0x66, 0x73,
-	0xff, 0xbb, 0xcc, 0x08,
-	0xff, 0xba, 0x20, 0x09,
-	0xff, 0x66, 0x76, 0x09,
-	0xff, 0x65, 0x20, 0x09,
-	0xff, 0xbb, 0x70, 0x73,
+	0x20, 0x36, 0x92, 0x7b,
+	0x00, 0x90, 0x66, 0x5b,
+	0xff, 0x65, 0x92, 0x73,
+	0xff, 0x52, 0x90, 0x73,
 	0xff, 0xba, 0xcc, 0x08,
-	0xff, 0xbb, 0x20, 0x09,
+	0xff, 0x52, 0x20, 0x09,
 	0xff, 0x66, 0x74, 0x09,
 	0xff, 0x65, 0x20, 0x0d,
 	0xff, 0xba, 0x7e, 0x0c,
-	0x00, 0x6a, 0x72, 0x5c,
+	0x00, 0x6a, 0x92, 0x5c,
 	0x0d, 0x6a, 0x6a, 0x00,
-	0x00, 0x51, 0xfe, 0x43,
-	0xff, 0x3f, 0xcc, 0x73,
+	0x00, 0x51, 0x1e, 0x44,
+	0xff, 0x3f, 0xec, 0x73,
 	0xff, 0x6a, 0xa2, 0x00,
-	0x00, 0x3f, 0x3e, 0x5b,
-	0xff, 0x65, 0xcc, 0x73,
+	0x00, 0x3f, 0x66, 0x5b,
+	0xff, 0x65, 0xec, 0x73,
 	0x20, 0x36, 0x6c, 0x00,
-	0x20, 0xa0, 0x86, 0x6b,
+	0x20, 0xa0, 0xa6, 0x6b,
 	0xff, 0xb9, 0xa2, 0x0c,
 	0xff, 0x6a, 0xa2, 0x04,
 	0xff, 0x65, 0xa4, 0x08,
 	0xe0, 0x6a, 0xcc, 0x00,
-	0x45, 0x6a, 0xf2, 0x5b,
+	0x45, 0x6a, 0x12, 0x5c,
 	0x01, 0x6a, 0xd0, 0x01,
 	0x09, 0x6a, 0xd6, 0x01,
-	0x80, 0xeb, 0x92, 0x7b,
+	0x80, 0xeb, 0xb2, 0x7b,
 	0x01, 0x6a, 0xd6, 0x01,
 	0x01, 0xe9, 0xa4, 0x34,
 	0x88, 0x6a, 0xcc, 0x00,
-	0x45, 0x6a, 0xf2, 0x5b,
+	0x45, 0x6a, 0x12, 0x5c,
 	0x01, 0x6a, 0x18, 0x01,
 	0xff, 0x6a, 0x1a, 0x09,
 	0xff, 0x6a, 0x1c, 0x09,
 	0x0d, 0x6a, 0x26, 0x01,
-	0x00, 0x65, 0x64, 0x5c,
+	0x00, 0x65, 0x84, 0x5c,
 	0xff, 0x99, 0xa4, 0x0c,
 	0xff, 0x65, 0xa4, 0x08,
 	0xe0, 0x6a, 0xcc, 0x00,
-	0x45, 0x6a, 0xf2, 0x5b,
+	0x45, 0x6a, 0x12, 0x5c,
 	0x01, 0x6a, 0xd0, 0x01,
 	0x01, 0x6a, 0xdc, 0x05,
 	0x88, 0x6a, 0xcc, 0x00,
-	0x45, 0x6a, 0xf2, 0x5b,
+	0x45, 0x6a, 0x12, 0x5c,
 	0x01, 0x6a, 0x18, 0x01,
 	0xff, 0x6a, 0x1a, 0x09,
 	0xff, 0x6a, 0x1c, 0x09,
 	0x01, 0x6a, 0x26, 0x05,
 	0x01, 0x65, 0xd8, 0x31,
 	0x09, 0xee, 0xdc, 0x01,
-	0x80, 0xee, 0xc2, 0x7b,
+	0x80, 0xee, 0xe2, 0x7b,
 	0xff, 0x6a, 0xdc, 0x0d,
 	0xff, 0x65, 0x32, 0x09,
 	0x0a, 0x93, 0x26, 0x01,
-	0x00, 0x65, 0x64, 0x44,
+	0x00, 0x65, 0x84, 0x44,
 	0xff, 0x37, 0xc8, 0x08,
-	0x00, 0x6a, 0x88, 0x5b,
+	0x00, 0x6a, 0xa8, 0x5b,
 	0xff, 0x52, 0xa2, 0x0c,
-	0x01, 0x0c, 0xd2, 0x7b,
-	0x04, 0x0c, 0xd2, 0x6b,
-	0xe0, 0x03, 0x7a, 0x08,
-	0xff, 0x3d, 0x06, 0x0c,
+	0x01, 0x0c, 0xf2, 0x7b,
+	0x04, 0x0c, 0xf2, 0x6b,
+	0xe0, 0x03, 0x06, 0x08,
+	0xe0, 0x03, 0x7a, 0x0c,
 	0xff, 0x8c, 0x10, 0x08,
 	0xff, 0x8d, 0x12, 0x08,
 	0xff, 0x8e, 0x14, 0x0c,
@@ -515,29 +531,29 @@
 	0x00, 0x6c, 0xda, 0x24,
 	0xff, 0x65, 0xc8, 0x08,
 	0xe0, 0x6a, 0xcc, 0x00,
-	0x41, 0x6a, 0xee, 0x5b,
+	0x41, 0x6a, 0x0e, 0x5c,
 	0xff, 0x90, 0xe2, 0x09,
 	0x20, 0x6a, 0xd0, 0x01,
-	0x04, 0x35, 0x10, 0x7c,
+	0x04, 0x35, 0x30, 0x7c,
 	0x1d, 0x6a, 0xdc, 0x01,
-	0xdc, 0xee, 0x0c, 0x64,
-	0x00, 0x65, 0x1c, 0x44,
+	0xdc, 0xee, 0x2c, 0x64,
+	0x00, 0x65, 0x3c, 0x44,
 	0x01, 0x6a, 0xdc, 0x01,
 	0x20, 0xa0, 0xd8, 0x31,
 	0x09, 0xee, 0xdc, 0x01,
-	0x80, 0xee, 0x16, 0x7c,
+	0x80, 0xee, 0x36, 0x7c,
 	0x19, 0x6a, 0xdc, 0x01,
-	0xd8, 0xee, 0x1a, 0x64,
+	0xd8, 0xee, 0x3a, 0x64,
 	0xff, 0x6a, 0xdc, 0x09,
-	0x18, 0xee, 0x1e, 0x6c,
+	0x18, 0xee, 0x3e, 0x6c,
 	0xff, 0x6a, 0xd4, 0x0c,
 	0x88, 0x6a, 0xcc, 0x00,
-	0x41, 0x6a, 0xee, 0x5b,
+	0x41, 0x6a, 0x0e, 0x5c,
 	0x20, 0x6a, 0x18, 0x01,
 	0xff, 0x6a, 0x1a, 0x09,
 	0xff, 0x6a, 0x1c, 0x09,
 	0xff, 0x35, 0x26, 0x09,
-	0x04, 0x35, 0x48, 0x6c,
+	0x04, 0x35, 0x68, 0x6c,
 	0xa0, 0x6a, 0xca, 0x00,
 	0x20, 0x65, 0xc8, 0x18,
 	0xff, 0x6c, 0x32, 0x09,
@@ -548,14 +564,14 @@
 	0xff, 0x6c, 0x32, 0x09,
 	0xff, 0x6c, 0x32, 0x09,
 	0xff, 0x6c, 0x32, 0x09,
-	0x00, 0x65, 0x34, 0x64,
+	0x00, 0x65, 0x54, 0x64,
 	0x0a, 0x93, 0x26, 0x01,
-	0x00, 0x65, 0x64, 0x5c,
-	0x04, 0x35, 0x38, 0x7b,
-	0xa0, 0x6a, 0x54, 0x5c,
-	0x00, 0x65, 0x56, 0x5c,
-	0x00, 0x65, 0x56, 0x5c,
-	0x00, 0x65, 0x56, 0x44,
+	0x00, 0x65, 0x84, 0x5c,
+	0x04, 0x35, 0x60, 0x7b,
+	0xa0, 0x6a, 0x74, 0x5c,
+	0x00, 0x65, 0x76, 0x5c,
+	0x00, 0x65, 0x76, 0x5c,
+	0x00, 0x65, 0x76, 0x44,
 	0xff, 0x65, 0xcc, 0x08,
 	0xff, 0x99, 0xda, 0x08,
 	0xff, 0x99, 0xda, 0x08,
@@ -564,37 +580,40 @@
 	0xff, 0x99, 0xda, 0x08,
 	0xff, 0x99, 0xda, 0x08,
 	0xff, 0x99, 0xda, 0x0c,
-	0x08, 0x94, 0x64, 0x7c,
+	0x08, 0x94, 0x84, 0x7c,
 	0xf7, 0x93, 0x26, 0x09,
-	0x08, 0x93, 0x68, 0x6c,
+	0x08, 0x93, 0x88, 0x6c,
 	0xff, 0x6a, 0xd4, 0x0c,
 	0xff, 0x40, 0x74, 0x09,
 	0xff, 0x90, 0x80, 0x08,
 	0xff, 0x6a, 0x72, 0x05,
-	0xff, 0x40, 0x80, 0x64,
-	0xff, 0x3f, 0x78, 0x64,
+	0xff, 0x40, 0xa0, 0x64,
+	0xff, 0x3f, 0x98, 0x64,
 	0xff, 0x6a, 0xca, 0x04,
 	0xff, 0x3f, 0x20, 0x09,
 	0x01, 0x6a, 0x6a, 0x00,
-	0x00, 0xb9, 0xfe, 0x5b,
-	0x00, 0x90, 0x5c, 0x43,
+	0x00, 0xb9, 0x1e, 0x5c,
+	0xff, 0xba, 0x7e, 0x0c,
 	0xff, 0x40, 0x20, 0x09,
 	0xff, 0xba, 0x80, 0x0c,
-	0xff, 0x6a, 0x76, 0x01,
 	0xff, 0x3f, 0x74, 0x09,
-	0xff, 0x90, 0x7e, 0x08,
-	0xff, 0xba, 0x38, 0x73,
-	0xff, 0xba, 0x20, 0x09,
-	0xff, 0x3f, 0x76, 0x09,
-	0xff, 0x3f, 0x20, 0x0d,
+	0xff, 0x90, 0x7e, 0x0c,
 };
 
+static int aic7xxx_patch13_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch13_func(struct aic7xxx_host *p)
+{
+	return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895);
+}
+
 static int aic7xxx_patch12_func(struct aic7xxx_host *p);
 
 static int
 aic7xxx_patch12_func(struct aic7xxx_host *p)
 {
-	return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895);
+	return ((p->features & AHC_CMD_CHAN) == 0);
 }
 
 static int aic7xxx_patch11_func(struct aic7xxx_host *p);
@@ -699,71 +718,81 @@
 			skip_instr :10,
 			skip_patch :12;
 } sequencer_patches[] = {
-	{ aic7xxx_patch1_func, 1, 1, 2 },
-	{ aic7xxx_patch0_func, 2, 1, 1 },
-	{ aic7xxx_patch2_func, 3, 2, 1 },
-	{ aic7xxx_patch3_func, 7, 1, 1 },
+	{ aic7xxx_patch1_func, 2, 1, 2 },
+	{ aic7xxx_patch0_func, 3, 1, 1 },
+	{ aic7xxx_patch2_func, 4, 2, 1 },
 	{ aic7xxx_patch3_func, 8, 1, 1 },
-	{ aic7xxx_patch4_func, 11, 4, 1 },
-	{ aic7xxx_patch5_func, 16, 3, 2 },
-	{ aic7xxx_patch0_func, 19, 4, 1 },
-	{ aic7xxx_patch6_func, 23, 1, 1 },
-	{ aic7xxx_patch7_func, 26, 1, 1 },
-	{ aic7xxx_patch4_func, 34, 4, 1 },
-	{ aic7xxx_patch8_func, 38, 3, 2 },
-	{ aic7xxx_patch0_func, 41, 3, 1 },
-	{ aic7xxx_patch9_func, 47, 7, 1 },
-	{ aic7xxx_patch4_func, 55, 3, 1 },
-	{ aic7xxx_patch8_func, 58, 2, 1 },
-	{ aic7xxx_patch1_func, 63, 60, 1 },
-	{ aic7xxx_patch8_func, 164, 1, 2 },
-	{ aic7xxx_patch0_func, 165, 1, 1 },
-	{ aic7xxx_patch2_func, 169, 1, 1 },
-	{ aic7xxx_patch2_func, 172, 1, 2 },
-	{ aic7xxx_patch0_func, 173, 2, 1 },
-	{ aic7xxx_patch10_func, 175, 1, 1 },
-	{ aic7xxx_patch8_func, 182, 1, 2 },
-	{ aic7xxx_patch0_func, 183, 3, 1 },
-	{ aic7xxx_patch8_func, 187, 1, 2 },
-	{ aic7xxx_patch0_func, 188, 1, 1 },
-	{ aic7xxx_patch8_func, 189, 7, 2 },
-	{ aic7xxx_patch0_func, 196, 1, 1 },
-	{ aic7xxx_patch2_func, 201, 13, 2 },
-	{ aic7xxx_patch0_func, 214, 8, 1 },
-	{ aic7xxx_patch10_func, 222, 1, 1 },
-	{ aic7xxx_patch8_func, 227, 1, 1 },
-	{ aic7xxx_patch8_func, 228, 1, 1 },
-	{ aic7xxx_patch8_func, 233, 1, 1 },
-	{ aic7xxx_patch8_func, 235, 2, 1 },
-	{ aic7xxx_patch8_func, 240, 8, 1 },
-	{ aic7xxx_patch8_func, 249, 1, 1 },
-	{ aic7xxx_patch2_func, 250, 2, 2 },
-	{ aic7xxx_patch0_func, 252, 4, 1 },
-	{ aic7xxx_patch10_func, 256, 2, 2 },
-	{ aic7xxx_patch0_func, 258, 1, 1 },
-	{ aic7xxx_patch11_func, 265, 1, 2 },
-	{ aic7xxx_patch0_func, 266, 1, 1 },
-	{ aic7xxx_patch5_func, 328, 1, 2 },
-	{ aic7xxx_patch0_func, 329, 1, 1 },
-	{ aic7xxx_patch3_func, 332, 1, 1 },
-	{ aic7xxx_patch11_func, 351, 1, 2 },
-	{ aic7xxx_patch0_func, 352, 1, 1 },
-	{ aic7xxx_patch6_func, 356, 1, 1 },
-	{ aic7xxx_patch7_func, 364, 3, 2 },
-	{ aic7xxx_patch0_func, 367, 1, 1 },
-	{ aic7xxx_patch1_func, 396, 3, 1 },
-	{ aic7xxx_patch10_func, 410, 1, 1 },
-	{ aic7xxx_patch2_func, 453, 7, 2 },
-	{ aic7xxx_patch0_func, 460, 8, 1 },
-	{ aic7xxx_patch2_func, 469, 4, 2 },
-	{ aic7xxx_patch0_func, 473, 6, 1 },
-	{ aic7xxx_patch2_func, 479, 4, 2 },
-	{ aic7xxx_patch0_func, 483, 3, 1 },
-	{ aic7xxx_patch2_func, 512, 17, 4 },
-	{ aic7xxx_patch12_func, 520, 4, 2 },
-	{ aic7xxx_patch0_func, 524, 2, 1 },
-	{ aic7xxx_patch0_func, 529, 33, 1 },
-	{ aic7xxx_patch6_func, 566, 2, 1 },
-	{ aic7xxx_patch6_func, 569, 9, 1 },
+	{ aic7xxx_patch3_func, 9, 1, 1 },
+	{ aic7xxx_patch4_func, 12, 4, 1 },
+	{ aic7xxx_patch5_func, 17, 3, 2 },
+	{ aic7xxx_patch0_func, 20, 4, 1 },
+	{ aic7xxx_patch6_func, 24, 1, 1 },
+	{ aic7xxx_patch7_func, 27, 1, 1 },
+	{ aic7xxx_patch2_func, 30, 1, 2 },
+	{ aic7xxx_patch0_func, 31, 3, 1 },
+	{ aic7xxx_patch4_func, 40, 4, 1 },
+	{ aic7xxx_patch8_func, 44, 3, 2 },
+	{ aic7xxx_patch0_func, 47, 3, 1 },
+	{ aic7xxx_patch9_func, 52, 7, 1 },
+	{ aic7xxx_patch4_func, 60, 3, 1 },
+	{ aic7xxx_patch8_func, 63, 2, 1 },
+	{ aic7xxx_patch1_func, 68, 60, 1 },
+	{ aic7xxx_patch8_func, 162, 1, 2 },
+	{ aic7xxx_patch0_func, 163, 2, 1 },
+	{ aic7xxx_patch2_func, 167, 2, 3 },
+	{ aic7xxx_patch8_func, 167, 1, 1 },
+	{ aic7xxx_patch0_func, 169, 2, 1 },
+	{ aic7xxx_patch8_func, 172, 1, 2 },
+	{ aic7xxx_patch0_func, 173, 1, 1 },
+	{ aic7xxx_patch2_func, 177, 1, 1 },
+	{ aic7xxx_patch2_func, 180, 3, 2 },
+	{ aic7xxx_patch0_func, 183, 5, 1 },
+	{ aic7xxx_patch2_func, 191, 2, 3 },
+	{ aic7xxx_patch8_func, 191, 1, 1 },
+	{ aic7xxx_patch0_func, 193, 3, 1 },
+	{ aic7xxx_patch10_func, 196, 2, 1 },
+	{ aic7xxx_patch8_func, 198, 7, 2 },
+	{ aic7xxx_patch0_func, 205, 1, 1 },
+	{ aic7xxx_patch2_func, 210, 14, 3 },
+	{ aic7xxx_patch10_func, 223, 1, 1 },
+	{ aic7xxx_patch0_func, 224, 9, 1 },
+	{ aic7xxx_patch8_func, 238, 2, 1 },
+	{ aic7xxx_patch8_func, 240, 1, 1 },
+	{ aic7xxx_patch10_func, 241, 6, 3 },
+	{ aic7xxx_patch2_func, 241, 2, 2 },
+	{ aic7xxx_patch0_func, 243, 4, 1 },
+	{ aic7xxx_patch8_func, 248, 1, 1 },
+	{ aic7xxx_patch8_func, 252, 11, 1 },
+	{ aic7xxx_patch2_func, 264, 3, 3 },
+	{ aic7xxx_patch10_func, 266, 1, 1 },
+	{ aic7xxx_patch0_func, 267, 5, 1 },
+	{ aic7xxx_patch10_func, 272, 1, 2 },
+	{ aic7xxx_patch0_func, 273, 7, 1 },
+	{ aic7xxx_patch11_func, 287, 1, 2 },
+	{ aic7xxx_patch0_func, 288, 1, 1 },
+	{ aic7xxx_patch5_func, 348, 1, 2 },
+	{ aic7xxx_patch0_func, 349, 1, 1 },
+	{ aic7xxx_patch3_func, 352, 1, 1 },
+	{ aic7xxx_patch2_func, 362, 3, 2 },
+	{ aic7xxx_patch0_func, 365, 5, 1 },
+	{ aic7xxx_patch11_func, 373, 1, 2 },
+	{ aic7xxx_patch0_func, 374, 1, 1 },
+	{ aic7xxx_patch6_func, 379, 1, 1 },
+	{ aic7xxx_patch1_func, 416, 3, 1 },
+	{ aic7xxx_patch10_func, 421, 11, 1 },
+	{ aic7xxx_patch2_func, 469, 7, 2 },
+	{ aic7xxx_patch0_func, 476, 8, 1 },
+	{ aic7xxx_patch2_func, 485, 4, 2 },
+	{ aic7xxx_patch0_func, 489, 6, 1 },
+	{ aic7xxx_patch2_func, 495, 4, 2 },
+	{ aic7xxx_patch0_func, 499, 3, 1 },
+	{ aic7xxx_patch12_func, 509, 10, 1 },
+	{ aic7xxx_patch2_func, 528, 17, 4 },
+	{ aic7xxx_patch13_func, 536, 4, 2 },
+	{ aic7xxx_patch0_func, 540, 2, 1 },
+	{ aic7xxx_patch0_func, 545, 33, 1 },
+	{ aic7xxx_patch12_func, 578, 4, 1 },
+	{ aic7xxx_patch6_func, 582, 2, 1 },
+	{ aic7xxx_patch6_func, 585, 9, 1 },
 
 };
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.3.5/linux/drivers/scsi/scsi.c	Mon May 31 22:28:06 1999
+++ linux/drivers/scsi/scsi.c	Tue Jun  8 10:41:03 1999
@@ -263,6 +263,7 @@
 {"HP", "C1790A", "", BLIST_NOLUN},              /* scanjet iip */
 {"HP", "C2500A", "", BLIST_NOLUN},              /* scanjet iicx */
 {"YAMAHA", "CDR102", "1.00", BLIST_NOLUN},	/* extra reset */
+{"RELISYS", "Scorpio", "*", BLIST_NOLUN},	/* responds to all LUN */
 
 /*
  * Other types of devices that have special flags.
@@ -669,8 +670,8 @@
     SCpnt->request.rq_status = RQ_SCSI_BUSY;
     spin_lock_irq(&io_request_lock);
     scsi_do_cmd (SCpnt, (void *) scsi_cmd,
-                 (void *) scsi_result,
-                 256, scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5);
+                 (void *) NULL,
+                 0, scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5);
     spin_unlock_irq(&io_request_lock);
     down (&sem);
     SCpnt->request.sem = NULL;
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v2.3.5/linux/drivers/scsi/scsi.h	Fri May 14 18:55:22 1999
+++ linux/drivers/scsi/scsi.h	Wed Jun  9 16:59:16 1999
@@ -296,6 +296,7 @@
 #define SCSI_1          1
 #define SCSI_1_CCS      2
 #define SCSI_2          3
+#define SCSI_3          4
 
 /*
  *  Every SCSI command starts with a one byte OP-code.
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/scsi_proc.c linux/drivers/scsi/scsi_proc.c
--- v2.3.5/linux/drivers/scsi/scsi_proc.c	Sat Jan  2 17:55:06 1999
+++ linux/drivers/scsi/scsi_proc.c	Wed Jun  9 16:59:16 1999
@@ -298,7 +298,7 @@
 		     scd->type < MAX_SCSI_DEVICE_CODE ? 
 		     scsi_device_types[(int)scd->type] : "Unknown          " );
     y += sprintf(buffer + len + y, "               ANSI"
-		     " SCSI revision: %02x", (scd->scsi_level < 3)?1:2);
+		     " SCSI revision: %02x", (scd->scsi_level - 1)?scd->scsi_level - 1:1);
     if (scd->scsi_level == 2)
 	y += sprintf(buffer + len + y, " CCS\n");
     else
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/scsi_syms.c linux/drivers/scsi/scsi_syms.c
--- v2.3.5/linux/drivers/scsi/scsi_syms.c	Sat Jan  2 17:55:06 1999
+++ linux/drivers/scsi/scsi_syms.c	Wed Jun  9 16:59:16 1999
@@ -27,13 +27,11 @@
 #include "constants.h"
 
 #include "sd.h"
+#include <scsi/scsicam.h>
 /*
  * This source file contains the symbol table used by scsi loadable
  * modules.
  */
-extern int scsicam_bios_param (Disk * disk,
-                               int dev,	int *ip	); 
-
 
 extern void print_command (unsigned char *command);
 extern void print_sense(const char * devclass, Scsi_Cmnd * SCpnt);
@@ -47,6 +45,7 @@
 EXPORT_SYMBOL(scsi_register);
 EXPORT_SYMBOL(scsi_unregister);
 EXPORT_SYMBOL(scsicam_bios_param);
+EXPORT_SYMBOL(scsi_partsize);
 EXPORT_SYMBOL(scsi_allocate_device);
 EXPORT_SYMBOL(scsi_do_cmd);
 EXPORT_SYMBOL(scsi_command_size);
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/scsicam.c linux/drivers/scsi/scsicam.c
--- v2.3.5/linux/drivers/scsi/scsicam.c	Sun Nov  8 13:51:34 1998
+++ linux/drivers/scsi/scsicam.c	Wed Jun  9 16:59:16 1999
@@ -21,9 +21,8 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
+#include <scsi/scsicam.h>
 
-static int partsize(struct buffer_head *bh, unsigned long capacity,
-    unsigned int  *cyls, unsigned int *hds, unsigned int *secs);
 static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds,
     unsigned int *secs);
 
@@ -51,7 +50,7 @@
 	return -1;
 
     /* try to infer mapping from partition table */
-    ret_code = partsize (bh, (unsigned long) size, (unsigned int *) ip + 2, 
+    ret_code = scsi_partsize (bh, (unsigned long) size, (unsigned int *) ip + 2, 
 	(unsigned int *) ip + 0, (unsigned int *) ip + 1);
     brelse (bh);
 
@@ -80,7 +79,7 @@
 }
 
 /*
- * Function : static int partsize(struct buffer_head *bh, unsigned long 
+ * Function : static int scsi_partsize(struct buffer_head *bh, unsigned long 
  *     capacity,unsigned int *cyls, unsigned int *hds, unsigned int *secs);
  *
  * Purpose : to determine the BIOS mapping used to create the partition
@@ -90,7 +89,7 @@
  *
  */
 
-static int partsize(struct buffer_head *bh, unsigned long capacity,
+int scsi_partsize(struct buffer_head *bh, unsigned long capacity,
     unsigned int  *cyls, unsigned int *hds, unsigned int *secs) {
     struct partition *p, *largest = NULL;
     int i, largest_cyl;
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v2.3.5/linux/drivers/scsi/sd.c	Fri May 14 18:55:22 1999
+++ linux/drivers/scsi/sd.c	Mon Jun  7 16:50:56 1999
@@ -1729,7 +1729,7 @@
 static void sd_detach(Scsi_Device * SDp)
 {
     Scsi_Disk * dpnt;
-    int i;
+    int i, j;
     int max_p;
     int start;
 
@@ -1741,8 +1741,8 @@
 	    max_p = sd_gendisk.max_p;
 	    start = i << sd_gendisk.minor_shift;
 
-	    for (i=max_p - 1; i >=0 ; i--) {
-		int index = start+i;
+	    for (j=max_p - 1; j >=0 ; j--) {
+		int index = start+j;
 		kdev_t devi = MKDEV_SD_PARTITION(index);
                 struct super_block *sb = get_super(devi);
 		sync_dev(devi);
@@ -1759,7 +1759,7 @@
 	    SDp->attached--;
 	    sd_template.dev_noticed--;
 	    sd_template.nr_dev--;
-	    SD_GENDISK(start).nr_real--;
+	    SD_GENDISK(i).nr_real--;
 	    return;
 	}
     return;
diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
--- v2.3.5/linux/drivers/scsi/sg.c	Sat May 15 23:46:03 1999
+++ linux/drivers/scsi/sg.c	Mon Jun  7 16:26:25 1999
@@ -16,12 +16,10 @@
  *
  *  Borrows code from st driver. Thanks to Alessandro Rubini's "dd" book.
  */
- static char * sg_version_str = "Version: 2.1.32 (990501)";
+ static char * sg_version_str = "Version: 2.1.34 (990603)";
+ static int sg_version_num = 20134; /* 2 digits for each component */
 /*
- *  D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au)
- *      - scatter list logic replaces previous large atomic SG_BIG_BUFF
- *        sized allocation. See notes in <scsi/sg.h> include file. 
- * 
+ *  D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes:
  *      - scsi logging is available via SCSI_LOG_TIMEOUT macros. First
  *        the kernel/module needs to be built with CONFIG_SCSI_LOGGING
  *        (otherwise the macros compile to empty statements), then do
@@ -34,13 +32,27 @@
  *        Should use hlcomplete but it is too "noisy" (sd uses it).
  *
  *      - This driver obtains memory (heap) for the low-level driver to
- *        transfer/dma to and from. It is obtained from up to 4 sources:
- *              - 1 SG_SCATTER_SZ sized buffer on open() (per fd)
- *                [could be less if SG_SCATTER_SZ bytes not available]
- *              - obtain heap as required on write()s (get_free_pages)
+ *        transfer/dma to and from. It is obtained from up to 3 sources:
+ *              - obtain heap via get_free_pages()
  *              - obtain heap from the shared scsi dma pool
  *              - obtain heap from kernel directly (kmalloc) [last choice]
- *        the 'alt_address' field in the scatter_list structure and the
+ *        Each open() attempts to obtain a "reserve" buffer of
+ *        SG_DEF_RESERVED_SIZE bytes (or 0 bytes if opened O_RDONLY). The
+ *        amount actually obtained [which could be 0 bytes] can be found from
+ *        the SG_GET_RESERVED_SIZE ioctl(). This reserved buffer size can
+ *        be changed by calling the SG_SET_RESERVED_SIZE ioctl(). Since this
+ *        is an ambit claim, it should be followed by a SG_GET_RESERVED_SIZE
+ *        ioctl() to find out how much was actually obtained.
+ *        A subsequent write() to this file descriptor will use the
+ *        reserved buffer unless:
+ *              - it is already in use (eg during command queuing)
+ *              - or the write() needs a buffer size larger than the
+ *                reserved size
+ *        In these cases the write() will attempt to get the required memory
+ *        for the duration of this request but, if memory is low, it may
+ *        fail with ENOMEM.
+ *
+ *      - The 'alt_address' field in the scatter_list structure and the
  *        related 'mem_src' indicate the source of the heap allocation.
  *
  */
@@ -67,11 +79,11 @@
 #include <scsi/sg.h>
 
 
-int sg_big_buff = SG_SCATTER_SZ;  /* sg_big_buff is ro through sysctl */
+int sg_big_buff = SG_DEF_RESERVED_SIZE; /* sg_big_buff is ro through sysctl */
 /* N.B. This global is here to keep existing software happy. It now holds
-   the size of the "first buffer" of the most recent sucessful sg_open(). 
+   the size of the reserve buffer of the most recent sucessful sg_open(). 
    Only available when 'sg' compiled into kernel (rather than a module). 
-   This should probably be deprecated (use SG_GET_RESERVED_SIZE instead). */
+   This is deprecated (use SG_GET_RESERVED_SIZE ioctl() instead). */
 
 #define SG_SECTOR_SZ 512
 #define SG_SECTOR_MSK (SG_SECTOR_SZ - 1)
@@ -89,7 +101,6 @@
 static int sg_num_page = 0;
 #endif
 
-#define SG_HEAP_FB 0    /* heap obtained at open() (one buffer per fd) */
 #define SG_HEAP_PAGE 1  /* heap from kernel via get_free_pages() */
 #define SG_HEAP_KMAL 2  /* heap from kernel via kmalloc() */
 #define SG_HEAP_POOL 3  /* heap from scsi dma pool (mid-level) */
@@ -112,9 +123,9 @@
 {
     unsigned short use_sg;      /* Number of pieces of scatter-gather */
     unsigned short sglist_len;  /* size of malloc'd scatter-gather list */
-    unsigned bufflen;           /* Size of data buffer */
+    unsigned bufflen;           /* Size of (aggregate) data buffer */
     unsigned b_malloc_len;      /* actual len malloc'ed in buffer */
-    void * buffer;              /* Data buffer or scatter list (12 bytes) */
+    void * buffer;              /* Data buffer or scatter list,12 bytes each*/
     char mem_src;               /* heap whereabouts of 'buffer' */
 } Sg_scatter_hold;    /* 20 bytes long on i386 */
 
@@ -126,10 +137,10 @@
     Scsi_Cmnd * my_cmdp;        /* NULL -> ready to read, else id */
     struct sg_request * nextrp; /* NULL -> tail request (slist) */
     struct sg_fd * parentfp;    /* NULL -> not in use */
-    Sg_scatter_hold data;       /* hold buffers, perhaps scatter list */
-    struct sg_header header;    /* scsi command+info <include/sg.h> */
-    char fb_used;               /* 1 -> using fst_buf, normally 0 (used) */
-} Sg_request; /* around 72 bytes long on i386 */
+    Sg_scatter_hold data;       /* hold buffer, perhaps scatter list */
+    struct sg_header header;    /* scsi command+info, see <scsi/sg.h> */
+    char res_used;              /* 1 -> using reserve buffer, 0 -> not ... */
+} Sg_request; /* 72 bytes long on i386 */
 
 typedef struct sg_fd /* holds the state of a file descriptor */
 {
@@ -137,41 +148,52 @@
     struct sg_device * parentdp;     /* owning device */
     wait_queue_head_t read_wait;   /* queue read until command done */
     int timeout;                     /* defaults to SG_DEFAULT_TIMEOUT */
-    char * fst_buf;     /* try to grab SG_SCATTER_SZ sized buffer on open */
-    int fb_size;        /* actual size of allocated fst_buf */
-    Sg_request * headrp; /* head of request slist, NULL->empty */
+    Sg_scatter_hold reserve;  /* buffer held for this file descriptor */
+    unsigned save_scat_len;   /* original length of trunc. scat. element */
+    Sg_request * headrp;      /* head of request slist, NULL->empty */
     struct fasync_struct * async_qp; /* used by asynchronous notification */
     Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */
-    char low_dma;       /* as in parent but possible overridden to 1 */
+    char low_dma;       /* as in parent but possibly overridden to 1 */
     char force_packid;  /* 1 -> pack_id input to read(), 0 -> ignored */
     char closed;        /* 1 -> fd closed but request(s) outstanding */
-    char my_mem_src;    /* heap whereabouts of this sg_fb object */
+    char my_mem_src;    /* heap whereabouts of this Sg_fd object */
     char cmd_q;         /* 1 -> allow command queuing, 0 -> don't */
     char underrun_flag; /* 1 -> flag underruns, 0 -> don't, 2 -> test */
-} Sg_fd; /* around 1192 bytes long on i386 */
+    char next_cmd_len;  /* 0 -> automatic (def), >0 -> use on next write() */
+} Sg_fd; /* 1208 bytes long on i386 */
 
 typedef struct sg_device /* holds the state of each scsi generic device */
 {
     Scsi_Device * device;
-    wait_queue_head_t generic_wait;/* queue open if O_EXCL on prev. open */
+    wait_queue_head_t o_excl_wait; /* queue open() when O_EXCL in use */
     int sg_tablesize;   /* adapter's max scatter-gather table size */
     Sg_fd * headfp;     /* first open fd belonging to this device */
     kdev_t i_rdev;      /* holds device major+minor number */
     char exclude;       /* opened for exclusive access */
     char sgdebug;       /* 0->off, 1->sense, 9->dump dev, 10-> all devs */
-    unsigned char merge_fd; /* 0->sequencing per fd (def) else fd count */
-} Sg_device; /* around 24 bytes long on i386 */
+    unsigned char merge_fd; /* 0->sequencing per fd, else fd count */
+} Sg_device; /* 24 bytes long on i386 */
 
 
 static int sg_fasync(int fd, struct file * filp, int mode);
 static void sg_command_done(Scsi_Cmnd * SCpnt);
-static int sg_sc_build(Sg_request * srp, int max_buff_size,
-                       const char * inp, int num_write_xfer);
-static int sg_sc_undo_rem(Sg_request * srp, char * outp,
-                          int num_read_xfer);
-static char * sg_malloc(Sg_request * srp, int size, int * retSzp, 
+static int sg_start_req(Sg_request * srp, int max_buff_size,
+                        const char * inp, int num_write_xfer);
+static void sg_finish_rem_req(Sg_request * srp, char * outp,
+                              int num_read_xfer);
+static int sg_build_scat(Sg_scatter_hold * schp, int buff_size, 
+                         const Sg_fd * sfp);
+static void sg_write_xfer(Sg_scatter_hold * schp, const char * inp, 
+                          int num_write_xfer);
+static void sg_remove_scat(Sg_scatter_hold * schp);
+static void sg_read_xfer(Sg_scatter_hold * schp, char * outp,
+                         int num_read_xfer);
+static void sg_build_reserve(Sg_fd * sfp, int req_size);
+static void sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size);
+static void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp);
+static char * sg_malloc(const Sg_fd * sfp, int size, int * retSzp, 
                         int * mem_srcp);
-static void sg_free(Sg_request * srp, char * buff, int size, int mem_src);
+static void sg_free(char * buff, int size, int mem_src);
 static char * sg_low_malloc(int rqSz, int lowDma, int mem_src, 
                             int * retSzp);
 static void sg_low_free(char * buff, int size, int mem_src);
@@ -180,7 +202,7 @@
 static Sg_request * sg_get_request(const Sg_fd * sfp, int pack_id);
 static Sg_request * sg_add_request(Sg_fd * sfp);
 static int sg_remove_request(Sg_fd * sfp, const Sg_request * srp);
-static int sg_fb_in_use(const Sg_fd * sfp);
+static int sg_res_in_use(const Sg_fd * sfp);
 static void sg_clr_scpnt(Scsi_Cmnd * SCpnt);
 static void sg_shorten_timeout(Scsi_Cmnd * scpnt);
 static void sg_debug(const Sg_device * sdp, const Sg_fd * sfp, int part_of);
@@ -197,6 +219,7 @@
     int flags = filp->f_flags;
     Sg_device * sdp;
     Sg_fd * sfp;
+    int res;
 
     if ((NULL == sg_dev_arr) || (dev < 0) || (dev >= sg_template.dev_max))
         return -ENXIO;
@@ -207,38 +230,35 @@
         printk("sg_open: inode maj=%d, min=%d   sdp maj=%d, min=%d\n",
                MAJOR(inode->i_rdev), MINOR(inode->i_rdev),
                MAJOR(sdp->i_rdev), MINOR(sdp->i_rdev));
+    /* If we are in the middle of error recovery, don't let anyone
+     * else try and use this device.  Also, if error recovery fails, it
+     * may try and take the device offline, in which case all further
+     * access to the device is prohibited.  */
     if(! scsi_block_when_processing_errors(sdp->device))
         return -ENXIO;
-/*  if (O_RDWR != (flags & O_ACCMODE)) */
-/*      return -EACCES;   May just want to get to a ioctl, so remove */
 
     SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags));
-   /* If we want exclusive access, then wait until the device is not
-    * busy, and then set the flag to prevent anyone else from using it. */
+
     if (flags & O_EXCL) {
         if (O_RDONLY == (flags & O_ACCMODE))
             return -EACCES;   /* Can't lock it with read only access */
-        while (sdp->headfp) {
-            if (flags & O_NONBLOCK)
-                return -EBUSY;
-            interruptible_sleep_on(&sdp->generic_wait);
-            if (signal_pending(current))
-                return -ERESTARTSYS;
-        }
-        sdp->exclude = 1;
-    }
-    else { /* Wait until nobody has an exclusive open on this device. */
-        while (sdp->exclude) {
-            if (flags & O_NONBLOCK)
-                return -EBUSY;
-            interruptible_sleep_on(&sdp->generic_wait);
-            if (signal_pending(current))
-                return -ERESTARTSYS;
-        }
+        if (sdp->headfp && (filp->f_flags & O_NONBLOCK))
+            return -EBUSY;
+        res = 0;  /* following is a macro that beats race condition */
+        __wait_event_interruptible(sdp->o_excl_wait, 
+               ((sdp->headfp || sdp->exclude) ? 0 : (sdp->exclude = 1)), 
+                                   res);
+        if (res)
+            return res; /* -ERESTARTSYS because signal hit process */
+    }
+    else if (sdp->exclude) { /* some other fd has an exclusive lock on dev */
+        if (filp->f_flags & O_NONBLOCK)
+            return -EBUSY;
+        res = 0;  /* following is a macro that beats race condition */
+        __wait_event_interruptible(sdp->o_excl_wait, (! sdp->exclude), res);
+        if (res)
+            return res; /* -ERESTARTSYS because signal hit process */
     }
-    /* OK, we should have grabbed the device.  Mark the thing so
-     * that other processes know that we have it, and initialize the
-     * state variables to known values. */
     if (! sdp->headfp) { /* no existing opens on this device */
         sdp->sgdebug = 0;
         sdp->sg_tablesize = sdp->device->host->sg_tablesize;
@@ -284,16 +304,14 @@
     if(sg_template.module)
         __MOD_DEC_USE_COUNT(sg_template.module);
     sdp->exclude = 0;
-    wake_up_interruptible(&sdp->generic_wait);
+    wake_up_interruptible(&sdp->o_excl_wait);
     return 0;
 }
 
-/* Read back the results of a SCSI command which was sent in a prior
-   write(). */
 static ssize_t sg_read(struct file * filp, char * buf,
                        size_t count, loff_t *ppos)
 {
-    int k;
+    int k, res;
     Sg_device * sdp;
     Sg_fd * sfp;
     Sg_request * srp;
@@ -305,13 +323,8 @@
     SCSI_LOG_TIMEOUT(3, printk("sg_read: dev=%d, count=%d\n", 
                                MINOR(sdp->i_rdev), (int)count));
     
-    /* If we are in the middle of error recovery, don't let anyone
-     * else try and use this device.  Also, if error recovery fails, it
-     * may try and take the device offline, in which case all further
-     * access to the device is prohibited.  */
     if(! scsi_block_when_processing_errors(sdp->device))
         return -ENXIO;
-
     if (ppos != &filp->f_pos)
         ; /* FIXME: Hmm.  Seek to the right place, or fail?  */
     if ((k = verify_area(VERIFY_WRITE, buf, count)))
@@ -319,13 +332,15 @@
     if (sfp->force_packid && (count >= size_sg_header))
         req_pack_id = shp->pack_id;
     srp = sg_get_request(sfp, req_pack_id);
-    while(! srp) {
+    if (! srp) { /* now wait on packet to arrive */
         if (filp->f_flags & O_NONBLOCK)
             return -EAGAIN;
-        interruptible_sleep_on(&sfp->read_wait);
-        if (signal_pending(current))
-            return -ERESTARTSYS;
-        srp = sg_get_request(sfp, req_pack_id);
+        res = 0;  /* following is a macro that beats race condition */
+        __wait_event_interruptible(sfp->read_wait, 
+                                   (srp = sg_get_request(sfp, req_pack_id)),
+                                   res);
+        if (res)
+            return res; /* -ERESTARTSYS because signal hit process */
     }
     if (2 != sfp->underrun_flag)
         srp->header.pack_len = srp->header.reply_len;   /* Why ????? */
@@ -337,13 +352,13 @@
         if (count > srp->header.reply_len)
             count = srp->header.reply_len;
         if (count > size_sg_header) /* release does copy_to_user */
-            sg_sc_undo_rem(srp, buf, count - size_sg_header);
+            sg_finish_rem_req(srp, buf, count - size_sg_header);
         else
-            sg_sc_undo_rem(srp, NULL, 0);
+            sg_finish_rem_req(srp, NULL, 0);
     }
     else {
         count = (srp->header.result == 0) ? 0 : -EIO;
-        sg_sc_undo_rem(srp, NULL, 0);
+        sg_finish_rem_req(srp, NULL, 0);
     }
     return count;
 }
@@ -366,22 +381,15 @@
     SCSI_LOG_TIMEOUT(3, printk("sg_write: dev=%d, count=%d\n", 
                                MINOR(sdp->i_rdev), (int)count));
 
-/* If we are in the middle of error recovery, don't let anyone
- * else try and use this device.  Also, if error recovery fails, it
- * may try and take the device offline, in which case all further
- * access to the device is prohibited.  */
     if(! scsi_block_when_processing_errors(sdp->device) )
         return -ENXIO;
-
     if (ppos != &filp->f_pos)
         ; /* FIXME: Hmm.  Seek to the right place, or fail?  */
 
     if ((k = verify_area(VERIFY_READ, buf, count)))
         return k;  /* protects following copy_from_user()s + get_user()s */
-/* The minimum scsi command length is 6 bytes.  If we get anything
- * less than this, it is clearly bogus.  */
     if (count < (size_sg_header + 6))
-        return -EIO;
+        return -EIO;   /* The minimum scsi command length is 6 bytes. */ 
 
     srp = sg_add_request(sfp);
     if (! srp) {
@@ -392,55 +400,54 @@
     buf += size_sg_header;
     srp->header.pack_len = count;
     __get_user(opcode, buf);
-    cmd_size = COMMAND_SIZE(opcode);
-    if ((opcode >= 0xc0) && srp->header.twelve_byte) 
-        cmd_size = 12;
+    if (sfp->next_cmd_len > 0) {
+        if (sfp->next_cmd_len > MAX_COMMAND_SIZE) {
+            SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n"));
+            sfp->next_cmd_len = 0;
+            return -EDOM;
+        }
+        cmd_size = sfp->next_cmd_len;
+        sfp->next_cmd_len = 0; /* reset so only this write() effected */
+    }
+    else {
+        cmd_size = COMMAND_SIZE(opcode); /* based on SCSI command group */
+        if ((opcode >= 0xc0) && srp->header.twelve_byte) 
+            cmd_size = 12;
+    }
     SCSI_LOG_TIMEOUT(4, printk("sg_write:   scsi opcode=0x%02x, cmd_size=%d\n", 
                                (int)opcode, cmd_size));
 /* Determine buffer size.  */
     input_size = count - cmd_size;
     mxsize = (input_size > srp->header.reply_len) ? input_size :
                                                     srp->header.reply_len;
-/* Don't include the command header itself in the size.  */
     mxsize -= size_sg_header;
     input_size -= size_sg_header;
-/* Verify user has actually passed enough bytes for this command. */
     if (input_size < 0) {
-        sg_sc_undo_rem(srp, NULL, 0);
-        return -EIO; 
+        sg_remove_request(sfp, srp);
+        return -EIO; /* User did not pass enough bytes for this command. */
     }
-
-/* If we cannot allocate the buffer, report an error. */
-    if ((k = sg_sc_build(srp, mxsize, buf + cmd_size, input_size))) {
+    if ((k = sg_start_req(srp, mxsize, buf + cmd_size, input_size))) {
         SCSI_LOG_TIMEOUT(1, printk("sg_write: build err=%d\n", k));
-        sg_sc_undo_rem(srp, NULL, 0);
-        return k;
+        sg_finish_rem_req(srp, NULL, 0);
+        return k;    /* probably out of space --> ENOMEM */
     }
-
 /*  SCSI_LOG_TIMEOUT(7, printk("sg_write: allocating device\n")); */
-/* Grab a command pointer for the device we want to talk to. If we
- * don't want to block, just return with the appropriate message.  */
     if (! (SCpnt = scsi_allocate_device(NULL, sdp->device, 
                                         !(filp->f_flags & O_NONBLOCK)))) {
-        sg_sc_undo_rem(srp, NULL, 0);
-        return -EAGAIN;
+        sg_finish_rem_req(srp, NULL, 0);
+        return -EAGAIN;   /* No available command blocks at the moment */
     }
 /*  SCSI_LOG_TIMEOUT(7, printk("sg_write: device allocated\n")); */
-
     srp->my_cmdp = SCpnt;
     SCpnt->request.rq_dev = sdp->i_rdev;
     SCpnt->request.rq_status = RQ_ACTIVE;
     SCpnt->sense_buffer[0] = 0;
     SCpnt->cmd_len = cmd_size;
-    /* Now copy the SCSI command from the user's address space.  */
     __copy_from_user(cmnd, buf, cmd_size);
-
-/* Set the LUN field in the command structure.  */
+/* Set the LUN field in the command structure, overriding user input  */
     cmnd[1]= (cmnd[1] & 0x1f) | (sdp->device->lun << 5);
+
 /*  SCSI_LOG_TIMEOUT(7, printk("sg_write: do cmd\n")); */
-/* Now pass the actual command down to the low-level driver.  We
- * do not do any more here - when the interrupt arrives, we will
- * then do the post-processing.  */
     spin_lock_irqsave(&io_request_lock, flags);
     SCpnt->use_sg = srp->data.use_sg;
     SCpnt->sglist_len = srp->data.sglist_len;
@@ -454,6 +461,8 @@
     srp->data.sglist_len = 0;
     srp->data.bufflen = 0;
     srp->data.buffer = NULL;
+/* Now send everything of to mid-level. The next time we hear about this
+   packet is when sg_command_done() is called (ie a callback). */
     scsi_do_cmd(SCpnt, (void *)cmnd,
                 (void *)SCpnt->buffer, mxsize,
                 sg_command_done, sfp->timeout, SG_DEFAULT_RETRIES);
@@ -475,9 +484,6 @@
         return -ENXIO;
     SCSI_LOG_TIMEOUT(3, printk("sg_ioctl: dev=%d, cmd=0x%x\n", 
                                MINOR(sdp->i_rdev), (int)cmd_in));
-     /* If we are in the middle of error recovery, then don't allow any
-     *  access to this device.  Also, error recovery *may* have taken the
-     *  device offline, in which case all further access is prohibited.  */
     if(! scsi_block_when_processing_errors(sdp->device) )
         return -ENXIO;
 
@@ -485,20 +491,18 @@
     {
     case SG_SET_TIMEOUT:
         return get_user(sfp->timeout, (int *)arg);
-    case SG_GET_TIMEOUT:
+    case SG_GET_TIMEOUT:  /* N.B. User receives timeout as return value */
         return sfp->timeout; /* strange ..., for backward compatibility */
     case SG_SET_FORCE_LOW_DMA:
         result = get_user(val, (int *)arg);
         if (result) return result;
         if (val) {
-            if ((0 == sfp->low_dma) && (0 == sg_fb_in_use(sfp))) {
-                sg_low_free(sfp->fst_buf, sfp->fb_size, SG_HEAP_PAGE);
-                sfp->fst_buf = sg_low_malloc(SG_SCATTER_SZ, 1, 
-                                             SG_HEAP_PAGE, &sfp->fb_size);
-            }
             sfp->low_dma = 1;
-            if (! sfp->fst_buf)
-                return -ENOMEM;
+            if ((0 == sfp->low_dma) && (0 == sg_res_in_use(sfp))) {
+                val = (int)sfp->reserve.bufflen;
+                sg_remove_scat(&sfp->reserve);
+                sg_build_reserve(sfp, val);
+            }
         }
         else
             sfp->low_dma = sdp->device->host->unchecked_isa_dma;
@@ -550,15 +554,20 @@
     case SG_GET_SG_TABLESIZE:
         return put_user(sdp->sg_tablesize, (int *)arg);
     case SG_SET_RESERVED_SIZE:
-        /* currently ignored, future extension */
         if (O_RDWR != (filp->f_flags & O_ACCMODE))
             return -EACCES;
         result = get_user(val, (int *)arg);
         if (result) return result;
-        /* logic should go here */
+        if (val != sfp->reserve.bufflen) {
+            if (sg_res_in_use(sfp))
+                return -EBUSY;
+            sg_remove_scat(&sfp->reserve);
+            sg_build_reserve(sfp, val);
+        }
         return 0;
     case SG_GET_RESERVED_SIZE:
-        return put_user(sfp->fb_size, (int *)arg);
+        val = (int)sfp->reserve.bufflen;
+        return put_user(val, (int *)arg);
     case SG_GET_MERGE_FD:
         return put_user((int)sdp->merge_fd, (int *)arg);
     case SG_SET_MERGE_FD:
@@ -587,6 +596,13 @@
         return 0;
     case SG_GET_UNDERRUN_FLAG:
         return put_user((int)sfp->underrun_flag, (int *)arg);
+    case SG_NEXT_CMD_LEN:
+        result = get_user(val, (int *)arg);
+        if (result) return result;
+        sfp->next_cmd_len = (val > 0) ? val : 0;
+        return 0;
+    case SG_GET_VERSION_NUM:
+        return put_user(sg_version_num, (int *)arg);
     case SG_EMULATED_HOST:
         return put_user(sdp->device->host->hostt->emulated, (int *)arg);
     case SCSI_IOCTL_SEND_COMMAND:
@@ -594,8 +610,7 @@
            user already has read/write access to the generic device and so
            can execute arbitrary SCSI commands.  */
         if (O_RDWR != (filp->f_flags & O_ACCMODE))
-            return -EACCES; /* require write access since these could be
-                               dangerous */
+            return -EACCES; /* very dangerous things can be done here */
         return scsi_ioctl_send_command(sdp->device, (void *)arg);
     case SG_SET_DEBUG:
         result = get_user(val, (int *)arg);
@@ -613,8 +628,7 @@
         return scsi_ioctl(sdp->device, cmd_in, (void *)arg);
     default:
         if (O_RDWR != (filp->f_flags & O_ACCMODE))
-            return -EACCES; /* require write access since these could be
-                               dangerous */
+            return -EACCES; /* don't know so take safe approach */
         return scsi_ioctl(sdp->device, cmd_in, (void *)arg);
     }
 }
@@ -664,8 +678,7 @@
 }
 
 /* This function is called by the interrupt handler when we
- * actually have a command that is complete.  Change the
- * flags to indicate that we have a result.  */
+ * actually have a command that is complete. */
 static void sg_command_done(Scsi_Cmnd * SCpnt)
 {
     int dev = MINOR(SCpnt->request.rq_dev);
@@ -712,17 +725,16 @@
     sg_clr_scpnt(SCpnt);
     srp->my_cmdp = NULL;
 
-    SCSI_LOG_TIMEOUT(4, 
-              printk("sg__done: dev=%d, scsi_stat=%d, res=0x%x\n", 
+    SCSI_LOG_TIMEOUT(4, printk("sg__done: dev=%d, scsi_stat=%d, res=0x%x\n", 
                 dev, (int)status_byte(SCpnt->result), (int)SCpnt->result));
-/* See if the command completed normally, or whether something went wrong. */
     memcpy(srp->header.sense_buffer, SCpnt->sense_buffer,
            sizeof(SCpnt->sense_buffer));
     switch (host_byte(SCpnt->result)) 
-    {
+    { /* This setup of 'result' is for backward compatibility and is best
+         ignored by the user who should use target, host + driver status */
     case DID_OK:
-    case DID_PASSTHROUGH: /* just guessing */
-    case DID_SOFT_ERROR:  /* just guessing */
+    case DID_PASSTHROUGH: 
+    case DID_SOFT_ERROR:
       srp->header.result = 0;
       break;
     case DID_NO_CONNECT:
@@ -738,12 +750,6 @@
       srp->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)
           srp->header.result = 0;
@@ -767,8 +773,6 @@
 /* filesystems using this device. */
         sdp->device->changed = 1;
     }
-
-/* Pick up error and status information */
     srp->header.target_status = status_byte(SCpnt->result);
     if ((sdp->sgdebug > 0) && 
         ((CHECK_CONDITION == srp->header.target_status) ||
@@ -784,15 +788,14 @@
         SCSI_LOG_TIMEOUT(1,
                printk("sg__done: already closed, freeing ...\n"));
 /* should check if module is unloaded <<<<<<< */
-        sg_sc_undo_rem(srp, NULL, 0);
+        sg_finish_rem_req(srp, NULL, 0);
         if (NULL == sfp->headrp) { 
             SCSI_LOG_TIMEOUT(1,
                 printk("sg__done: already closed, final cleanup\n"));
             sg_remove_sfp(sdp, sfp);
         }
     }
-/* Now wake up the process that is waiting for the result. */
-    /* A. Rubini says this is preferable+faster than wake_up() */
+/* Now wake up any sg_read() that is waiting for this packet. */
     wake_up_interruptible(&sfp->read_wait);
     if ((sfp->async_qp) && (! closed))
         kill_fasync(sfp->async_qp, SIGPOLL);
@@ -873,17 +876,20 @@
             printk("  *** Following data belongs to invoking FD ***\n");
         else if (! fp->parentdp)
             printk(">> Following FD has NULL parent pointer ???\n");
-        printk("   FD(%d): timeout=%d, fb_size=%d, cmd_q=%d\n",
-               k, fp->timeout, fp->fb_size, (int)fp->cmd_q);
-        printk("   low_dma=%d, force_packid=%d, urun_flag=%d, closed=%d\n",
-               (int)fp->low_dma, (int)fp->force_packid, 
-               (int)fp->underrun_flag, (int)fp->closed);
+        printk("   FD(%d): timeout=%d, bufflen=%d, use_sg=%d\n",
+               k, fp->timeout, fp->reserve.bufflen, (int)fp->reserve.use_sg);
+        printk("   low_dma=%d, cmd_q=%d, s_sc_len=%d, f_packid=%d\n",
+               (int)fp->low_dma, (int)fp->cmd_q, (int)fp->save_scat_len,
+               (int)fp->force_packid);
+        printk("   urun_flag=%d, next_cmd_len=%d, closed=%d\n",
+               (int)fp->underrun_flag, (int)fp->next_cmd_len, 
+               (int)fp->closed);
         srp = fp->headrp;
         if (NULL == srp)
             printk("     No requests active\n");
         while (srp) {
-            if (srp->fb_used)
-                printk("using 1st buff >> ");
+            if (srp->res_used)
+                printk("reserved buff >> ");
             else
                 printk("     ");
             if (srp->my_cmdp)
@@ -988,7 +994,7 @@
 
     SCSI_LOG_TIMEOUT(3, printk("sg_attach: dev=%d \n", k));
     sdp->device = scsidp;
-    init_waitqueue_head(&sdp->generic_wait);
+    init_waitqueue_head(&sdp->o_excl_wait);
     sdp->headfp= NULL;
     sdp->exclude = 0;
     sdp->merge_fd = 0;  /* Cope with SG_DEF_MERGE_FD on open */
@@ -1041,10 +1047,8 @@
         }
         scsidp->attached--;
         sg_template.nr_dev--;
-        /*
-         * avoid associated device /dev/sg? bying incremented
-         * each time module is inserted/removed , <dan@lectra.fr>
-         */
+/* avoid associated device /dev/sg? being incremented
+ * each time module is inserted/removed , <dan@lectra.fr> */
         sg_template.dev_noticed--;
         return;
     }
@@ -1099,42 +1103,80 @@
 #endif
 }
 
-static int sg_sc_build(Sg_request * srp, int max_buff_size,
-                       const char * inp, int num_write_xfer)
+static int sg_start_req(Sg_request * srp, int max_buff_size,
+                        const char * inp, int num_write_xfer)
+{
+    int res;
+    Sg_fd * sfp = srp->parentfp;
+    Sg_scatter_hold * req_schp = &srp->data;
+    Sg_scatter_hold * rsv_schp = &sfp->reserve;
+
+    SCSI_LOG_TIMEOUT(4, printk("sg_start_req: max_buff_size=%d\n", 
+                               max_buff_size)); 
+    if ((! sg_res_in_use(sfp)) && (max_buff_size <= rsv_schp->bufflen)) {
+        sg_link_reserve(sfp, srp, max_buff_size);
+        sg_write_xfer(req_schp, inp, num_write_xfer);
+    }
+    else {
+        res = sg_build_scat(req_schp, max_buff_size, sfp);
+        if (res) {
+            sg_remove_scat(req_schp);
+            return res;
+        }
+        sg_write_xfer(req_schp, inp, num_write_xfer);
+    }
+    return 0;
+}
+
+static void sg_finish_rem_req(Sg_request * srp, char * outp, 
+                              int num_read_xfer)
+{
+    Sg_fd * sfp = srp->parentfp;
+    Sg_scatter_hold * req_schp = &srp->data;
+
+    SCSI_LOG_TIMEOUT(4, printk("sg_finish_rem_req: res_used=%d\n",
+                               (int)srp->res_used)); 
+    if (num_read_xfer > 0)
+        sg_read_xfer(req_schp, outp, num_read_xfer);
+    if (srp->res_used)
+        sg_unlink_reserve(sfp, srp);
+    else 
+        sg_remove_scat(req_schp);
+    sg_remove_request(sfp, srp);
+}
+
+static int sg_build_scat(Sg_scatter_hold * schp, int buff_size, 
+                         const Sg_fd * sfp)
 {
     int ret_sz, mem_src;
-    int blk_size = max_buff_size;
+    int blk_size = buff_size;
     char * p = NULL;
 
-    if ((blk_size < 0) || (! srp))
+    if ((blk_size < 0) || (! sfp))
         return -EFAULT;
-    
-    SCSI_LOG_TIMEOUT(4, printk("sg_sc_build: m_b_s=%d, num_write_xfer=%d\n", 
-                                  max_buff_size, num_write_xfer)); 
     if (0 == blk_size)
         ++blk_size;             /* don't know why */
 /* round request up to next highest SG_SECTOR_SZ byte boundary */
     blk_size = (blk_size + SG_SECTOR_MSK) & (~SG_SECTOR_MSK);
-    SCSI_LOG_TIMEOUT(5, printk("sg_sc_build: blk_size=%d\n", blk_size));
-
+    SCSI_LOG_TIMEOUT(4, printk("sg_build_scat: buff_size=%d, blk_size=%d\n",
+                               buff_size, blk_size));
     if (blk_size <= SG_SCATTER_SZ) {
-        mem_src = SG_HEAP_FB;
-        p = sg_malloc(srp, blk_size, &ret_sz, &mem_src);
+        mem_src = SG_HEAP_PAGE;
+        p = sg_malloc(sfp, blk_size, &ret_sz, &mem_src);
         if (! p)
             return -ENOMEM;
         if (blk_size == ret_sz) { /* got it on the first attempt */
-            srp->data.buffer = p;
-            srp->data.bufflen = blk_size;
-            srp->data.mem_src = mem_src;
-            srp->data.b_malloc_len = blk_size;
-            if (inp && (num_write_xfer > 0))
-                __copy_from_user(srp->data.buffer, inp, num_write_xfer);
+            schp->use_sg = 0;
+            schp->buffer = p;
+            schp->bufflen = blk_size;
+            schp->mem_src = mem_src;
+            schp->b_malloc_len = blk_size;
             return 0;
         }
     }
     else {
         mem_src = SG_HEAP_PAGE;
-        p = sg_malloc(srp, SG_SCATTER_SZ, &ret_sz, &mem_src);
+        p = sg_malloc(sfp, SG_SCATTER_SZ, &ret_sz, &mem_src);
         if (! p)
             return -ENOMEM;
     }
@@ -1144,23 +1186,23 @@
         int k, rem_sz, num, nxt;
         int sc_bufflen = PAGE_SIZE;
         int mx_sc_elems = (sc_bufflen / sizeof(struct scatterlist)) - 1;
-        int sg_tablesize = srp->parentfp->parentdp->sg_tablesize;
+        int sg_tablesize = sfp->parentdp->sg_tablesize;
         int first = 1;
 
         k = SG_HEAP_KMAL;  /* want to protect mem_src, use k as scratch */
-        srp->data.buffer = (struct scatterlist *)sg_malloc(srp, 
+        schp->buffer = (struct scatterlist *)sg_malloc(sfp, 
                                 sc_bufflen, &num, &k);
-        srp->data.mem_src = (char)k;
+        schp->mem_src = (char)k;
         /* N.B. ret_sz and mem_src carried into this block ... */
-        if (! srp->data.buffer)
+        if (! schp->buffer)
             return -ENOMEM;
         else if (num != sc_bufflen) {
             sc_bufflen = num;
             mx_sc_elems = (sc_bufflen / sizeof(struct scatterlist)) - 1;
         }
-        srp->data.sglist_len = sc_bufflen;
-        memset(srp->data.buffer, 0, sc_bufflen);
-        for (k = 0, sclp = srp->data.buffer, rem_sz = blk_size, nxt =0; 
+        schp->sglist_len = sc_bufflen;
+        memset(schp->buffer, 0, sc_bufflen);
+        for (k = 0, sclp = schp->buffer, rem_sz = blk_size, nxt =0; 
              (k < sg_tablesize) && (rem_sz > 0) && (k < mx_sc_elems); 
              ++k, rem_sz -= ret_sz, ++sclp) {
             if (first) 
@@ -1168,7 +1210,7 @@
             else {
                 num = (rem_sz > SG_SCATTER_SZ) ? SG_SCATTER_SZ : rem_sz;
                 mem_src = SG_HEAP_PAGE;
-                p = sg_malloc(srp, num, &ret_sz, &mem_src);
+                p = sg_malloc(sfp, num, &ret_sz, &mem_src);
                 if (! p)
                     break;
             }
@@ -1176,73 +1218,188 @@
             sclp->length = ret_sz;
             sclp->alt_address = (char *)(long)mem_src;
             
-            if(inp && (num_write_xfer > 0)) {
-                num = (ret_sz > num_write_xfer) ? num_write_xfer : ret_sz;
-                __copy_from_user(sclp->address, inp, num);
-                num_write_xfer -= num;
-                inp += num;
-            }
             SCSI_LOG_TIMEOUT(5, 
-                printk("sg_sc_build: k=%d, a=0x%p, len=%d, ms=%d\n", 
+                printk("sg_build_build: k=%d, a=0x%p, len=%d, ms=%d\n", 
                 k, sclp->address, ret_sz, mem_src));
         } /* end of for loop */
-        srp->data.use_sg = k;
+        schp->use_sg = k;
         SCSI_LOG_TIMEOUT(5, 
-            printk("sg_sc_build: use_sg=%d, rem_sz=%d\n", k, rem_sz));
-        srp->data.bufflen = blk_size;
+            printk("sg_build_scat: use_sg=%d, rem_sz=%d\n", k, rem_sz));
+        schp->bufflen = blk_size;
         if (rem_sz > 0)   /* must have failed */
             return -ENOMEM;
     }
     return 0;
 }
 
-static int sg_sc_undo_rem(Sg_request * srp, char * outp,
-                          int num_read_xfer)
+static void sg_write_xfer(Sg_scatter_hold * schp, const char * inp, 
+                          int num_write_xfer)
 {
-    if (! srp)
-        return -EFAULT;
-    SCSI_LOG_TIMEOUT(4, printk("sg_sc_undo_rem: num_read_xfer=%d\n", 
-                               num_read_xfer)); 
-    if (! outp)
-        num_read_xfer = 0;
-    if(srp->data.use_sg) {
-        int k, num, mem_src;
-        struct scatterlist * sclp = (struct scatterlist *)srp->data.buffer;
-
-        for (k = 0; (k < srp->data.use_sg) && sclp->address; ++k, ++sclp) {
-            if (num_read_xfer > 0) {
-                num = (int)sclp->length;
-                if (num > num_read_xfer) {
-                    __copy_to_user(outp, sclp->address, num_read_xfer);
-                    outp += num_read_xfer;
-                    num_read_xfer = 0;
-                }
-                else {
-                    __copy_to_user(outp, sclp->address, num);
-                    outp += num;
-                    num_read_xfer -= num;
-                }
+    SCSI_LOG_TIMEOUT(4, printk("sg_write_xfer: num_write_xfer=%d, use_sg=%d\n", 
+                               num_write_xfer, schp->use_sg)); 
+    if ((! inp) || (num_write_xfer <= 0))
+        return;
+    if (schp->use_sg > 0) {
+        int k, num;
+        struct scatterlist * sclp = (struct scatterlist *)schp->buffer;
+
+        for (k = 0; (k < schp->use_sg) && sclp->address; ++k, ++sclp) { 
+            num = (int)sclp->length;
+            if (num > num_write_xfer) {
+                __copy_from_user(sclp->address, inp, num_write_xfer);
+                break;
             }
+            else {
+                __copy_from_user(sclp->address, inp, num);
+                num_write_xfer -= num;
+                if (num_write_xfer <= 0)
+                    break;
+                inp += num;
+            }
+        }
+    }
+    else
+        __copy_from_user(schp->buffer, inp, num_write_xfer);
+}
+
+static void sg_remove_scat(Sg_scatter_hold * schp)
+{
+    SCSI_LOG_TIMEOUT(4, printk("sg_remove_scat: use_sg=%d\n", schp->use_sg)); 
+    if(schp->use_sg > 0) {
+        int k, mem_src;
+        struct scatterlist * sclp = (struct scatterlist *)schp->buffer;
+
+        for (k = 0; (k < schp->use_sg) && sclp->address; ++k, ++sclp) {
             mem_src = (int)(long)sclp->alt_address;
             SCSI_LOG_TIMEOUT(5, 
-                printk("sg_sc_undo_rem: k=%d, a=0x%p, len=%d, ms=%d\n", 
+                printk("sg_remove_scat: k=%d, a=0x%p, len=%d, ms=%d\n", 
                        k, sclp->address, sclp->length, mem_src));
-            sg_free(srp, sclp->address, sclp->length, mem_src);
+            sg_free(sclp->address, sclp->length, mem_src);
+            sclp->address = NULL;
+            sclp->length = 0;
+        }
+        sg_free(schp->buffer, schp->sglist_len, schp->mem_src);
+    }
+    else if (schp->buffer)
+        sg_free(schp->buffer, schp->b_malloc_len, schp->mem_src);
+    schp->buffer = NULL;
+    schp->bufflen = 0;
+    schp->use_sg = 0;
+    schp->sglist_len = 0;
+}
+
+static void sg_read_xfer(Sg_scatter_hold * schp, char * outp,
+                         int num_read_xfer)
+{
+    SCSI_LOG_TIMEOUT(4, printk("sg_read_xfer: num_read_xfer=%d\n", 
+                               num_read_xfer)); 
+    if ((! outp) || (num_read_xfer <= 0))
+        return;
+    if(schp->use_sg > 0) {
+        int k, num;
+        struct scatterlist * sclp = (struct scatterlist *)schp->buffer;
+
+        for (k = 0; (k < schp->use_sg) && sclp->address; ++k, ++sclp) {
+            num = (int)sclp->length;
+            if (num > num_read_xfer) {
+                __copy_to_user(outp, sclp->address, num_read_xfer);
+                break;
+            }
+            else {
+                __copy_to_user(outp, sclp->address, num);
+                num_read_xfer -= num;
+                if (num_read_xfer <= 0)
+                    break;
+                outp += num;
+            }
         }
-        sg_free(srp, srp->data.buffer, srp->data.sglist_len, 
-                srp->data.mem_src);
+    }
+    else
+        __copy_to_user(outp, schp->buffer, num_read_xfer);
+}
+
+static void sg_build_reserve(Sg_fd * sfp, int req_size)
+{
+    Sg_scatter_hold * schp = &sfp->reserve;
+
+    SCSI_LOG_TIMEOUT(4, printk("sg_build_reserve: req_size=%d\n", req_size)); 
+    do {
+        if (req_size < PAGE_SIZE)
+            req_size = PAGE_SIZE;
+        if (0 == sg_build_scat(schp, req_size, sfp))
+            return;
+        else
+            sg_remove_scat(schp);
+        req_size >>= 1; /* divide by 2 */
+    } while (req_size >  (PAGE_SIZE / 2));
+}
+
+static void sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size)
+{
+    Sg_scatter_hold * req_schp = &srp->data;
+    Sg_scatter_hold * rsv_schp = &sfp->reserve;
+
+    SCSI_LOG_TIMEOUT(4, printk("sg_link_reserve: size=%d\n", size)); 
+    if (rsv_schp->use_sg > 0) {
+        int k, num;
+        int rem = size;
+        struct scatterlist * sclp = (struct scatterlist *)rsv_schp->buffer;
+
+        for (k = 0; k < rsv_schp->use_sg; ++k, ++sclp) {
+            num = (int)sclp->length;
+            if (rem <= num) {
+                sfp->save_scat_len = num;
+                sclp->length = (unsigned)rem;
+                break;
+            }
+            else
+                rem -= num;
+        }
+        if (k < rsv_schp->use_sg) {
+            req_schp->use_sg = k + 1;   /* adjust scatter list length */
+            req_schp->bufflen = size;
+            req_schp->sglist_len = rsv_schp->sglist_len;
+            req_schp->buffer = rsv_schp->buffer;
+            req_schp->mem_src = rsv_schp->mem_src;
+            req_schp->b_malloc_len = rsv_schp->b_malloc_len;
+        }
+        else
+            SCSI_LOG_TIMEOUT(1, printk("sg_link_reserve: BAD size\n")); 
     }
     else {
-        if (num_read_xfer > 0) 
-            __copy_to_user(outp, srp->data.buffer, num_read_xfer);
-        sg_free(srp, srp->data.buffer, srp->data.b_malloc_len, 
-                srp->data.mem_src);
-    }
-    if (0 == sg_remove_request(srp->parentfp, srp)) {
-        SCSI_LOG_TIMEOUT(1, printk("sg_sc_undo_rem: srp=0x%p not found\n", 
-                                   srp));
+        req_schp->use_sg = 0;
+        req_schp->bufflen = size;
+        req_schp->buffer = rsv_schp->buffer;
+        req_schp->mem_src = rsv_schp->mem_src;
+        req_schp->use_sg = rsv_schp->use_sg;
+        req_schp->b_malloc_len = rsv_schp->b_malloc_len;
     }
-    return 0;
+    srp->res_used = 1;
+}
+
+static void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp)
+{
+    Sg_scatter_hold * req_schp = &srp->data;
+    Sg_scatter_hold * rsv_schp = &sfp->reserve;
+
+    SCSI_LOG_TIMEOUT(4, printk("sg_unlink_reserve: req->use_sg=%d\n",
+                               (int)req_schp->use_sg)); 
+    if (rsv_schp->use_sg > 0) {
+        struct scatterlist * sclp = (struct scatterlist *)rsv_schp->buffer;
+
+        if (sfp->save_scat_len > 0) 
+            (sclp + (req_schp->use_sg - 1))->length = 
+                                        (unsigned)sfp->save_scat_len;
+        else
+            SCSI_LOG_TIMEOUT(1, printk(
+                        "sg_unlink_reserve: BAD save_scat_len\n")); 
+    }
+    req_schp->use_sg = 0;
+    req_schp->bufflen = 0;
+    req_schp->buffer = NULL;
+    req_schp->sglist_len = 0;
+    sfp->save_scat_len = 0;
+    srp->res_used = 0;
 }
 
 static Sg_request * sg_get_request(const Sg_fd * sfp, int pack_id)
@@ -1292,7 +1449,7 @@
     if (resp) {
         resp->parentfp = sfp;
         resp->nextrp = NULL;
-        resp->fb_used = 0;
+        resp->res_used = 0;
         memset(&resp->data, 0, sizeof(Sg_scatter_hold));
         memset(&resp->header, 0, sizeof(struct sg_header));
         resp->my_cmdp = NULL;
@@ -1347,13 +1504,6 @@
                    sdp->device->host->unchecked_isa_dma : 1;
     sfp->cmd_q = SG_DEF_COMMAND_Q;
     sfp->underrun_flag = SG_DEF_UNDERRUN_FLAG;
-    if (get_reserved)
-        sfp->fst_buf = sg_low_malloc(SG_SCATTER_SZ, sfp->low_dma, 
-                                     SG_HEAP_PAGE, &sfp->fb_size);
-    else
-        sfp->fst_buf = NULL;
-    if (! sfp->fst_buf)
-        sfp->fb_size = 0;
     sfp->parentdp = sdp;
     if (! sdp->headfp)
         sdp->headfp = sfp;
@@ -1363,11 +1513,14 @@
            pfp = pfp->nextfp;
         pfp->nextfp = sfp;
     }
-    sg_big_buff = sfp->fb_size; /* show sysctl most recent "fb" size */
     SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p, m_s=%d\n",
                                sfp, (int)sfp->my_mem_src));
-    SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp:   fb_sz=%d, fst_buf=0x%p\n",
-                               sfp->fb_size, sfp->fst_buf));
+    if (get_reserved) {
+        sg_build_reserve(sfp, SG_DEF_RESERVED_SIZE);
+        sg_big_buff = sfp->reserve.bufflen; /* sysctl shows most recent size */
+        SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp:   bufflen=%d, use_sg=%d\n",
+                               sfp->reserve.bufflen, sfp->reserve.use_sg));
+    }
     return sfp;
 }
 
@@ -1388,7 +1541,7 @@
         while (srp) {
             tsrp = srp->nextrp;
             if (! srp->my_cmdp)
-                sg_sc_undo_rem(srp, NULL, 0);
+                sg_finish_rem_req(srp, NULL, 0);
             else
                 ++dirty;
             srp = tsrp;
@@ -1409,12 +1562,12 @@
                 prev_fp = fp;
             }
         }
-SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp:    fb_sz=%d, fst_buf=0x%p\n",
-                 sfp->fb_size, sfp->fst_buf));
-        sg_low_free(sfp->fst_buf, sfp->fb_size, SG_HEAP_PAGE);
+        if (sfp->reserve.bufflen > 0) {
+SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp:    bufflen=%d, use_sg=%d\n",
+                 (int)sfp->reserve.bufflen, (int)sfp->reserve.use_sg));
+            sg_remove_scat(&sfp->reserve);
+        }
         sfp->parentdp = NULL;
-        sfp->fst_buf = NULL;
-        sfp->fb_size = 0;
     SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp:    sfp=0x%p\n", sfp));
         sg_low_free((char *)sfp, sizeof(Sg_fd), sfp->my_mem_src);
         res = 1;
@@ -1427,12 +1580,12 @@
     return res;
 }
 
-static int sg_fb_in_use(const Sg_fd * sfp)
+static int sg_res_in_use(const Sg_fd * sfp)
 {
     const Sg_request * srp = sfp->headrp;
 
     while (srp) {
-        if (srp->fb_used)
+        if (srp->res_used)
             return 1;
         srp = srp->nextrp;
     }
@@ -1511,7 +1664,7 @@
     return resp;
 }
 
-static char * sg_malloc(Sg_request * srp, int size, int * retSzp, 
+static char * sg_malloc(const Sg_fd * sfp, int size, int * retSzp, 
                         int * mem_srcp)
 {
     char * resp = NULL;
@@ -1520,26 +1673,16 @@
     if (size <= 0)
         ;
     else {
-        Sg_fd * sfp = srp->parentfp;
         int low_dma = sfp->low_dma;
         int l_ms = -1;  /* invalid value */
 
         switch (*mem_srcp) 
         {
         case SG_HEAP_PAGE:
-        case SG_HEAP_FB:
             l_ms = (size < PAGE_SIZE) ? SG_HEAP_POOL : SG_HEAP_PAGE;
             resp = sg_low_malloc(size, low_dma, l_ms, 0);
             if (resp)
                 break;
-            if ((size <= sfp->fb_size) && (0 == sg_fb_in_use(sfp))) {
-                SCSI_LOG_TIMEOUT(6,
-                    printk("sg_malloc: scsi_malloc failed, get fst_buf\n"));
-                resp = sfp->fst_buf;
-                srp->fb_used = 1;
-                l_ms = SG_HEAP_FB;
-                break;
-            }
             resp = sg_low_malloc(size, low_dma, l_ms, &size);
             if (! resp) {
                 l_ms = (SG_HEAP_POOL == l_ms) ? SG_HEAP_PAGE : SG_HEAP_POOL;
@@ -1595,18 +1738,12 @@
                mem_src, buff, size);
 }
 
-static void sg_free(Sg_request * srp, char * buff, int size, int mem_src)
+static void sg_free(char * buff, int size, int mem_src)
 {
-    Sg_fd * sfp = srp->parentfp;
-
     SCSI_LOG_TIMEOUT(6, 
         printk("sg_free: buff=0x%p, size=%d\n", buff, size));
-    if ((! sfp) || (! buff) || (size <= 0))
+    if ((! buff) || (size <= 0))
         ;
-    else if (sfp->fst_buf == buff) {
-        srp->fb_used = 0;
-        SCSI_LOG_TIMEOUT(6, printk("sg_free:   left cause fst_buf\n"));
-    }
     else
         sg_low_free(buff, size, mem_src);
 }
diff -u --recursive --new-file v2.3.5/linux/drivers/sound/dmasound.c linux/drivers/sound/dmasound.c
--- v2.3.5/linux/drivers/sound/dmasound.c	Fri May 14 18:55:22 1999
+++ linux/drivers/sound/dmasound.c	Mon Jun  7 12:12:22 1999
@@ -784,6 +784,7 @@
 
 struct sound_mixer {
     int busy;
+    int modify_counter;
 };
 
 static struct sound_mixer mixer;
@@ -3811,10 +3812,23 @@
 		       u_long arg)
 {
 	int data;
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+	    mixer.modify_counter++;
+	if (cmd == OSS_GETVERSION)
+	    return IOCTL_OUT(arg, SOUND_VERSION);
 	switch (sound.mach.type) {
 #ifdef CONFIG_ATARI
 	case DMASND_FALCON:
 		switch (cmd) {
+		case SOUND_MIXER_INFO: {
+		    mixer_info info;
+		    strncpy(info.id, "FALCON", sizeof(info.id));
+		    strncpy(info.name, "FALCON", sizeof(info.name));
+		    info.name[sizeof(info.name)-1] = 0;
+		    info.modify_counter = mixer.modify_counter;
+		    copy_to_user_ret((int *)arg, &info, sizeof(info), -EFAULT);
+		    return 0;
+		}
 		case SOUND_MIXER_READ_DEVMASK:
 			return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER);
 		case SOUND_MIXER_READ_RECMASK:
@@ -3866,6 +3880,15 @@
 
 	case DMASND_TT:
 		switch (cmd) {
+		case SOUND_MIXER_INFO: {
+		    mixer_info info;
+		    strncpy(info.id, "TT", sizeof(info.id));
+		    strncpy(info.name, "TT", sizeof(info.name));
+		    info.name[sizeof(info.name)-1] = 0;
+		    info.modify_counter = mixer.modify_counter;
+		    copy_to_user_ret((int *)arg, &info, sizeof(info), -EFAULT);
+		    return 0;
+		}
 		case SOUND_MIXER_READ_DEVMASK:
 			return IOCTL_OUT(arg,
 					 SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS |
@@ -3927,6 +3950,15 @@
 #ifdef CONFIG_AMIGA
 	case DMASND_AMIGA:
 		switch (cmd) {
+		case SOUND_MIXER_INFO: {
+		    mixer_info info;
+		    strncpy(info.id, "AMIGA", sizeof(info.id));
+		    strncpy(info.name, "AMIGA", sizeof(info.name));
+		    info.name[sizeof(info.name)-1] = 0;
+		    info.modify_counter = mixer.modify_counter;
+		    copy_to_user_ret((int *)arg, &info, sizeof(info), -EFAULT);
+		    return 0;
+		}
 		case SOUND_MIXER_READ_DEVMASK:
 			return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE);
 		case SOUND_MIXER_READ_RECMASK:
@@ -3953,6 +3985,16 @@
 	case DMASND_AWACS:
 		if (awacs_revision<AWACS_BURGUNDY) { /* Different IOCTLS for burgundy*/
 			switch (cmd) {
+			case SOUND_MIXER_INFO: {
+			    mixer_info info;
+			    strncpy(info.id, "AWACS", sizeof(info.id));
+			    strncpy(info.name, "AWACS", sizeof(info.name));
+			    info.name[sizeof(info.name)-1] = 0;
+			    info.modify_counter = mixer.modify_counter;
+			    copy_to_user_ret((int *)arg, &info, 
+					     sizeof(info), -EFAULT);
+			    return 0;
+			}
 			case SOUND_MIXER_READ_DEVMASK:
 				data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
 					| SOUND_MASK_LINE | SOUND_MASK_MIC
@@ -4066,6 +4108,16 @@
 		} else {
 			/* We are, we are, we are... Burgundy or better */
 			switch(cmd) {
+			case SOUND_MIXER_INFO: {
+			    mixer_info info;
+			    strncpy(info.id, "AWACS", sizeof(info.id));
+			    strncpy(info.name, "AWACS", sizeof(info.name));
+			    info.name[sizeof(info.name)-1] = 0;
+			    info.modify_counter = mixer.modify_counter;
+			    copy_to_user_ret((int *)arg, &info, 
+					     sizeof(info), -EFAULT);
+			    return 0;
+			}
 			case SOUND_MIXER_READ_DEVMASK:
 				data = SOUND_MASK_VOLUME | SOUND_MASK_CD |
 					SOUND_MASK_LINE | SOUND_MASK_MIC |
diff -u --recursive --new-file v2.3.5/linux/drivers/sound/sound_core.c linux/drivers/sound/sound_core.c
--- v2.3.5/linux/drivers/sound/sound_core.c	Mon May 31 22:28:06 1999
+++ linux/drivers/sound/sound_core.c	Mon Jun  7 11:06:10 1999
@@ -52,6 +52,22 @@
 	struct sound_unit *next;
 };
 
+#ifdef CONFIG_SOUND_SONICVIBES
+extern int init_sonicvibes(void);
+#endif
+#ifdef CONFIG_SOUND_ES1370
+extern int init_es1370(void);
+#endif
+#ifdef CONFIG_SOUND_ES1371
+extern int init_es1371(void);
+#endif
+#ifdef CONFIG_SOUND_MSNDCLAS
+extern int msnd_classic_init(void);
+#endif
+#ifdef CONFIG_SOUND_MSNDPIN
+extern int msnd_pinnacle_init(void);
+#endif
+
 /*
  *	Low level list operator. Scan the ordered list, find a hole and
  *	join into it. Called with the lock asserted
@@ -132,7 +148,7 @@
  *	This lock guards the sound loader list.
  */
 
-static spinlock_t sound_loader_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t sound_loader_lock = SPIN_LOCK_UNLOCKED;
 
 /*
  *	Allocate the controlling structure and add it to the sound driver
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/CREDITS linux/drivers/usb/CREDITS
--- v2.3.5/linux/drivers/usb/CREDITS	Tue May 11 10:04:03 1999
+++ linux/drivers/usb/CREDITS	Tue Jun  8 10:52:26 1999
@@ -8,6 +8,7 @@
   Johannes Erdfelt <jerdfelt@sventech.com>
   ham <ham@unsuave.com>
   Bradley M Keryan <keryan@andrew.cmu.edu>
+  Paul Mackerras <paulus@cs.anu.edu.au>
   Vojtech Pavlik <vojtech@twilight.ucw.cz>
   Gregory P. Smith <greg@electricrain.com>
   Linus Torvalds <torvalds@transmeta.com>
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/Config.in linux/drivers/usb/Config.in
--- v2.3.5/linux/drivers/usb/Config.in	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/Config.in	Mon Jun  7 20:04:01 1999
@@ -29,7 +29,11 @@
   dep_tristate 'USB keyboard support' CONFIG_USB_KBD $CONFIG_USB
   dep_tristate 'USB audio parsing support' CONFIG_USB_AUDIO $CONFIG_USB
   dep_tristate 'USB Abstract Control Model support' CONFIG_USB_ACM $CONFIG_USB
-  dep_tristate 'Preliminary USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB
+  dep_tristate 'USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB
+  dep_tristate 'USB SCSI Support' CONFIG_USB_SCSI $CONFIG_USB
+  if [ "$CONFIG_USB_SCSI" != "n" ]; then
+	  dep_tristate '  USB SCSI verbose debug' CONFIG_USB_SCSI_DEBUG $CONFIG_USB_SCSI
+  fi
 fi
 
 endmenu
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/Makefile linux/drivers/usb/Makefile
--- v2.3.5/linux/drivers/usb/Makefile	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/Makefile	Tue Jun  8 10:52:26 1999
@@ -109,6 +109,13 @@
   MIX_OBJS += cpia.o
 endif
 
+ifeq ($(CONFIG_USB_SCSI),y)
+  L_OBJS += usb_scsi.o
+  ifeq ($(CONFIG_USB_SCSI_DEBUG),y)
+    L_OBJS += usb_scsi_debug.o
+  endif
+endif
+
 include $(TOPDIR)/Rules.make
 
 keymap.o: keymap.c
@@ -116,8 +123,17 @@
 keymap.c: maps/serial.map maps/usb.map maps/fixup.map
 	./mkmap > $@
 
+keymap-mac.o: keymap-mac.c
+keymap-mac.c: maps/mac.map maps/usb.map
+	./mkmap.adb > $@
+
+ifneq ($(CONFIG_MAC_KEYBOARD),y)
 usb-keyboard.o: keymap.o keyboard.o
 	$(LD) $(LD_RFLAG) -r -o $@  keymap.o keyboard.o 
+else
+usb-keyboard.o: keymap-mac.o keyboard.o
+	$(LD) $(LD_RFLAG) -r -o $@  keymap-mac.o keyboard.o 
+endif
 
 usb-uhci.o: uhci.o uhci-debug.o 
 	$(LD) $(LD_RFLAG) -r -o $@  uhci.o uhci-debug.o 
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/README.ohci linux/drivers/usb/README.ohci
--- v2.3.5/linux/drivers/usb/README.ohci	Mon May 17 09:55:22 1999
+++ linux/drivers/usb/README.ohci	Tue Jun  8 10:52:26 1999
@@ -1,3 +1,9 @@
+June 08, 1999 01:23:34
+
+Paul Mackerras went through the OHCI (& USB code) to fix most of the
+endianness issues so that the code now works on Linux-PPC.  He also
+simplified add_td_to_ed to be simpler & atomic to the hardware.
+
 May 16, 1999 16:20:54
 
 EDs are now allocated dynamically from their device's pool.  Root hub
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/acm.c linux/drivers/usb/acm.c
--- v2.3.5/linux/drivers/usb/acm.c	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/acm.c	Fri Jun  4 13:33:43 1999
@@ -198,7 +198,7 @@
 	/*Now scan all configs for a ACM configuration*/
 	for (cfgnum=0;cfgnum<dev->descriptor.bNumConfigurations;cfgnum++) {
 		/* The first one should be Communications interface? */
-		interface = &dev->config[cfgnum].interface[0];
+		interface = &dev->config[cfgnum].altsetting[0].interface[0];
 		if (interface->bInterfaceClass != 2 ||
 		    interface->bInterfaceSubClass != 2 ||
 		    interface->bInterfaceProtocol != 1 ||
@@ -212,7 +212,7 @@
 			continue;
 			
 		/* The second one should be a Data interface? */
-		interface = &dev->config[cfgnum].interface[1];
+		interface = &dev->config[cfgnum].altsetting[0].interface[1];
 		if (interface->bInterfaceClass != 10 ||
 		    interface->bInterfaceSubClass != 0 ||
 		    interface->bInterfaceProtocol != 0 ||
@@ -234,12 +234,12 @@
 		printk("USB ACM found\n");
 		usb_set_configuration(dev, dev->config[cfgnum].bConfigurationValue);
 		acm->dev=dev;
-		acm->readendp=dev->config[cfgnum].interface[1].endpoint[0].bEndpointAddress;
-		acm->writeendp=dev->config[cfgnum].interface[1].endpoint[1].bEndpointAddress;
-		acm->ctrlendp=dev->config[cfgnum].interface[0].endpoint[0].bEndpointAddress;
+		acm->readendp=dev->config[cfgnum].altsetting[0].interface[1].endpoint[0].bEndpointAddress;
+		acm->writeendp=dev->config[cfgnum].altsetting[0].interface[1].endpoint[1].bEndpointAddress;
+		acm->ctrlendp=dev->config[cfgnum].altsetting[0].interface[0].endpoint[0].bEndpointAddress;
 		acm->readpipe=usb_rcvbulkpipe(dev,acm->readendp);
 		acm->writepipe=usb_sndbulkpipe(dev,acm->writeendp);
-		usb_request_irq(dev,acm->ctrlpipe=usb_rcvctrlpipe(dev,acm->ctrlendp), acm_irq, dev->config[cfgnum].interface[0].endpoint[0].bInterval, &acm->ctrlbuffer);
+		usb_request_irq(dev,acm->ctrlpipe=usb_rcvctrlpipe(dev,acm->ctrlendp), acm_irq, dev->config[cfgnum].altsetting[0].interface[0].endpoint[0].bInterval, &acm->ctrlbuffer);
 		acm->present = 1;
 		acm->buffer=0;
 		return 0;
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/audio.c linux/drivers/usb/audio.c
--- v2.3.5/linux/drivers/usb/audio.c	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/audio.c	Fri Jun  4 13:33:43 1999
@@ -42,7 +42,7 @@
 	int i;
 	int na=0;
 	
-	interface = &dev->config[0].interface[0];
+	interface = &dev->config[0].altsetting[0].interface[0];
 
 	for(i=0;i<dev->config[0].bNumInterfaces;i++)
 	{
@@ -133,7 +133,7 @@
 	return usb_audio_init();
 }
 
-void module_cleanup(void)
+void cleanup_module(void)
 {
 	usb_deregister(&usb_audio_driver);
 }
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/cpia.c linux/drivers/usb/cpia.c
--- v2.3.5/linux/drivers/usb/cpia.c	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/cpia.c	Fri Jun  4 13:33:43 1999
@@ -1164,7 +1164,7 @@
 		return -1;
 #endif
 
-	interface = &dev->config[0].interface[0];
+	interface = &dev->config[0].altsetting[0].interface[0];
 
 	/* Is it a CPiA? */
 /*
@@ -1260,7 +1260,7 @@
 {
 	return usb_cpia_init();
 }
-void module_cleanup(void)
+void cleanup_module(void)
 {
 }
 #endif
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/hub.c linux/drivers/usb/hub.c
--- v2.3.5/linux/drivers/usb/hub.c	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/hub.c	Tue Jun  8 10:52:26 1999
@@ -159,7 +159,7 @@
 	if (dev->config[0].bNumInterfaces != 1)
 		return -1;
 
-	interface = &dev->config[0].interface[0];
+	interface = &dev->config[0].altsetting[0].interface[0];
 
 	/* Is it a hub? */
 	if (interface->bInterfaceClass != 9)
@@ -241,8 +241,8 @@
 		return;
 	}
 
-	portstatus = *((unsigned short *)buf + 0);
-	portchange = *((unsigned short *)buf + 1);
+	portstatus = le16_to_cpup((unsigned short *)buf + 0);
+	portchange = le16_to_cpup((unsigned short *)buf + 1);
 
 	if ((!(portstatus & USB_PORT_STAT_CONNECTION)) &&
 		(!(portstatus & USB_PORT_STAT_ENABLE))) {
@@ -294,8 +294,8 @@
 				continue;
 			}
 
-			portstatus = *((unsigned short *)buf + 0);
-			portchange = *((unsigned short *)buf + 1);
+			portstatus = le16_to_cpup((unsigned short *)buf + 0);
+			portchange = le16_to_cpup((unsigned short *)buf + 1);
 
 			if (portchange & USB_PORT_STAT_C_CONNECTION) {
 				printk("hub: port %d connection change\n", i + 1);
@@ -421,7 +421,7 @@
 	return usb_hub_init();
 }
 
-void module_cleanup(void){
+void cleanup_module(void){
 	usb_hub_cleanup();
 }
 #endif
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/keyboard.c linux/drivers/usb/keyboard.c
--- v2.3.5/linux/drivers/usb/keyboard.c	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/keyboard.c	Tue Jun  8 10:52:26 1999
@@ -56,10 +56,12 @@
     int scancode = (int) usb_kbd_map[key];
     if(scancode)
     {
+#ifndef CONFIG_MAC_KEYBOARD
         if(scancode & PCKBD_NEEDS_E0)
         {
             handle_scancode(0xe0, 1);
         }
+#endif /* CONFIG_MAC_KEYBOARD */
         handle_scancode((scancode & ~PCKBD_NEEDS_E0), down);
     }
 }
@@ -171,7 +173,10 @@
     struct usb_endpoint_descriptor *endpoint;
     struct usb_keyboard *kbd;
 
-    interface = &dev->config[0].interface[0];
+    if (dev->descriptor.bNumConfigurations < 1)
+	return -1;
+
+    interface = &dev->config[0].altsetting[0].interface[0];
     endpoint = &interface->endpoint[0];
 
     if(interface->bInterfaceClass != 3
@@ -233,7 +238,7 @@
 	return usb_kbd_init();
 }
 
-void module_cleanup(void)
+void cleanup_module(void)
 {
 	usb_deregister(&usb_kbd_driver);
 }
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/keymap-mac.c linux/drivers/usb/keymap-mac.c
--- v2.3.5/linux/drivers/usb/keymap-mac.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/keymap-mac.c	Tue Jun  8 10:52:26 1999
@@ -0,0 +1,50 @@
+unsigned char usb_kbd_map[256] = 
+{
+    0x00,  0x00,  0x00,  0x00,  0x80,  0x0b,  0x08,  0x02,
+    0x0e,  0x03,  0x05,  0x04,  0x22,  0x26,  0x28,  0x25,
+
+    0x2e,  0x2d,  0x1f,  0x23,  0x0c,  0x0f,  0x01,  0x11,
+    0x20,  0x09,  0x0d,  0x07,  0x10,  0x06,  0x12,  0x13,
+
+    0x14,  0x15,  0x17,  0x16,  0x1a,  0x1c,  0x19,  0x1d,
+    0x24,  0x35,  0x33,  0x30,  0x31,  0x1b,  0x18,  0x21,
+
+    0x1e,  0x2a,  0x00,  0x29,  0x27,  0x32,  0x2b,  0x2f,
+    0x2c,  0x39,  0x7a,  0x78,  0x63,  0x76,  0x60,  0x61,
+
+    0x62,  0x64,  0x65,  0x6d,  0x67,  0x6f,  0x69,  0x6b,
+    0x71,  0x72,  0x73,  0x74,  0x75,  0x77,  0x79,  0x3c,
+
+    0x3b,  0x3d,  0x3e,  0x47,  0x4b,  0x43,  0x4e,  0x45,
+    0x4c,  0x53,  0x54,  0x55,  0x56,  0x57,  0x58,  0x59,
+
+    0x5b,  0x5c,  0x52,  0x41,  0x00,  0x00,  0x00,  0x00,
+    0x69,  0x6b,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+    0x36,  0x38,  0x3a,  0x37,  0x7d,  0x7b,  0x7c,  0x37,
+
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+};
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/maps/mac.map linux/drivers/usb/maps/mac.map
--- v2.3.5/linux/drivers/usb/maps/mac.map	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/maps/mac.map	Tue Jun  8 10:52:26 1999
@@ -0,0 +1,350 @@
+# Kernel keymap for Macintoshes. This uses 7 modifier combinations.
+keymaps 0-2,4-5,8,12
+#
+# Fixups:
+keycode 0x69 = Print_Screen
+keycode 0x6b = F14
+keycode 0x37 = Window_R
+#
+#keycode 0x00 = a
+# hack!
+keycode 0x80 = a
+	altgr   keycode 0x00 = Hex_A
+keycode 0x01 = s               
+keycode 0x02 = d
+	altgr   keycode 0x02 = Hex_D   
+keycode 0x03 = f
+	altgr   keycode 0x03 = Hex_F               
+keycode 0x04 = h               
+keycode 0x05 = g  
+keycode 0x06 = z
+keycode 0x07 = x               
+keycode 0x08 = c
+	altgr   keycode 0x08 = Hex_C   
+keycode 0x09 = v
+keycode 0x0a =
+keycode 0x0b = b
+	altgr   keycode 0x0b = Hex_B
+keycode 0x0c = q               
+keycode 0x0d = w               
+keycode 0x0e = e
+	altgr   keycode 0x0e = Hex_E   
+keycode 0x0f = r               
+keycode 0x10 = y               
+keycode 0x11 = t               
+keycode 0x12 = one              exclam          
+	alt     keycode 0x12 = Meta_one        
+keycode 0x13 = two              at               at              
+	control	keycode 0x13 = nul             
+	shift	control	keycode 0x13 = nul             
+	alt	keycode 0x13 = Meta_two        
+keycode 0x14 = three            numbersign      
+	control keycode 0x14 = Escape          
+	alt     keycode 0x14 = Meta_three      
+keycode 0x15 = four             dollar           dollar          
+	control keycode 0x15 = Control_backslash
+	alt     keycode 0x15 = Meta_four       
+keycode 0x16 = six              asciicircum     
+	control keycode 0x16 = Control_asciicircum
+	alt     keycode 0x16 = Meta_six        
+keycode 0x17 = five             percent         
+	control keycode 0x17 = Control_bracketright
+	alt     keycode 0x17 = Meta_five       
+keycode 0x18 = equal            plus            
+	alt     keycode 0x18 = Meta_equal      
+keycode 0x19 = nine             parenleft        bracketright    
+	alt     keycode 0x19 = Meta_nine       
+keycode 0x1a = seven            ampersand        braceleft       
+	control keycode 0x1a = Control_underscore
+	alt     keycode 0x1a = Meta_seven      
+keycode 0x1b = minus            underscore       backslash       
+	control	keycode 0x1b = Control_underscore
+	shift	control	keycode 0x1b = Control_underscore
+	alt	keycode 0x1b = Meta_minus      
+keycode 0x1c = eight            asterisk         bracketleft     
+	control keycode 0x1c = Delete          
+	alt     keycode 0x1c = Meta_eight      
+keycode 0x1d = zero             parenright       braceright      
+	alt     keycode 0x1d = Meta_zero       
+keycode 0x1e = bracketright     braceright       asciitilde      
+	control keycode 0x1e = Control_bracketright
+	alt     keycode 0x1e = Meta_bracketright
+keycode 0x1f = o               
+keycode 0x20 = u               
+keycode 0x21 = bracketleft      braceleft       
+	control keycode 0x21 = Escape          
+	alt     keycode 0x21 = Meta_bracketleft
+keycode 0x22 = i               
+keycode 0x23 = p               
+keycode 0x24 = Return          
+	alt     keycode 0x24 = Meta_Control_m  
+keycode 0x25 = l               
+keycode 0x26 = j               
+keycode 0x27 = apostrophe       quotedbl        
+	control keycode 0x27 = Control_g       
+	alt     keycode 0x27 = Meta_apostrophe 
+keycode 0x28 = k               
+keycode 0x29 = semicolon        colon           
+	alt     keycode 0x29 = Meta_semicolon  
+keycode 0x2a = backslash        bar             
+	control keycode 0x2a = Control_backslash
+	alt     keycode 0x2a = Meta_backslash  
+keycode 0x2b = comma            less            
+	alt     keycode 0x2b = Meta_comma      
+keycode 0x2c = slash            question        
+	control keycode 0x2c = Delete          
+	alt     keycode 0x2c = Meta_slash      
+keycode 0x2d = n
+keycode 0x2e = m               
+keycode 0x2f = period           greater         
+	control keycode 0x2f = Compose         
+	alt     keycode 0x2f = Meta_period     
+keycode 0x30 = Tab              Tab             
+	alt     keycode 0x30 = Meta_Tab        
+keycode 0x31 = space            space           
+	control keycode 0x31 = nul             
+	alt     keycode 0x31 = Meta_space      
+keycode 0x32 = grave            asciitilde      
+	control keycode 0x32 = nul             
+	alt     keycode 0x32 = Meta_grave      
+keycode 0x33 = Delete           Delete          
+	control keycode 0x33 = BackSpace
+	alt     keycode 0x33 = Meta_Delete     
+keycode 0x34 =
+keycode 0x35 = Escape           Escape          
+	alt     keycode 0x35 = Meta_Escape     
+keycode 0x36 = Control         
+keycode 0x37 = Window             
+keycode 0x38 = Shift
+keycode 0x39 = Caps_Lock       
+keycode 0x3a = Alt
+keycode 0x3b = Left            
+	alt     keycode 0x3b = Decr_Console
+keycode 0x3c = Right           
+	alt     keycode 0x3c = Incr_Console
+keycode 0x3d = Down            
+keycode 0x3e = Up              
+keycode 0x3f =
+keycode 0x40 =
+keycode 0x41 = KP_Period       
+keycode 0x42 =
+keycode 0x43 = KP_Multiply     
+keycode 0x44 =
+keycode 0x45 = KP_Add          
+keycode 0x46 =
+keycode 0x47 = Num_Lock
+#	shift   keycode 0x47 = Bare_Num_Lock
+keycode 0x48 =
+keycode 0x49 =
+keycode 0x4a =
+keycode 0x4b = KP_Divide       
+keycode 0x4c = KP_Enter        
+keycode 0x4d =
+keycode 0x4e = KP_Subtract     
+keycode 0x4f =
+keycode 0x50 =
+keycode 0x51 =
+#keycode 0x51 = KP_Equals
+keycode 0x52 = KP_0            
+	alt     keycode 0x52 = Ascii_0         
+	altgr   keycode 0x52 = Hex_0         
+keycode 0x53 = KP_1            
+	alt     keycode 0x53 = Ascii_1         
+	altgr   keycode 0x53 = Hex_1         
+keycode 0x54 = KP_2            
+	alt     keycode 0x54 = Ascii_2         
+	altgr   keycode 0x54 = Hex_2         
+keycode 0x55 = KP_3            
+	alt     keycode 0x55 = Ascii_3         
+	altgr   keycode 0x55 = Hex_3         
+keycode 0x56 = KP_4            
+	alt     keycode 0x56 = Ascii_4         
+	altgr   keycode 0x56 = Hex_4         
+keycode 0x57 = KP_5            
+	alt     keycode 0x57 = Ascii_5         
+	altgr   keycode 0x57 = Hex_5         
+keycode 0x58 = KP_6            
+	alt     keycode 0x58 = Ascii_6         
+	altgr   keycode 0x58 = Hex_6         
+keycode 0x59 = KP_7            
+	alt     keycode 0x59 = Ascii_7         
+	altgr   keycode 0x59 = Hex_7
+keycode 0x5b = KP_8            
+	alt     keycode 0x5b = Ascii_8         
+	altgr   keycode 0x5b = Hex_8         
+keycode 0x5c = KP_9            
+	alt     keycode 0x5c = Ascii_9         
+	altgr   keycode 0x5c = Hex_9         
+keycode 0x5d =
+keycode 0x5e =
+keycode 0x5f =
+keycode 0x60 = F5               F15              Console_17      
+	control keycode 0x60 = F5              
+	alt     keycode 0x60 = Console_5       
+	control alt     keycode 0x60 = Console_5       
+keycode 0x61 = F6               F16              Console_18      
+	control keycode 0x61 = F6              
+	alt     keycode 0x61 = Console_6       
+	control alt     keycode 0x61 = Console_6       
+keycode 0x62 = F7               F17              Console_19      
+	control keycode 0x62 = F7              
+	alt     keycode 0x62 = Console_7       
+	control alt     keycode 0x62 = Console_7       
+keycode 0x63 = F3               F13              Console_15      
+	control keycode 0x63 = F3              
+	alt     keycode 0x63 = Console_3       
+	control alt     keycode 0x63 = Console_3       
+keycode 0x64 = F8               F18              Console_20      
+	control keycode 0x64 = F8              
+	alt     keycode 0x64 = Console_8       
+	control alt     keycode 0x64 = Console_8       
+keycode 0x65 = F9               F19              Console_21      
+	control keycode 0x65 = F9              
+	alt     keycode 0x65 = Console_9       
+	control alt     keycode 0x65 = Console_9       
+keycode 0x66 =
+keycode 0x67 = F11              F11              Console_23      
+	control keycode 0x67 = F11             
+	alt     keycode 0x67 = Console_11      
+	control alt     keycode 0x67 = Console_11      
+keycode 0x68 =
+keycode 0x69 = F13             
+keycode 0x6a =
+keycode 0x6b = Scroll_Lock      Show_Memory      Show_Registers  
+	control keycode 0x6b = Show_State      
+	alt     keycode 0x6b = Scroll_Lock     
+keycode 0x6c =
+keycode 0x6d = F10              F20              Console_22      
+	control keycode 0x6d = F10             
+	alt     keycode 0x6d = Console_10      
+	control alt     keycode 0x6d = Console_10      
+keycode 0x6e =
+keycode 0x6f = F12              F12              Console_24      
+	control keycode 0x6f = F12             
+	alt     keycode 0x6f = Console_12      
+	control alt     keycode 0x6f = Console_12      
+keycode 0x70 =
+keycode 0x71 = Pause
+keycode 0x72 = Insert          
+keycode 0x73 = Home
+keycode 0x74 = Prior           
+	shift   keycode 0x74 = Scroll_Backward 
+keycode 0x75 = Remove          
+keycode 0x76 = F4               F14              Console_16      
+	control keycode 0x76 = F4              
+	alt     keycode 0x76 = Console_4       
+	control alt     keycode 0x76 = Console_4       
+keycode 0x77 = End
+keycode 0x78 = F2               F12              Console_14      
+	control keycode 0x78 = F2              
+	alt     keycode 0x78 = Console_2       
+	control alt     keycode 0x78 = Console_2       
+keycode 0x79 = Next            
+	shift   keycode 0x79 = Scroll_Forward  
+keycode 0x7a = F1               F11              Console_13      
+	control keycode 0x7a = F1              
+	alt     keycode 0x7a = Console_1       
+	control alt     keycode 0x7a = Console_1       
+keycode 0x7b = Shift_R
+keycode 0x7c = Alt_R
+keycode 0x7d = Control_R
+keycode 0x7e =
+keycode 0x7f =
+#keycode 0x7f = Power
+	control shift   keycode 0x7f = Boot
+string F1 = "\033[[A"
+string F2 = "\033[[B"
+string F3 = "\033[[C"
+string F4 = "\033[[D"
+string F5 = "\033[[E"
+string F6 = "\033[17~"
+string F7 = "\033[18~"
+string F8 = "\033[19~"
+string F9 = "\033[20~"
+string F10 = "\033[21~"
+string F11 = "\033[23~"
+string F12 = "\033[24~"
+string F13 = "\033[25~"
+string F14 = "\033[26~"
+string F15 = "\033[28~"
+string F16 = "\033[29~"
+string F17 = "\033[31~"
+string F18 = "\033[32~"
+string F19 = "\033[33~"
+string F20 = "\033[34~"
+string Find = "\033[1~"
+string Insert = "\033[2~"
+string Remove = "\033[3~"
+string Select = "\033[4~"
+string Prior = "\033[5~"
+string Next = "\033[6~"
+string Macro = "\033[M"
+string Pause = "\033[P"
+compose '`' 'A' to 'À'
+compose '`' 'a' to 'à'
+compose '\'' 'A' to 'Á'
+compose '\'' 'a' to 'á'
+compose '^' 'A' to 'Â'
+compose '^' 'a' to 'â'
+compose '~' 'A' to 'Ã'
+compose '~' 'a' to 'ã'
+compose '"' 'A' to 'Ä'
+compose '"' 'a' to 'ä'
+compose 'O' 'A' to 'Å'
+compose 'o' 'a' to 'å'
+compose '0' 'A' to 'Å'
+compose '0' 'a' to 'å'
+compose 'A' 'A' to 'Å'
+compose 'a' 'a' to 'å'
+compose 'A' 'E' to 'Æ'
+compose 'a' 'e' to 'æ'
+compose ',' 'C' to 'Ç'
+compose ',' 'c' to 'ç'
+compose '`' 'E' to 'È'
+compose '`' 'e' to 'è'
+compose '\'' 'E' to 'É'
+compose '\'' 'e' to 'é'
+compose '^' 'E' to 'Ê'
+compose '^' 'e' to 'ê'
+compose '"' 'E' to 'Ë'
+compose '"' 'e' to 'ë'
+compose '`' 'I' to 'Ì'
+compose '`' 'i' to 'ì'
+compose '\'' 'I' to 'Í'
+compose '\'' 'i' to 'í'
+compose '^' 'I' to 'Î'
+compose '^' 'i' to 'î'
+compose '"' 'I' to 'Ï'
+compose '"' 'i' to 'ï'
+compose '-' 'D' to 'Ð'
+compose '-' 'd' to 'ð'
+compose '~' 'N' to 'Ñ'
+compose '~' 'n' to 'ñ'
+compose '`' 'O' to 'Ò'
+compose '`' 'o' to 'ò'
+compose '\'' 'O' to 'Ó'
+compose '\'' 'o' to 'ó'
+compose '^' 'O' to 'Ô'
+compose '^' 'o' to 'ô'
+compose '~' 'O' to 'Õ'
+compose '~' 'o' to 'õ'
+compose '"' 'O' to 'Ö'
+compose '"' 'o' to 'ö'
+compose '/' 'O' to 'Ø'
+compose '/' 'o' to 'ø'
+compose '`' 'U' to 'Ù'
+compose '`' 'u' to 'ù'
+compose '\'' 'U' to 'Ú'
+compose '\'' 'u' to 'ú'
+compose '^' 'U' to 'Û'
+compose '^' 'u' to 'û'
+compose '"' 'U' to 'Ü'
+compose '"' 'u' to 'ü'
+compose '\'' 'Y' to 'Ý'
+compose '\'' 'y' to 'ý'
+compose 'T' 'H' to 'Þ'
+compose 't' 'h' to 'þ'
+compose 's' 's' to 'ß'
+compose '"' 'y' to 'ÿ'
+compose 's' 'z' to 'ß'
+compose 'i' 'j' to 'ÿ'
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/mkmap.adb linux/drivers/usb/mkmap.adb
--- v2.3.5/linux/drivers/usb/mkmap.adb	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/mkmap.adb	Tue Jun  8 10:52:26 1999
@@ -0,0 +1,88 @@
+#!/usr/bin/perl
+
+($ME = $0) =~ s|.*/||;
+
+$file = "maps/mac.map";
+$line = 1;
+open(PC, $file) || die("$!");
+while(<PC>)
+{
+    if(/^\s*keycode\s+(\d+|0x[0-9a-fA-F]+)\s*=\s*(\S+)/)
+    {
+        my($idx) = $1;
+        my($sym) = $2;
+        if ($idx =~ "0x.*") {
+            $idx = hex($idx);
+        } else {
+            $idx = int($idx);
+        }
+        if(defined($map{uc($sym)}))
+        {
+            # print STDERR "$file:$line: warning: `$sym' redefined\n";
+        }
+        $map{uc($sym)} = $idx;
+    }
+    $line++;
+}
+close(PC);
+
+# $file = "maps/fixup.map";
+# $line = 1;
+# open(FIXUP, $file) || die("$!");
+# while(<FIXUP>)
+# {
+#     if(/^\s*keycode\s+(\d+)\s*=\s*/)
+#     {
+#       my($idx) = int($1);
+#       for $sym (split(/\s+/, $'))
+#         {
+#           $map{uc($sym)} = $idx;
+#       }
+#     }
+#     $line++;
+# }
+# close(FIXUP);
+
+$file = "maps/usb.map";
+$line = 1;
+open(USB, $file) || die("$!");
+while(<USB>)
+{
+    if(/^\s*keycode\s+(\d+)\s*=\s*/)
+    {
+        my($idx) = int($1);
+        for $sym (split(/\s+/, $'))
+        {
+            my($val) = $map{uc($sym)};
+            $map[$idx] = $val;
+            if(!defined($val))
+            {
+                print STDERR "$file:$line: warning: `$sym' undefined\n";
+            }
+            else
+            {
+                last;
+            }
+        }
+    }
+    $line++;
+}
+close(USB);
+
+print "unsigned char usb_kbd_map[256] = \n{\n";
+for($x = 0; $x < 32; $x++)
+{
+    if($x && !($x % 2))
+    {
+        print "\n";
+    }
+    print "  ";
+    for($y = 0; $y < 8; $y++)
+    {
+        my($idx) = $x * 8 + $y;
+        print sprintf("  0x%02x,",
+                      int(defined($map[$idx]) ? $map[$idx]:0));
+    }
+    print "\n";
+}
+print "};\n";
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/mouse.c linux/drivers/usb/mouse.c
--- v2.3.5/linux/drivers/usb/mouse.c	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/mouse.c	Mon Jun  7 13:36:40 1999
@@ -75,7 +75,7 @@
 	mouse->buttons = data[0] & 0x07;
 	mouse->dx += data[1]; /* data[] is signed, so this works */
 	mouse->dy -= data[2]; /* y-axis is reversed */
-	mouse->dz += data[3];
+	mouse->dz -= data[3];
 	mouse->ready = 1;
 
 	add_mouse_randomness((mouse->buttons << 24) + (mouse->dz << 16 ) + 
@@ -249,7 +249,7 @@
 		return -1;
 
 	/* Is it a mouse interface? */
-	interface = &dev->config[0].interface[0];
+	interface = &dev->config[0].altsetting[0].interface[0];
 	if (interface->bInterfaceClass != 3)
 		return -1;
 	if (interface->bInterfaceSubClass != 1)
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/ohci-debug.c linux/drivers/usb/ohci-debug.c
--- v2.3.5/linux/drivers/usb/ohci-debug.c	Mon May 17 09:55:22 1999
+++ linux/drivers/usb/ohci-debug.c	Tue Jun  8 10:52:26 1999
@@ -67,7 +67,7 @@
 
 void show_ohci_ed(struct ohci_ed *ed)
 {
-	int stat = ed->status;
+	int stat = le32_to_cpup(&ed->status);
 	int skip = (stat & OHCI_ED_SKIP);
 	int mps = (stat & OHCI_ED_MPS) >> 16;
 	int isoc = (stat & OHCI_ED_F_ISOC);
@@ -75,8 +75,8 @@
 	int dir = (stat & OHCI_ED_D);
 	int endnum = (stat & OHCI_ED_EN) >> 7;
 	int funcaddr = (stat & OHCI_ED_FA);
-	int halted = (ed->_head_td & 1);
-	int toggle = (ed->_head_td & 2) >> 1;
+	int halted = (le32_to_cpup(&ed->_head_td) & 1);
+	int toggle = (le32_to_cpup(&ed->_head_td) & 2) >> 1;
 
 	printk(KERN_DEBUG "   ohci ED:\n");
 	printk(KERN_DEBUG "     status     =  0x%x\n", stat);
@@ -92,23 +92,24 @@
 			endnum,
 			funcaddr,
 			(stat & ED_ALLOCATED) ? " Allocated" : "");
-	printk(KERN_DEBUG "     tail_td    =  0x%x\n", ed->tail_td);
+	printk(KERN_DEBUG "     tail_td    =  0x%x\n", ed_tail_td(ed));
 	printk(KERN_DEBUG "     head_td    =  0x%x\n", ed_head_td(ed));
-	printk(KERN_DEBUG "     next_ed    =  0x%x\n", ed->next_ed);
+	printk(KERN_DEBUG "     next_ed    =  0x%x\n", le32_to_cpup(&ed->next_ed));
 } /* show_ohci_ed() */
 
 
 void show_ohci_td(struct ohci_td *td)
 {
-	int td_round = td->info & OHCI_TD_ROUND;
-	int td_dir = td->info & OHCI_TD_D;
-	int td_int_delay = (td->info & OHCI_TD_IOC_DELAY) >> 21;
-	int td_toggle = (td->info & OHCI_TD_DT) >> 24;
+	int info = le32_to_cpup(&td->info);
+	int td_round = info & OHCI_TD_ROUND;
+	int td_dir = info & OHCI_TD_D;
+	int td_int_delay = (info & OHCI_TD_IOC_DELAY) >> 21;
+	int td_toggle = (info & OHCI_TD_DT) >> 24;
 	int td_errcnt = td_errorcount(*td);
-	int td_cc = OHCI_TD_CC_GET(td->info);
+	int td_cc = OHCI_TD_CC_GET(info);
 
 	printk(KERN_DEBUG "   ohci TD hardware fields:\n");
-	printk(KERN_DEBUG "      info     =  0x%x\n", td->info);
+	printk(KERN_DEBUG "      info     =  0x%x\n", info);
 	printk(KERN_DEBUG "        %s%s%s%d %s %s%d\n",
 		td_round ? "Rounding " : "",
 		(td_dir == OHCI_TD_D_IN) ? "Input " :
@@ -125,9 +126,9 @@
 
 	printk(KERN_DEBUG "        %s\n", td_allocated(*td) ? "Allocated" : "Free");
 
-	printk(KERN_DEBUG "      cur_buf  =  0x%x\n", td->cur_buf);
-	printk(KERN_DEBUG "      next_td  =  0x%x\n", td->next_td);
-	printk(KERN_DEBUG "      buf_end  =  0x%x\n", td->buf_end);
+	printk(KERN_DEBUG "      cur_buf  =  0x%x\n", le32_to_cpup(&td->cur_buf));
+	printk(KERN_DEBUG "      next_td  =  0x%x\n", le32_to_cpup(&td->next_td));
+	printk(KERN_DEBUG "      buf_end  =  0x%x\n", le32_to_cpup(&td->buf_end));
 	printk(KERN_DEBUG "   ohci TD driver fields:\n");
 	printk(KERN_DEBUG "      data     =  %p\n", td->data);
 	printk(KERN_DEBUG "      dev_id   =  %p\n", td->dev_id);
@@ -169,11 +170,14 @@
 	printk(KERN_DEBUG "  ohci_hcca\n");
 
 	for (idx=0; idx<NUM_INTS; idx++) {
-		printk(KERN_DEBUG "    int_table[%2d]  == %p\n", idx, hcca->int_table +idx);
+		printk(KERN_DEBUG "    int_table[%2d]  == %x\n", idx,
+		       le32_to_cpup(hcca->int_table + idx));
 	}
 
-	printk(KERN_DEBUG "    frame_no          == %d\n", hcca->frame_no);
-	printk(KERN_DEBUG "    donehead          == 0x%08x\n", hcca->donehead);
+	printk(KERN_DEBUG "    frame_no          == %d\n",
+	       le16_to_cpup(&hcca->frame_no));
+	printk(KERN_DEBUG "    donehead          == 0x%08x\n",
+	       le32_to_cpup(&hcca->donehead));
 } /* show_ohci_hcca() */
 
 
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/ohci.c linux/drivers/usb/ohci.c
--- v2.3.5/linux/drivers/usb/ohci.c	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/ohci.c	Tue Jun  8 10:52:26 1999
@@ -71,60 +71,84 @@
 
 static spinlock_t ohci_edtd_lock = SPIN_LOCK_UNLOCKED;
 
+#define FIELDS_OF_ED(e)	le32_to_cpup(&e->status), le32_to_cpup(&e->tail_td), \
+			le32_to_cpup(&e->_head_td), le32_to_cpup(&e->next_ed)
+#define FIELDS_OF_TD(t)	le32_to_cpup(&t->info), le32_to_cpup(&t->cur_buf), \
+			le32_to_cpup(&t->next_td), le32_to_cpup(&t->buf_end)
+
+static const char *cc_names[16] = {
+	"no error",
+	"CRC error",
+	"bit stuff error",
+	"data toggle mismatch",
+	"stall",
+	"device not responding",
+	"PID check failed",
+	"unexpected PID",
+	"data overrun",
+	"data underrun",
+	"reserved (10)",
+	"reserved (11)",
+	"buffer overrun",
+	"buffer underrun",
+	"not accessed (14)",
+	"not accessed"
+};
+
 /*
- * Add a TD to the end of the TD list on a given ED.  This function
- * does NOT advance the ED's tail_td pointer beyond the given TD.  To
- * add multiple TDs, call this function once for each TD.  Do not
- * "simply" update tail_td yourself... This function does more than
- * that.
- * 
- * If this ED is on the controller, you MUST set its SKIP flag before
- * calling this function.
+ * Add a chain of TDs to the end of the TD list on a given ED.
+ *
+ * This function uses the first TD of the chain as the new dummy TD
+ * for the ED, and uses the old dummy TD instead of the first TD
+ * of the chain.  The reason for this is that this makes it possible
+ * to update the TD chain without needing any locking between the
+ * CPU and the OHCI controller.
  *
- * Important!  This function needs locking and atomicity as it works
- * in parallel with the HC's DMA.  Locking ohci_edtd_lock while using
- * the function is a must.
+ * The return value is the pointer to the new first TD (the old
+ * dummy TD).
+ *
+ * Important!  This function is not re-entrant w.r.t. each ED.
+ * Locking ohci_edtd_lock while using the function is a must
+ * if there is any possibility of another CPU or an interrupt routine
+ * calling this function with the same ED.
  *
  * This function can be called by the interrupt handler.
  */
-static void ohci_add_td_to_ed(struct ohci_td *td, struct ohci_ed *ed)
+static struct ohci_td *ohci_add_td_to_ed(struct ohci_td *td,
+				struct ohci_td *last_td, struct ohci_ed *ed)
 {
-	struct ohci_td *dummy_td, *prev_td;
+	struct ohci_td *t, *dummy_td;
+	u32 new_dummy;
 
 	if (ed->tail_td == 0) {
 		printk("eek! an ED without a dummy_td\n");
+		return td;
 	}
 
-	/* The ED's tail_td is constant, always pointing to the
-	 * dummy_td.  The reason the ED never processes the dummy is
-	 * that it stops processing TDs as soon as head_td == tail_td.
-	 * When it advances to this last dummy TD it conveniently stops. */
-	dummy_td = bus_to_virt(ed->tail_td);
-
-	/* Dummy's data pointer is used to point to the previous TD */
-	if (ed_head_td(ed) != ed->tail_td) {
-		prev_td = (struct ohci_td *) dummy_td->data;
-	} else {
-		/* if the ED is empty, previous is meaningless */
-		/* We'll be inserting into the head of the list */
-		prev_td = NULL;
-	}
-
-	/* Store the new back pointer and set up this TD's next */
-	dummy_td->data = td;
-	td->next_td = ed->tail_td;
-
-	/* Store the TD pointer back to the ED */
-	td->ed = ed;
-
-	if (!prev_td) { /* No previous TD? then insert us at the head */
-		if (ed_head_td(ed) != ed->tail_td)
-			printk(KERN_DEBUG "Suspicious ED...\n");
-		set_ed_head_td(ed, virt_to_bus(td));	/* put it on the ED */
-	} else {
-		/* add the TD to the end */
-		prev_td->next_td = virt_to_bus(td);
+	/* Get a pointer to the current dummy TD. */
+	dummy_td = bus_to_virt(ed_tail_td(ed));
+
+	for (t = td; ; t = bus_to_virt(le32_to_cpup(&t->next_td))) {
+		t->ed = ed;
+		if (t == last_td)
+			break;
 	}
+
+	/* Make the last TD point back to the first, since it
+	 * will become the new dummy TD. */
+	new_dummy = cpu_to_le32(virt_to_bus(td));
+	last_td->next_td = new_dummy;
+
+	/* Copy the contents of the first TD into the dummy */
+	*dummy_td = *td;
+
+	/* Turn the first TD into a dummy */
+	make_dumb_td(td);
+
+	/* Set the HC's tail pointer to the new dummy */
+	ed->tail_td = new_dummy;
+
+	return dummy_td;	/* replacement head of chain */
 } /* ohci_add_td_to_ed() */
 
 
@@ -169,9 +193,7 @@
 
 	/* if the list is not empty, insert this ED at the front */
 	/* XXX should they go on the end? */
-	if (listhead) {
-		ed->next_ed = listhead;
-	}
+	ed->next_ed = cpu_to_le32(listhead);
 
 	/* update the hardware listhead pointer */
 	writel(virt_to_bus(ed), hw_listhead_p);
@@ -221,7 +243,7 @@
 	 * Insert this ED at the front of the list.
 	 */
 	ed->next_ed = int_ed->next_ed;
-	int_ed->next_ed = virt_to_bus(ed);
+	int_ed->next_ed = cpu_to_le32(virt_to_bus(ed));
 
 	spin_unlock_irqrestore(&ohci_edtd_lock, flags);
 
@@ -249,21 +271,21 @@
  */
 void ohci_wait_for_ed_safe(struct ohci_regs *regs, struct ohci_ed *ed, int ed_type)
 {
-	__u32 hw_listcurrent;
+	__u32 *hw_listcurrent;
 
 	/* tell the controller to skip this ED */
-	ed->status |= OHCI_ED_SKIP;
+	ed->status |= cpu_to_le32(OHCI_ED_SKIP);
 
 	switch (ed_type) {
 	case HCD_ED_CONTROL:
-		hw_listcurrent = readl(regs->ed_controlcurrent);
+		hw_listcurrent = &regs->ed_controlcurrent;
 		break;
 	case HCD_ED_BULK:
-		hw_listcurrent = readl(regs->ed_bulkcurrent);
+		hw_listcurrent = &regs->ed_bulkcurrent;
 		break;
 	case HCD_ED_ISOC:
 	case HCD_ED_INT:
-		hw_listcurrent = readl(regs->ed_periodcurrent);
+		hw_listcurrent = &regs->ed_periodcurrent;
 		break;
 	default:
 		return;
@@ -273,11 +295,11 @@
 	 * If the HC is processing this ED we need to wait until the
 	 * at least the next frame.
 	 */
-	if (virt_to_bus(ed) == hw_listcurrent) {
+	if (virt_to_bus(ed) == readl(hw_listcurrent)) {
 		DECLARE_WAITQUEUE(wait, current);
 
 #ifdef OHCI_DEBUG
-		printk("Waiting a frame for OHC to finish with ED %p\n", ed);
+		printk("Waiting a frame for OHC to finish with ED %p [%x %x %x %x]\n", ed, FIELDS_OF_ED(ed));
 #endif
 
 		add_wait_queue(&start_of_frame_wakeup, &wait);
@@ -349,7 +371,7 @@
 		/* walk the list and unlink the ED if found */
 		do {
 			prev = cur;
-			cur = bus_to_virt(cur->next_ed);
+			cur = bus_to_virt(le32_to_cpup(&cur->next_ed));
 
 			if (virt_to_bus(cur) == bus_ed) {
 				/* unlink from the list */
@@ -401,7 +423,7 @@
 		return;
 
 	/* set the "skip me bit" in this ED */
-	ed->status |= OHCI_ED_SKIP;
+	ed->status |= cpu_to_le32(OHCI_ED_SKIP);
 
 	/* XXX Assuming this list will never be circular */
 
@@ -415,7 +437,7 @@
 		/* FIXME: collapse this into a nice simple loop :) */
 		if (head_td->next_td != 0) {
 			prev_td = head_td;
-			cur_td = bus_to_virt(head_td->next_td);
+			cur_td = bus_to_virt(le32_to_cpup(&head_td->next_td));
 			for (;;) {
 				if (td == cur_td) {
 					/* remove it */
@@ -425,7 +447,7 @@
 				if (cur_td->next_td == 0)
 					break;
 				prev_td = cur_td;
-				cur_td = bus_to_virt(cur_td->next_td);
+				cur_td = bus_to_virt(le32_to_cpup(&cur_td->next_td));
 			}
 		}
 	}
@@ -437,7 +459,7 @@
 	ohci_free_td(td);
 
 	/* unset the "skip me bit" in this ED */
-	ed->status &= ~OHCI_ED_SKIP;
+	ed->status &= cpu_to_le32(~OHCI_ED_SKIP);
 
 	spin_unlock_irqrestore(&ohci_edtd_lock, flags);
 } /* ohci_remove_td_from_ed() */
@@ -465,7 +487,7 @@
 			/* zero out the TD */
 			memset(new_td, 0, sizeof(*new_td));
 			/* mark the new TDs as unaccessed */
-			new_td->info = OHCI_TD_CC_NEW;
+			new_td->info = cpu_to_le32(OHCI_TD_CC_NEW);
 			/* mark it as allocated */
 			allocate_td(new_td);
 			return new_td;
@@ -492,7 +514,7 @@
 			/* zero out the ED */
 			memset(new_ed, 0, sizeof(*new_ed));
 			/* all new EDs start with the SKIP bit set */
-			new_ed->status |= OHCI_ED_SKIP;
+			new_ed->status |= cpu_to_le32(OHCI_ED_SKIP);
 			/* mark it as allocated */
 			allocate_ed(new_ed);
 			return new_ed;
@@ -509,12 +531,21 @@
 	if (!ed)
 		return;
 
-	if (ed->tail_td == 0) {
-		printk("yikes! an ED without a dummy_td\n");
-	} else
-		ohci_free_td((struct ohci_td *)bus_to_virt(ed->tail_td));
+	if (ed_head_td(ed) != 0) {
+		struct ohci_td *td, *tail_td, *next_td;
 
-	ed->status &= ~(__u32)ED_ALLOCATED;
+		td = bus_to_virt(ed_head_td(ed));
+		tail_td = bus_to_virt(ed_tail_td(ed));
+		for (;;) {
+			next_td = bus_to_virt(le32_to_cpup(&td->next_td));
+			ohci_free_td(td);
+			if (td == tail_td)
+				break;
+			td = next_td;
+		}
+	}
+
+	ed->status &= cpu_to_le32(~(__u32)ED_ALLOCATED);
 } /* ohci_free_ed() */
 
 
@@ -527,12 +558,13 @@
 inline struct ohci_td *ohci_fill_new_td(struct ohci_td *td, int dir, int toggle, __u32 flags, void *data, __u32 len, void *dev_id, usb_device_irq completed)
 {
 	/* hardware fields */
-	td->info = OHCI_TD_CC_NEW |
-		(dir & OHCI_TD_D) |
-		(toggle & OHCI_TD_DT) |
-		flags;
-	td->cur_buf = (data == NULL) ? 0 : virt_to_bus(data);
-	td->buf_end = (len == 0) ? 0 : td->cur_buf + len - 1;
+	td->info = cpu_to_le32(OHCI_TD_CC_NEW |
+			       (dir & OHCI_TD_D) |
+			       (toggle & OHCI_TD_DT) |
+			       flags);
+	td->cur_buf = (data == NULL) ? 0 : cpu_to_le32(virt_to_bus(data));
+	td->buf_end = (len == 0) ? 0 :
+		cpu_to_le32(le32_to_cpup(&td->cur_buf) + len - 1);
 
 	/* driver fields */
 	td->data = data;
@@ -555,11 +587,13 @@
  *  not be any!).  This assumes that the ED is Allocated and will
  *  force the Allocated bit on.
  */
-struct ohci_ed *ohci_fill_ed(struct ohci_device *dev, struct ohci_ed *ed, int maxpacketsize, int lowspeed, int endp_id, int isoc_tds)
+struct ohci_ed *ohci_fill_ed(struct ohci_device *dev, struct ohci_ed *ed,
+			     int maxpacketsize, int lowspeed, int endp_id,
+			     int isoc_tds)
 {
 	struct ohci_td *dummy_td;
 
-	if (ed_head_td(ed) != ed->tail_td)
+	if (ed_head_td(ed) != ed_tail_td(ed))
 		printk("Reusing a non-empty ED %p!\n", ed);
 
 	if (!ed->tail_td) {
@@ -569,9 +603,9 @@
 			return NULL;	/* no dummy available! */
 		}
 		make_dumb_td(dummy_td);	/* flag it as a dummy */
-		ed->tail_td = virt_to_bus(dummy_td);
+		ed->tail_td = cpu_to_le32(virt_to_bus(dummy_td));
 	} else {
-		dummy_td = bus_to_virt(ed->tail_td);
+		dummy_td = bus_to_virt(ed_tail_td(ed));
 		if (!td_dummy(*dummy_td))
 			printk("ED %p's dummy %p is screwy\n", ed, dummy_td);
 	}
@@ -579,11 +613,11 @@
 	/* set the head TD to the dummy and clear the Carry & Halted bits */
 	ed->_head_td = ed->tail_td;
 
-	ed->status = \
+	ed->status = cpu_to_le32(
 		ed_set_maxpacket(maxpacketsize) |
 		ed_set_speed(lowspeed) |
 		(endp_id & 0x7ff) |
-		((isoc_tds == 0) ? OHCI_ED_F_NORM : OHCI_ED_F_ISOC);
+		((isoc_tds == 0) ? OHCI_ED_F_NORM : OHCI_ED_F_ISOC));
 	allocate_ed(ed);
 	ed->next_ed = 0;
 
@@ -611,6 +645,7 @@
 	struct ohci_device *dev = usb_to_ohci(usb);
 	struct ohci_td *td;
 	struct ohci_ed *interrupt_ed;	/* endpoint descriptor for this irq */
+	int maxps = usb_maxpacket(usb, pipe);
 
 	/* Get an ED and TD */
 	interrupt_ed = ohci_get_free_ed(dev);
@@ -630,14 +665,16 @@
 	 * Set the max packet size, device speed, endpoint number, usb
 	 * device number (function address), and type of TD.
 	 */
-	ohci_fill_ed(dev, interrupt_ed, usb_maxpacket(usb,pipe), usb_pipeslow(pipe),
-		usb_pipe_endpdev(pipe), 0 /* normal TDs */);
+	ohci_fill_ed(dev, interrupt_ed, maxps, usb_pipeslow(pipe),
+		     usb_pipe_endpdev(pipe), 0 /* normal TDs */);
 
 	/* Fill in the TD */
+	if (maxps > sizeof(dev->data))
+		maxps = sizeof(dev->data);
 	ohci_fill_new_td(td, td_set_dir_out(usb_pipeout(pipe)),
 			TOGGLE_AUTO,
 			OHCI_TD_ROUND,
-			&dev->data, DATA_BUF_LEN,
+			dev->data, maxps,
 			dev_id, handler);
 	/*
 	 * TODO: be aware of how the OHCI controller deals with DMA
@@ -647,12 +684,12 @@
 	/*
 	 *  Put the TD onto our ED and make sure its ready to run
 	 */
-	ohci_add_td_to_ed(td, interrupt_ed);
-	interrupt_ed->status &= ~OHCI_ED_SKIP;
+	td = ohci_add_td_to_ed(td, td, interrupt_ed);
+	interrupt_ed->status &= cpu_to_le32(~OHCI_ED_SKIP);
 	ohci_unhalt_ed(interrupt_ed);
 
-	/* Linus did this. see asm/system.h; scary concept... I don't
-	 * know if its needed here or not but it won't hurt. */
+	/* Make sure all the stores above get done before
+	 * the store which tells the OHCI about the new ed. */
 	wmb();
 
 	/* Assimilate the new ED into the collective */
@@ -700,7 +737,8 @@
  *
  * This function can NOT be called from an interrupt.
  */
-static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, void *cmd, void *data, int len)
+static int ohci_control_msg(struct usb_device *usb, unsigned int pipe,
+			    devrequest *cmd, void *data, int len)
 {
 	struct ohci_device *dev = usb_to_ohci(usb);
 	struct ohci_ed *control_ed = ohci_get_free_ed(dev);
@@ -708,8 +746,16 @@
 	DECLARE_WAITQUEUE(wait, current);
 	unsigned long flags;
 	int completion_status = -1;
+	devrequest our_cmd;
 
-#ifdef OHCI_DEBUG 
+	/* byte-swap fields of cmd if necessary */
+	our_cmd = *cmd;
+	cpu_to_le16s(&our_cmd.value);
+	cpu_to_le16s(&our_cmd.index);
+	cpu_to_le16s(&our_cmd.length);
+
+#ifdef OHCI_DEBUG
+	if (MegaDebug)
 	printk(KERN_DEBUG "ohci_control_msg %p (ohci_dev: %p) pipe %x, cmd %p, data %p, len %d\n", usb, dev, pipe, cmd, data, len);
 #endif
 	if (!control_ed) {
@@ -743,12 +789,11 @@
 	 * uses a DATA0 packet.
 	 *
 	 * The setup packet contains a devrequest (usb.h) which
-	 * will always be 8 bytes long.  FIXME: the cmd parameter
-	 * should be a pointer to one of these instead of a void* !!!
+	 * will always be 8 bytes long.
 	 */
 	ohci_fill_new_td(setup_td, OHCI_TD_D_SETUP, TOGGLE_DATA0,
 			OHCI_TD_IOC_OFF,
-			cmd, 8,		/* cmd is always 8 bytes long */
+			&our_cmd, 8,	/* cmd is always 8 bytes long */
 			NULL, NULL);
 
 	/* allocate the next TD */
@@ -761,7 +806,7 @@
 	}
 
 	/* link to the next TD */
-	setup_td->next_td = virt_to_bus(data_td);
+	setup_td->next_td = cpu_to_le32(virt_to_bus(data_td));
 
 	if (len > 0) {
 
@@ -795,7 +840,7 @@
 			return -1;
 		}
 
-		data_td->next_td = virt_to_bus(status_td);
+		data_td->next_td = cpu_to_le32(virt_to_bus(status_td));
 	} else {
 		status_td = data_td; /* no data_td, use it for status */
 	}
@@ -815,13 +860,7 @@
 	 * Add the chain of 2-3 control TDs to the control ED's TD list
 	 */
 	spin_lock_irqsave(&ohci_edtd_lock, flags);
-	control_ed->status |= OHCI_ED_SKIP;
-	ohci_add_td_to_ed(setup_td, control_ed);
-	if (data_td != status_td)
-		ohci_add_td_to_ed(data_td, control_ed);
-	ohci_add_td_to_ed(status_td, control_ed);
-	control_ed->status &= ~OHCI_ED_SKIP;
-	ohci_unhalt_ed(control_ed);
+	setup_td = ohci_add_td_to_ed(setup_td, status_td, control_ed);
 	spin_unlock_irqrestore(&ohci_edtd_lock, flags);
 
 #ifdef OHCI_DEBUG
@@ -873,17 +912,28 @@
 	}
 #endif
 
-	/* clean up */
-	ohci_free_td(setup_td);
-	if (data_td != status_td)
-		ohci_free_td(data_td);
-	ohci_free_td(status_td);
 	/* remove the control ED from the HC */
 	ohci_remove_control_ed(dev->ohci, control_ed);
 	ohci_free_ed(control_ed);	 /* return it to the pool */
 
-#if 0
-	printk(KERN_DEBUG "leaving ohci_control_msg\n");
+#ifdef OHCI_DEBUG
+	if (completion_status != 0) {
+		printk(KERN_ERR "ohci_control_msg: %s on cmd %x %x %x %x %x\n",
+		       cc_names[completion_status & 0xf], cmd->requesttype,
+		       cmd->request, cmd->value, cmd->index, cmd->length);
+	} else if (!usb_pipeout(pipe)) {
+		unsigned char *q = data;
+		int i;
+		printk(KERN_DEBUG "ctrl msg %x %x %x %x %x returned:",
+		       cmd->requesttype, cmd->request, cmd->value, cmd->index,
+		       cmd->length);
+		for (i = 0; i < len; ++i) {
+			if (i % 16 == 0)
+				printk("\n" KERN_DEBUG);
+			printk(" %x", q[i]);
+		}
+		printk("\n");
+	}
 #endif
 	return completion_status;
 } /* ohci_control_msg() */
@@ -921,7 +971,7 @@
 	/* Initialize all EDs in a new device with the skip flag so that
 	 * they are ignored by the controller until set otherwise. */
 	for (idx = 0; idx < NUM_EDS; ++idx) {
-		dev->ed[idx].status |= OHCI_ED_SKIP;
+		dev->ed[idx].status = cpu_to_le32(OHCI_ED_SKIP);
 	}
 
 	/*
@@ -1221,20 +1271,20 @@
  */
 static void ohci_root_hub_events(struct ohci *ohci)
 {
-		int num = 0;
+	int num = 0;
 	struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub);
 	int maxport = root_hub->usb->maxchild;
 
 	if (!waitqueue_active(&ohci_configure))
 		return;
-		do {
-			__u32 *portstatus_p = &ohci->regs->roothub.portstatus[num];
-			if (readl(portstatus_p) & PORT_CSC) {
-				if (waitqueue_active(&ohci_configure))
-					wake_up(&ohci_configure);
-				return;
-			}
-		} while (++num < maxport);
+	do {
+		__u32 *portstatus_p = &ohci->regs->roothub.portstatus[num];
+		if (readl(portstatus_p) & PORT_CSC) {
+			if (waitqueue_active(&ohci_configure))
+				wake_up(&ohci_configure);
+			return;
+		}
+	} while (++num < maxport);
 	
 } /* ohci_root_hub_events() */
 
@@ -1255,16 +1305,15 @@
 	struct ohci_hcca *hcca = root_hub->hcca;
 	struct ohci_td *td_list = NULL;
 	struct ohci_td *td_rev = NULL;
-  	
-	td_list_hc = hcca->donehead & 0xfffffff0;
+
+	td_list_hc = le32_to_cpup(&hcca->donehead) & 0xfffffff0;
 	hcca->donehead = 0;
 
  	while(td_list_hc) {
 		td_list = (struct ohci_td *) bus_to_virt(td_list_hc);
 		td_list->next_dl_td = td_rev;
-			
 		td_rev = td_list;
-		td_list_hc = td_list->next_td & 0xfffffff0;
+		td_list_hc = le32_to_cpup(&td_list->next_td) & 0xfffffff0;
 	}
 
 	return td_list;
@@ -1288,28 +1337,66 @@
 
 	while (td != NULL) {
 		struct ohci_td *next_td = td->next_dl_td;
+		int cc = OHCI_TD_CC_GET(le32_to_cpup(&td->info));
 
 		if (td_dummy(*td))
 			printk("yikes! reaping a dummy TD\n");
 
 		/* FIXME: munge td->info into a future standard status format */
+
+		if (cc != 0 && ohci_ed_halted(td->ed) && td->completed == 0) {
+			/*
+			 * There was an error on this TD and the ED
+			 * is halted, and this was not the last TD
+			 * of the transaction, so there will be TDs
+			 * to clean off the ED.
+			 * (We assume that a TD with a non-NULL completed
+			 * field is the last one of a transaction.
+			 * Ultimately we should have a flag in the TD
+			 * to say that it is the last one.)
+			 */
+			struct ohci_ed *ed = td->ed;
+			struct ohci_td *tail_td = bus_to_virt(ed_tail_td(ed));
+			struct ohci_td *ntd;
+
+			ohci_free_td(td);
+			td = ntd = bus_to_virt(ed_head_td(ed));
+			while (td != tail_td) {
+				ntd = bus_to_virt(le32_to_cpup(&td->next_td));
+				if (td->completed != 0)
+					break;
+				ohci_free_td(td);
+				td = ntd;
+			}
+			/* Set the ED head past the ones we cleaned
+			   off, and clear the halted flag */
+			set_ed_head_td(ed, virt_to_bus(ntd));
+			ohci_unhalt_ed(ed);
+			/* If we didn't find a TD with a completion
+			   routine, give up */
+			if (td == tail_td) {
+				td = next_td;
+				continue;
+			}
+		}
+
 		/* Check if TD should be re-queued */
 		if ((td->completed != NULL) &&
-		    (td->completed(OHCI_TD_CC_GET(td->info), td->data, td->dev_id)))
-		{
+		    (td->completed(cc, td->data, td->dev_id))) {
 			/* Mark the TD as active again:
 			 * Set the not accessed condition code
 			 * Reset the Error count
-			 * [FIXME: report errors to the device's driver]
 			 */
-			td->info |= OHCI_TD_CC_NEW;
+			td->info |= cpu_to_le32(OHCI_TD_CC_NEW);
 			clear_td_errorcount(td);
+			/* reset the toggle field to TOGGLE_AUTO (0) */
+			td->info &= cpu_to_le32(~OHCI_TD_DT);
 
 			/* point it back to the start of the data buffer */
-			td->cur_buf = virt_to_bus(td->data);
+			td->cur_buf = cpu_to_le32(virt_to_bus(td->data));
 
 			/* insert it back on its ED */
-			ohci_add_td_to_ed(td, td->ed);
+			ohci_add_td_to_ed(td, td, td->ed);
 		} else {
 			/* return it to the pool of free TDs */
 			ohci_free_td(td);
@@ -1341,7 +1428,7 @@
 	/* make context = the interrupt status bits that we care about */
 	if (hcca->donehead != 0) {
 		context = OHCI_INTR_WDH;   /* hcca donehead needs processing */
-		if (hcca->donehead & 1) {
+		if (hcca->donehead & cpu_to_le32(1)) {
 			context |= status;  /* other status change to check */
 		}
 	} else {
@@ -1352,7 +1439,7 @@
 		}
 	}
 
-	/* Disable HC interrupts */
+	/* Disable HC interrupts */ /* why? - paulus */
 	writel(OHCI_INTR_MIE, &regs->intrdisable);
 
 	/* Process the done list */
@@ -1361,7 +1448,7 @@
 		ohci_reap_donelist(ohci);
 
 		/* reset the done queue and tell the controller */
-		hcca->donehead = 0;
+		hcca->donehead = 0;	/* XXX already done in ohci_reverse_donelist */
 		writel(OHCI_INTR_WDH, &regs->intrstatus);
 
 		context &= ~OHCI_INTR_WDH;  /* mark this as checked */
@@ -1499,7 +1586,8 @@
 	 * page as that's guaranteed to have a nice boundary.
 	 */
 	dev->hcca = (struct ohci_hcca *) __get_free_page(GFP_KERNEL);
-
+	memset(dev->hcca, 0, sizeof(struct ohci_hcca));
+ 
 	/* Tell the controller where the HCCA is */
 	writel(virt_to_bus(dev->hcca), &ohci->regs->hcca);
 
@@ -1523,11 +1611,11 @@
 	 * Initialize the ED polling "tree" (for simplicity's sake in
 	 * this driver many nodes in the tree will be identical)
 	 */
-	dev->ed[ED_INT_32].next_ed = virt_to_bus(&dev->ed[ED_INT_16]);
-	dev->ed[ED_INT_16].next_ed = virt_to_bus(&dev->ed[ED_INT_8]);
-	dev->ed[ED_INT_8].next_ed = virt_to_bus(&dev->ed[ED_INT_4]);
-	dev->ed[ED_INT_4].next_ed = virt_to_bus(&dev->ed[ED_INT_2]);
-	dev->ed[ED_INT_2].next_ed = virt_to_bus(&dev->ed[ED_INT_1]);
+	dev->ed[ED_INT_32].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_16]));
+	dev->ed[ED_INT_16].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_8]));
+	dev->ed[ED_INT_8].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_4]));
+	dev->ed[ED_INT_4].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_2]));
+	dev->ed[ED_INT_2].next_ed = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_1]));
 
 	/*
 	 * Initialize the polling table to call interrupts at the
@@ -1535,23 +1623,23 @@
 	 * placeholders.  They have their SKIP bit set and are used as
 	 * list heads to insert real EDs onto.
 	 */
-	dev->hcca->int_table[0] = virt_to_bus(&dev->ed[ED_INT_1]);
+	dev->hcca->int_table[0] = cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_1]));
 	for (i = 1; i < NUM_INTS; i++) {
 		if (i & 16)
 			dev->hcca->int_table[i] =
-				virt_to_bus(&dev->ed[ED_INT_32]);
+				cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_32]));
 		if (i & 8)
 			dev->hcca->int_table[i] =
-				virt_to_bus(&dev->ed[ED_INT_16]);
+				cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_16]));
 		if (i & 4)
 			dev->hcca->int_table[i] =
-				virt_to_bus(&dev->ed[ED_INT_8]);
+				cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_8]));
 		if (i & 2)
 			dev->hcca->int_table[i] =
-				virt_to_bus(&dev->ed[ED_INT_4]);
+				cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_4]));
 		if (i & 1)
 			dev->hcca->int_table[i] =
-				virt_to_bus(&dev->ed[ED_INT_2]);
+				cpu_to_le32(virt_to_bus(&dev->ed[ED_INT_2]));
 	}
 
 	/*
@@ -1569,6 +1657,7 @@
 #if 0
 	printk(KERN_DEBUG "leaving alloc_ohci %p\n", ohci);
 #endif
+printk("alloc_ohci done\n");
 
 	return ohci;
 } /* alloc_ohci() */
@@ -1929,7 +2018,7 @@
 /*
  *  Clean up when unloading the module
  */
-void module_cleanup(void){
+void cleanup_module(void){
 #	ifdef CONFIG_APM
 	apm_unregister_callback(&handle_apm_event);
 #	endif
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/ohci.h linux/drivers/usb/ohci.h
--- v2.3.5/linux/drivers/usb/ohci.h	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/ohci.h	Tue Jun  8 10:52:26 1999
@@ -60,14 +60,14 @@
 #define TOGGLE_DATA1	(3 << 24)	/* force Data1 */
 #define td_force_toggle(b)	(((b) | 2) << 24)
 #define OHCI_TD_ERRCNT	(3 << 26)	/* error count */
-#define td_errorcount(td)	(((td).info >> 26) & 3)
-#define clear_td_errorcount(td)	((td)->info &= ~(__u32)OHCI_TD_ERRCNT)
+#define td_errorcount(td)	((le32_to_cpup(&(td).info) >> 26) & 3)
+#define clear_td_errorcount(td)	((td)->info &= cpu_to_le32(~(__u32)OHCI_TD_ERRCNT))
 #define OHCI_TD_CC	(0xf << 28)	/* condition code */
 #define OHCI_TD_CC_GET(td_i) (((td_i) >> 28) & 0xf)
 #define OHCI_TD_CC_NEW	(OHCI_TD_CC)	/* set this on all unaccessed TDs! */
-#define td_cc_notaccessed(td)	(((td).info >> 29) == 7)
-#define td_cc_accessed(td)	(((td).info >> 29) != 7)
-#define td_cc_noerror(td)	((((td).info) & OHCI_TD_CC) == 0)
+#define td_cc_notaccessed(td)	((le32_to_cpup(&(td).info) >> 29) == 7)
+#define td_cc_accessed(td)	((le32_to_cpup(&(td).info) >> 29) != 7)
+#define td_cc_noerror(td)	(((le32_to_cpup(&(td).info)) & OHCI_TD_CC) == 0)
 #define td_active(td)	(!td_cc_noerror((td)) && (td_errorcount((td)) < 3))
 #define td_done(td)	(td_cc_noerror((td)) || (td_errorcount((td)) == 3))
 
@@ -95,15 +95,17 @@
 } __attribute((aligned(16)));
 
 /* get the head_td */
-#define ed_head_td(ed)	((ed)->_head_td & 0xfffffff0)
+#define ed_head_td(ed)	(le32_to_cpup(&(ed)->_head_td) & 0xfffffff0)
+#define ed_tail_td(ed)	(le32_to_cpup(&(ed)->tail_td))
 
 /* save the carry & halted flag while setting the head_td */
-#define set_ed_head_td(ed, td)	((ed)->_head_td = (td) | ((ed)->_head_td & 3))
+#define set_ed_head_td(ed, td)	((ed)->_head_td = cpu_to_le32((td)) \
+				 | ((ed)->_head_td & cpu_to_le32(3)))
 
 /* Control the ED's halted flag */
-#define ohci_halt_ed(ed)	((ed)->_head_td |= 1)
-#define ohci_unhalt_ed(ed)	((ed)->_head_td &= ~(__u32)1)
-#define ohci_ed_halted(ed)	((ed)->_head_td & 1)
+#define ohci_halt_ed(ed)	((ed)->_head_td |= cpu_to_le32(1))
+#define ohci_unhalt_ed(ed)	((ed)->_head_td &= cpu_to_le32(~(__u32)1))
+#define ohci_ed_halted(ed)	((ed)->_head_td & cpu_to_le32(1))
 
 #define OHCI_ED_SKIP	(1 << 14)
 #define OHCI_ED_MPS	(0x7ff << 16)
@@ -130,8 +132,8 @@
  * driver or not.  If the bit is set, it is being used.
  */
 #define ED_ALLOCATED	(1 << 31)
-#define ed_allocated(ed)	((ed).status & ED_ALLOCATED)
-#define allocate_ed(ed)		((ed)->status |= ED_ALLOCATED)
+#define ed_allocated(ed)	(le32_to_cpup(&(ed).status) & ED_ALLOCATED)
+#define allocate_ed(ed)		((ed)->status |= cpu_to_le32(ED_ALLOCATED))
 
 /*
  * The HCCA (Host Controller Communications Area) is a 256 byte
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/printer.c linux/drivers/usb/printer.c
--- v2.3.5/linux/drivers/usb/printer.c	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/printer.c	Mon Jun  7 20:04:01 1999
@@ -179,13 +179,14 @@
 			}
 			result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev,
 					 usb_sndbulkpipe(p->pusb_dev, 1), obuf, thistime, &partial);
-			if (result & 0x08) {	/* NAK - so hold for a while */
-				obuf += partial;
-				thistime -= partial;
+			if (result == USB_ST_TIMEOUT) {	/* NAK - so hold for a while */
 				if(!maxretry--)
 					return -ETIME;
                                 interruptible_sleep_on_timeout(&p->wait_q, NAK_TIMEOUT);
 				continue;
+			} else if (!result & partial) {
+				obuf += partial;
+				thistime -= partial;
 			} else
 				break;
 		};
@@ -230,7 +231,7 @@
 			  usb_rcvbulkpipe(p->pusb_dev, 2), buf, this_read, &partial);
 
 		/* unlike writes, we don't retry a NAK, just stop now */
-		if (result & 0x08)
+		if (!result & partial)
 			count = this_read = partial;
 		else if (result)
 			return -EIO;
@@ -260,7 +261,7 @@
 		return -1;
 	}
 
-	interface = dev->config->interface;
+	interface = dev->config->altsetting->interface;
 
 	/* Lets be paranoid (for the moment)*/
 	if (interface->bInterfaceClass != 7 ||
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/stopusb linux/drivers/usb/stopusb
--- v2.3.5/linux/drivers/usb/stopusb	Tue May 11 10:04:03 1999
+++ linux/drivers/usb/stopusb	Fri Jun  4 13:33:43 1999
@@ -1,7 +1,7 @@
 #!/bin/sh
 
-killall ohci-control
+killall uhci-control
 
 sleep 2
 
-rmmod usb-ohci
+rmmod usb-uhci
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/uhci.c linux/drivers/usb/uhci.c
--- v2.3.5/linux/drivers/usb/uhci.c	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/uhci.c	Tue Jun  8 10:53:43 1999
@@ -52,11 +52,40 @@
 static int apm_resume = 0;
 #endif
 
+static int uhci_debug = 1;
+
 #define compile_assert(x) do { switch (0) { case 1: case !(x): } } while (0)
 
 static DECLARE_WAIT_QUEUE_HEAD(uhci_configure);
 
 /*
+ * Map status to standard result codes
+ */
+static int uhci_map_status(int status, int dir_out)
+{
+    if (!status)
+    	    return USB_ST_NOERROR;
+    if (status & 0x02)				/* Bitstuff error*/
+    	    return USB_ST_BITSTUFF;
+    if (status & 0x04)	{			/* CRC/Timeout */
+	if (dir_out)
+    	    return USB_ST_TIMEOUT;
+	else
+	    return USB_ST_CRC;
+    }
+    if (status & 0x08)				/* NAK */
+    	    return USB_ST_TIMEOUT;
+    if (status & 0x10)				/* Babble */
+    	    return USB_ST_STALL;
+    if (status & 0x20)				/* Buffer error */
+    	    return USB_ST_BUFFERUNDERRUN;
+    if (status & 0x40)				/* Stalled */
+    	    return USB_ST_STALL;
+    if (status & 0x80)				/* Active */
+    	    return USB_ST_NOERROR;
+    return USB_ST_INTERNALERROR;
+}
+/*
  * Return the result of a TD..
  */
 static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned long *rval)
@@ -64,21 +93,35 @@
 	unsigned int status;
 	struct uhci_td *tmp = td->first;
 
+	if(rval)
+		*rval = 0;
+
 	/* locate the first failing td, if any */
 
 	do {
 		status = (tmp->status >> 16) & 0xff;
-		if (status)
+		if (status) {
+			/* must reset the toggle on first error */
+    			if (uhci_debug) {
+			    printk("Set toggle from %x rval %d\n", (unsigned int)tmp, rval ? *rval : 0);
+			}
+			usb_settoggle(dev->usb, usb_pipeendpoint(tmp->info), (tmp->info >> 19) & 1);
 			break;
+		} else {
+		    if(rval)
+			*rval += (tmp->status & 0x3ff) + 1;
+		}
 		if ((tmp->link & 1) || (tmp->link & 2))
 			break;
 		tmp = bus_to_virt(tmp->link & ~0xF);
 	} while (1);
 
-	if(rval)
-		*rval = 0;
+
+	if (!status)
+    		return USB_ST_NOERROR;
+
 	/* Some debugging code */
-	if (status && (!usb_pipeendpoint(tmp->info) || !(status & 0x08)) ) {
+	if (uhci_debug /* && (!usb_pipeendpoint(tmp->info) || !(status & 0x08))*/ ) {
 		int i = 10;
 
 		tmp = td->first;
@@ -93,35 +136,21 @@
 				break;
 		} while (1);
 	}
-	if (usb_pipeendpoint(tmp->info) && (status & 0x08)) {
-//		printk("uhci_td_result() - NAK\n");
-		/* find total length xferred and reset toggle on failed packets */
-		tmp = td->first;
-		do {
-			/* sum up packets that did not fail */
-			if(rval && !((tmp->status >> 16) & 0xff))
-				*rval += (tmp->status & 0x3ff) + 1;
-
-			/* 
-			 * Note - only the first to fail will be marked NAK
-			 */
-			if (tmp->status & 0xFF0000) {
-				/* must reset the toggle on any error */
-				usb_settoggle(dev->usb, usb_pipeendpoint(tmp->info), (tmp->info >> 19) & 1);
-				break;
-			} 
 
-			if ((tmp->link & 1) || (tmp->link & 2))
-				break;
-			tmp = bus_to_virt(tmp->link & ~0xF);
-		} while (1);
-#if 0
-		if (rval) {
-			printk("uhci_td_result returning partial count %d\n", *rval);
-		}
-#endif
+	if (status & 0x40) {
+	    /* endpoint has stalled - mark it halted */
+
+	    usb_endpoint_halt(dev->usb, usb_pipeendpoint(tmp->info));
+	    return USB_ST_STALL;
+
+	}
+
+	if (status == 0x80) {
+	    /* still active */
+	    if (!rval)
+		return USB_ST_DATAUNDERRUN;
 	}
-	return status;		
+	return uhci_map_status(status, usb_pipeout(tmp->info));		
 }
 
 /*
@@ -273,8 +302,10 @@
 	for (; (inuse = test_and_set_bit(0, &td->inuse)) != 0 && td < &dev->td[UHCI_MAXTD]; td++)
 		;
 
-	if (!inuse)
+	if (!inuse) {
+    		td->inuse = 1;
 		return(td);
+	}
 
 	printk("ran out of td's for dev %p\n", dev);
 	return(NULL);
@@ -341,22 +372,74 @@
 
 	td->link = 1;
 	td->status = status;			/* In */
-	td->info = destination | (7 << 21);	/* 8 bytes of data */
+	td->info = destination | (7 << 21) | (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe)) << 19);	/* 8 bytes of data */
 	td->buffer = virt_to_bus(dev->data);
 	td->first = td;
 	td->qh = interrupt_qh;
-	interrupt_qh->skel = &root_hub->skel_int8_qh;
+	td->dev = usb_dev;
+
+	/* if period 0, insert into fast q */
+
+	if (period == 0) {
+		td->inuse |= 2;
+		interrupt_qh->skel = &root_hub->skel_int2_qh;
+	} else
+		interrupt_qh->skel = &root_hub->skel_int8_qh;
 
 	uhci_add_irq_list(dev->uhci, td, handler, dev_id);
 
 	uhci_insert_td_in_qh(interrupt_qh, td);
 
 	/* Add it into the skeleton */
-	uhci_insert_qh(&root_hub->skel_int8_qh, interrupt_qh);
+	uhci_insert_qh(interrupt_qh->skel, interrupt_qh);
 	return 0;
 }
 
 /*
+ * Remove running irq td from queues
+ */
+
+static int uhci_remove_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
+{
+    struct uhci_device *dev = usb_to_uhci(usb_dev);
+    struct uhci_device *root_hub=usb_to_uhci(dev->uhci->bus->root_hub);
+    struct uhci_td *td;
+    struct uhci_qh *interrupt_qh;
+    unsigned long flags;
+    struct list_head *head = &dev->uhci->interrupt_list;
+    struct list_head *tmp;
+
+    spin_lock_irqsave(&irqlist_lock, flags);
+
+    /* find the TD in the interrupt list */
+
+    tmp = head->next;
+    while (tmp != head) {
+	td = list_entry(tmp, struct uhci_td, irq_list);
+	if (td->dev_id == dev_id && td->completed == handler) {
+
+	    /* found the right one - let's remove it */
+
+	    /* notify removal */
+
+	    td->completed(USB_ST_REMOVED, NULL, td->dev_id);
+
+	    /* this is DANGEROUS - not sure whether this is right */
+
+	    list_del(&td->irq_list);
+	    uhci_remove_td(td);
+	    interrupt_qh = td->qh;
+	    uhci_remove_qh(interrupt_qh->skel, interrupt_qh);
+	    uhci_td_deallocate(td);
+	    uhci_qh_deallocate(interrupt_qh);
+	    spin_unlock_irqrestore(&irqlist_lock, flags);
+	    return USB_ST_NOERROR;
+	}
+    }
+    spin_unlock_irqrestore(&irqlist_lock, flags);
+    return USB_ST_INTERNALERROR;
+}
+/*
  * Isochronous thread operations
  */
 
@@ -653,7 +736,8 @@
  * information, that's just ridiculously high. Most
  * control messages have just a few bytes of data.
  */
-static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void *cmd, void *data, int len)
+static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe,
+			    devrequest *cmd, void *data, int len)
 {
 	struct uhci_device *dev = usb_to_uhci(usb_dev);
 	struct uhci_td *first, *td, *prevtd;
@@ -756,8 +840,8 @@
 		} while (1);
 	}
 
-	if (ret) {
-		__u8 *p = cmd;
+	if (uhci_debug && ret) {
+		__u8 *p = (__u8 *) cmd;
 
 		printk("Failed cmd - %02X %02X %02X %02X %02X %02X %02X %02X\n",
 		       p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
@@ -869,6 +953,10 @@
 	int ret;
 	int maxsze = usb_maxpacket(usb_dev, pipe);
 
+	if (usb_endpoint_halted(usb_dev, usb_pipeendpoint(pipe)) &&
+	    usb_clear_halt(usb_dev, usb_pipeendpoint(pipe) | (pipe & 0x80)))
+		return USB_ST_STALL;
+
 	if (len > maxsze * 31)
 		printk("Warning, too much data for a bulk packet, crashing (%d/%d)\n", len, maxsze);
 
@@ -879,10 +967,10 @@
 	  I FORGOT WHAT IT EXACTLY DOES
 	*/
 	if (usb_pipeout(pipe)) {
-		destination = (pipe & 0x000Aff00) | 0xE1;
+		destination = (pipe & 0x0007ff00) | 0xE1;
 	}
 	else {
-		destination = (pipe & 0x000Aff00) | 0x69;
+		destination = (pipe & 0x0007ff00) | 0x69;
 	}
 
 	/* Status:    slow/fast,       Active,    Short Packet Detect     Three Errors */
@@ -896,6 +984,7 @@
 	while (len > 0) {
 		/* Build the TD for control status */
 		int pktsze = len;
+
 		if (pktsze > maxsze)
 			pktsze = maxsze;
 
@@ -918,10 +1007,8 @@
 		/* Alternate Data0/1 (start with Data0) */
 		usb_dotoggle(usb_dev, usb_pipeendpoint(pipe));
 	}
-	prevtd->link = 1;				/* Terminate */
-	prevtd->status = status | (1 << 24);		/* IOC */
-	prevtd->first = first;
-	uhci_td_deallocate(td);
+	td->link = 1;				/* Terminate */
+	td->status |= (1 << 24);		/* IOC */
 
 	/* CHANGE DIRECTION HERE! SAVE IT SOMEWHERE IN THE ENDPOINT!!! */
 
@@ -1007,7 +1094,7 @@
 	for (i = 0; i < UHCI_MAXTD; ++i) {
 		struct uhci_td *td = dev->td + i;
 
-		if (td->inuse) {
+		if (td->inuse & 1) {
 			uhci_remove_td(td);
 
 			/* And remove it from the irq list, if it's active */
@@ -1023,7 +1110,7 @@
 	for (i = 0; i < UHCI_MAXQH; ++i) {
 		struct uhci_qh *qh = dev->qh + i;
 
-		if (qh->inuse)
+		if (qh->inuse & 1)
 			uhci_remove_qh(qh->skel, qh);
 	}
 
@@ -1040,6 +1127,7 @@
 	uhci_control_msg,
 	uhci_bulk_msg,
 	uhci_request_irq,
+	uhci_remove_irq,
 };
 
 /*
@@ -1168,19 +1256,29 @@
 			/* remove from IRQ list */
 			__list_del(tmp->prev, next);
 			INIT_LIST_HEAD(tmp);
-			if (td->completed(td->status, bus_to_virt(td->buffer), td->dev_id)) {
+			if (td->completed(uhci_map_status((td->status & 0xff)>> 16, 0),
+					  bus_to_virt(td->buffer), td->dev_id)) {
 				list_add(&td->irq_list, &uhci->interrupt_list);
 
 				if (!(td->status & (1 << 25))) {
 					struct uhci_qh *interrupt_qh = td->qh;
 
-					td->info ^= 1 << 19; /* toggle between data0 and data1 */
+					usb_dotoggle(td->dev, usb_pipeendpoint(td->info));
+					td->info |= 1 << 19; /* toggle between data0 and data1 */
 					td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24);	/* active */
 
 					/* Remove then readd? Is that necessary */
 					uhci_remove_td(td);
 					uhci_insert_td_in_qh(interrupt_qh, td);
 				}
+			} else if (td->inuse & 2) {
+				struct uhci_qh *interrupt_qh = td->qh;
+				/* marked for removal */
+				td->inuse &= ~2;
+				usb_dotoggle(td->dev, usb_pipeendpoint(td->info));
+				uhci_remove_qh(interrupt_qh->skel, interrupt_qh);
+				uhci_qh_deallocate(interrupt_qh);
+				uhci_td_deallocate(td);
 			}
 			/* If completed wants to not reactivate, then it's */
 			/* responsible for free'ing the TD's and QH's */
@@ -1475,7 +1573,7 @@
 	printk("uhci_control_thread at %p\n", &uhci_control_thread);
 	exit_mm(current);
 	exit_files(current);
-	exit_fs(current);
+	//exit_fs(current);
 
 	strcpy(current->comm, "uhci-control");
 
@@ -1507,6 +1605,9 @@
 			if(signr == SIGUSR1) {
 				printk("UHCI queue dump:\n");
 				show_queues(uhci);
+			} else if (signr == SIGUSR2) {
+				printk("UHCI debug toggle\n");
+				uhci_debug = !uhci_debug;
 			} else {
 				break;
 			}
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/uhci.h linux/drivers/usb/uhci.h
--- v2.3.5/linux/drivers/usb/uhci.h	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/uhci.h	Tue Jun  8 23:04:22 1999
@@ -89,9 +89,10 @@
 	usb_device_irq completed;	/* Completion handler routine */
 	unsigned int *backptr;		/* Where to remove this from.. */
 	void *dev_id;
-	int inuse;			/* Inuse? */
+	int inuse;			/* Inuse? (b0) Remove (b1)*/
 	struct uhci_qh *qh;
 	struct uhci_td *first;
+	struct usb_device *dev;		/* the owning device */
 } __attribute__((aligned(32)));
 
 struct uhci_iso_td {
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/usb-core.c linux/drivers/usb/usb-core.c
--- v2.3.5/linux/drivers/usb/usb-core.c	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/usb-core.c	Mon Jun  7 20:04:01 1999
@@ -61,6 +61,9 @@
 #	ifdef CONFIG_USB_HUB
 		usb_hub_init();
 #	endif
+#	ifdef CONFIG_USB_SCSI
+		usb_scsi_init();
+#	endif
 #endif
 	return 0;
 }
@@ -84,7 +87,7 @@
 {
 	return usb_init();
 }
-void module_cleanup(void)
+void cleanup_module(void)
 {
 	cleanup_drivers();
 }
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/usb-debug.c linux/drivers/usb/usb-debug.c
--- v2.3.5/linux/drivers/usb/usb-debug.c	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/usb-debug.c	Fri Jun  4 13:33:43 1999
@@ -24,11 +24,18 @@
 
 static void usb_show_config(struct usb_config_descriptor *config)
 {
-	int i;
+  int i, j;
+  struct usb_alternate_setting *as;
 
-	usb_show_config_descriptor(config);
-	for (i = 0 ; i < config->bNumInterfaces; i++)
-		usb_show_interface(config->interface + i);
+  usb_show_config_descriptor(config);
+  for (i = 0; i < config->num_altsetting; i++) {
+    as = config->altsetting + i;
+    if ((as) == NULL)
+      break;
+    printk("\n  Alternate Setting: %d\n", i);
+    for (j = 0 ; j < config->bNumInterfaces; j++)
+      usb_show_interface(as->interface + j);
+  }
 }
 
 void usb_show_device(struct usb_device *dev)
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/usb.c linux/drivers/usb/usb.c
--- v2.3.5/linux/drivers/usb/usb.c	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/usb.c	Tue Jun  8 10:52:26 1999
@@ -123,7 +123,7 @@
 {
       /* Add it to the list of buses */
       list_add(&new_bus->bus_list, &usb_bus_list);
-      printk("New bus registered");
+      printk("New bus registered\n");
 }
 
 void usb_deregister_bus(struct usb_bus *bus)
@@ -148,11 +148,11 @@
               return;
       }
       for (i=0;i<USB_MAXCHILDREN;i++)
-              if ((dev->children[i]!=NULL)&&
-                      (dev->children[i]->driver==NULL))
+              if (dev->children[i]!=NULL)
                       usb_check_support(dev->children[i]);
       /*now we check this device*/
-      usb_device_descriptor(dev);
+      if (dev->driver==NULL)
+	      usb_device_descriptor(dev);
 }
 /*
  * This entrypoint gets called for each new device.
@@ -197,8 +197,8 @@
 
 		if (len < descindex)
 			return -1;
-		n_desc = *(unsigned short *)ptr;
-		n_len = n_desc & 0xff;
+		n_desc = le16_to_cpup((unsigned short *)ptr);
+		n_len = ptr[0];
 
 		if (n_desc == ((desctype << 8) + descindex))
 			break;
@@ -245,7 +245,10 @@
 
 	if (n_len < 2 || n_len > len)
 	{
-		printk("Short descriptor. (%d, %d)\n", len, n_len);
+		int i;
+		printk("Short descriptor. (%d, %d):\n", len, n_len);
+		for (i = 0; i < len; ++i)
+			printk(" %d: %x\n", i, ptr[i]);
 		return -1;
 	}
 
@@ -264,9 +267,10 @@
 	if (parsed < 0)
 		return parsed;
 	memcpy(endpoint, ptr + parsed, ptr[parsed]);
+	le16_to_cpus(&endpoint->wMaxPacketSize);
 
 	parsed += ptr[parsed];
-	len -= ptr[parsed];
+	len -= parsed;
 
 	while((i = usb_check_descriptor(ptr+parsed, len, 0x25))>=0)
 	{
@@ -328,7 +332,9 @@
 
 static int usb_parse_config(struct usb_device *dev, struct usb_config_descriptor *config, unsigned char *ptr, int len)
 {
-	int i;
+	int i, j;
+	int retval;
+	struct usb_alternate_setting *as;
 	int parsed = usb_expect_descriptor(ptr, len, USB_DT_CONFIG, 9);
 
 	if (parsed < 0)
@@ -337,6 +343,7 @@
 	memcpy(config, ptr + parsed, *ptr);
 	len -= *ptr;
 	parsed += *ptr;
+	le16_to_cpus(&config->wTotalLength);
 
 	if (config->MaxPower == 200) {
 		printk("bNumInterfaces kludge\n");
@@ -350,23 +357,59 @@
 
 	}
 
-	config->interface = (struct usb_interface_descriptor *)
+	config->altsetting = (struct usb_alternate_setting *)
+	        kmalloc(USB_MAXALTSETTING * sizeof(struct usb_alternate_setting), GFP_KERNEL);
+	if (config->altsetting == NULL) {
+	  printk(KERN_WARNING "usb: out of memory.\n");
+	  return -1;
+	}
+	config->act_altsetting = 0;
+	config->num_altsetting = 1;
+
+	config->altsetting->interface = (struct usb_interface_descriptor *)
 		kmalloc(config->bNumInterfaces * sizeof(struct usb_interface_descriptor), GFP_KERNEL);
-	if(config->interface==NULL)
+	if(config->altsetting->interface==NULL)
 	{
 		printk(KERN_WARNING "usb: out of memory.\n");
 		return -1;	
 	}
-	memset(config->interface, 0, config->bNumInterfaces*sizeof(struct usb_interface_descriptor));
+	memset(config->altsetting->interface, 
+	       0, config->bNumInterfaces*sizeof(struct usb_interface_descriptor));
 	
 	for (i = 0; i < config->bNumInterfaces; i++) {
-		int retval = usb_parse_interface(dev, config->interface + i, ptr + parsed, len);
+		retval = usb_parse_interface(dev, config->altsetting->interface + i, ptr + parsed, len);
 		if (retval < 0)
 			return parsed; // HACK
 //			return retval;
 		parsed += retval;
 		len -= retval;
 	}
+
+	printk("parsed = %d len = %d\n", parsed, len);
+
+	// now parse for additional alternate settings
+	for (j = 1; j < USB_MAXALTSETTING; j++) {
+	  retval = usb_expect_descriptor(ptr + parsed, len, USB_DT_INTERFACE, 9);
+	  if (retval) 
+	    break;
+	  config->num_altsetting++;
+	  as = config->altsetting + j;
+	  as->interface = (struct usb_interface_descriptor *)
+	    kmalloc(config->bNumInterfaces * sizeof(struct usb_interface_descriptor), GFP_KERNEL);
+	  if (as->interface == NULL) {
+	    printk(KERN_WARNING "usb: out of memory.\n");
+	    return -1;
+	  }
+	  memset(as->interface, 0, config->bNumInterfaces * sizeof(struct usb_interface_descriptor));
+	  for (i = 0; i < config->bNumInterfaces; i++) {
+	    retval = usb_parse_interface(dev, as->interface + i, 
+					 ptr + parsed, len);
+	    if (retval < 0)
+	      return parsed;
+	    parsed += retval;
+	    len -= retval;
+	  }
+	}
 	return parsed;
 }
 
@@ -403,26 +446,34 @@
 
 void usb_destroy_configuration(struct usb_device *dev)
 {
-	int c, i;
+	int c, a, i;
 	struct usb_config_descriptor *cf;
+	struct usb_alternate_setting *as;
 	struct usb_interface_descriptor *ifp;
 	
 	if(dev->config==NULL)
 		return;
 
-	for(c=0;c<dev->descriptor.bNumConfigurations;c++)
+	for(c = 0; c < dev->descriptor.bNumConfigurations; c++)
 	{
-		cf=&dev->config[c];
-		if(cf->interface==NULL)
-			break;
-		for(i=0;i<cf->bNumInterfaces;i++)
+		cf = &dev->config[c];
+		if (cf->altsetting == NULL)
+		        break;
+		for (a = 0; a < cf->num_altsetting; a++)
 		{
-			ifp=&cf->interface[i];
-			if(ifp->endpoint==NULL)
-				break;
-			kfree(ifp->endpoint);
+		        as = &cf->altsetting[a];
+			if (as->interface == NULL)
+			        break;
+			for(i=0;i<cf->bNumInterfaces;i++)
+			{
+			        ifp = &as->interface[i];
+				if(ifp->endpoint==NULL)
+				       break;
+				kfree(ifp->endpoint);
+			}
+			kfree(as->interface);
 		}
-		kfree(cf->interface);
+		kfree(cf->altsetting);
 	}
 	kfree(dev->config);
 
@@ -526,8 +577,6 @@
 int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)
 {
 	devrequest dr;
-	int i = 5;
-	int result;
 
 	dr.requesttype = 0x80;
 	dr.request = USB_REQ_GET_DESCRIPTOR;
@@ -535,16 +584,16 @@
 	dr.index = langid;
 	dr.length = size;
 
-	while (i--) {
-		if (!(result = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size)))
-			break;
-	}
-	return result;
+	return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size);
 }
 
 int usb_get_device_descriptor(struct usb_device *dev)
 {
 	return usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, sizeof(dev->descriptor));
+	le16_to_cpus(&dev->descriptor.bcdUSB);
+	le16_to_cpus(&dev->descriptor.idVendor);
+	le16_to_cpus(&dev->descriptor.idProduct);
+	le16_to_cpus(&dev->descriptor.bcdDevice);
 }
 
 int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size)
@@ -665,13 +714,15 @@
 
 static void usb_set_maxpacket(struct usb_device *dev)
 {
-	struct usb_endpoint_descriptor *ep;
-	struct usb_interface_descriptor *ip = dev->actconfig->interface;
 	int i;
+	struct usb_endpoint_descriptor *ep;
+	int act_as = dev->actconfig->act_altsetting;
+	struct usb_alternate_setting *as = dev->actconfig->altsetting + act_as;
+	struct usb_interface_descriptor *ip = as->interface;
 
 	for (i=0; i<dev->actconfig->bNumInterfaces; i++) {
-		if (dev->actconfig->interface[i].bInterfaceNumber == dev->ifnum) {
-			ip = &dev->actconfig->interface[i];
+		if (as->interface[i].bInterfaceNumber == dev->ifnum) {
+			ip = &as->interface[i];
 			break;
 		}
 	}
@@ -681,6 +732,52 @@
 	}
 }
 
+int usb_clear_halt(struct usb_device *dev, int endp)
+{
+	devrequest dr;
+	int result;
+	__u16 status;
+
+    	//if (!usb_endpoint_halted(dev, endp))
+	//	return 0;
+
+	dr.requesttype = USB_RT_ENDPOINT;
+	dr.request = USB_REQ_CLEAR_FEATURE;
+	dr.value = 0;
+	dr.index = endp;
+	dr.length = 0;
+
+	result = dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+
+	/* dont clear if failed */
+	if (result) {
+	    return result;
+	}
+
+#if 1	/* lets be really tough */
+	dr.requesttype = 0x80 | USB_RT_ENDPOINT;
+	dr.request = USB_REQ_GET_STATUS;
+	dr.length = 2;
+	status = 0xffff;
+
+	result = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, &status, 2);
+
+	if (result) {
+	    return result;
+	}
+	if (status & 1) {
+	    return 1;		/* still halted */
+	}
+#endif
+	usb_endpoint_running(dev, endp & 0x0f);
+
+	/* toggle is reset on clear */
+
+	usb_settoggle(dev, endp & 0x0f, 0);
+
+	return 0;
+}
+
 int usb_set_interface(struct usb_device *dev, int interface, int alternate)
 {
 	devrequest dr;
@@ -695,11 +792,11 @@
 		return -1;
 
 	dev->ifnum = interface;
+	dev->actconfig->act_altsetting = alternate;
 	usb_set_maxpacket(dev);
 	return 0;
 }
 
-
 int usb_set_configuration(struct usb_device *dev, int configuration)
 {
 	devrequest dr;
@@ -762,7 +859,7 @@
 	    		return -1;
 
   	  	/* Get the full buffer */
-	  	size = *(unsigned short *)(bufptr+2);
+	  	size = le16_to_cpup((unsigned short *)(bufptr+2));
 	  	if (bufptr+size > buffer+sizeof(buffer)) {
 			printk(KERN_INFO "usb: truncated DT_CONFIG (want %d).\n", size);
 			size = buffer+sizeof(buffer)-bufptr;
@@ -793,7 +890,7 @@
 	   usb_get_string(dev, 0, 0, buffer, sd->bLength))
 		return -1;
 	/* we are going to assume that the first ID is good */
-	langid = sd->wData[0];
+	langid = le16_to_cpup(&sd->wData[0]);
 
 	/* whip through and find total length and max index */
 	for (maxindex = 1, totalchars = 0; maxindex<=USB_MAXSTRINGS; maxindex++) {
@@ -823,7 +920,7 @@
 			continue;
 		dev->stringindex[i] = string;
 		for (j=0; j < (bLengths[i] - 2)/2; j++) {
-			*string++ =  sd->wData[j];
+			*string++ = le16_to_cpup(&sd->wData[j]);
 		}
 		*string++ = '\0';
 	}
@@ -932,4 +1029,3 @@
 {
 	return dev->bus->op->request_irq(dev, pipe, handler, period, dev_id);
 }
-
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/usb.h linux/drivers/usb/usb.h
--- v2.3.5/linux/drivers/usb/usb.h	Mon May 31 22:28:06 1999
+++ linux/drivers/usb/usb.h	Tue Jun  8 23:04:17 1999
@@ -96,6 +96,30 @@
 
 #define USB_RT_HIDD			(USB_TYPE_CLASS | USB_RECIP_INTERFACE)
 
+/* 
+ * Status codes 
+ */
+#define USB_ST_NOERROR		0x0
+#define USB_ST_CRC		0x1
+#define USB_ST_BITSTUFF		0x2
+#define USB_ST_DTMISMATCH	0x3
+#define USB_ST_STALL		0x4
+#define USB_ST_TIMEOUT		0x5
+#define USB_ST_PIDCHECK		0x6
+#define USB_ST_PIDUNDEF		0x7
+#define USB_ST_DATAOVERRUN	0x8
+#define USB_ST_DATAUNDERRUN	0x9
+#define USB_ST_RESERVED1	0xA
+#define USB_ST_RESERVED2	0xB
+#define USB_ST_BUFFEROVERRUN	0xC
+#define USB_ST_BUFFERUNDERRUN	0xD
+#define USB_ST_RESERVED3	0xE
+#define USB_ST_RESERVED4	0xF
+
+/* internal errors */
+#define USB_ST_REMOVED		0x100
+#define USB_ST_INTERNALERROR	-1
+
 /*
  * USB device number allocation bitmap. There's one bitmap
  * per USB tree.
@@ -112,6 +136,7 @@
  */
 
 #define USB_MAXCONFIG		8
+#define USB_MAXALTSETTING       5
 #define USB_MAXINTERFACES	32
 #define USB_MAXENDPOINTS	32
 #define USB_MAXSTRINGS		16
@@ -160,6 +185,11 @@
 	void  *audio;
 };
 
+/* hack for alternate settings */
+struct usb_alternate_setting {
+        struct usb_interface_descriptor *interface;
+};
+
 /* Configuration descriptor information.. */
 struct usb_config_descriptor {
 	__u8  bLength;
@@ -170,8 +200,9 @@
 	__u8  iConfiguration;
 	__u8  bmAttributes;
 	__u8  MaxPower;
-
-	struct usb_interface_descriptor *interface;
+        int act_altsetting;                /* active alternate setting */
+        int num_altsetting;                /* number of alternate settings */
+	struct usb_alternate_setting *altsetting;
 };
 
 /* String descriptor */
@@ -186,7 +217,7 @@
 	__u8  bLength;
 	__u8  bDescriptorType;
 	__u8  bNbrPorts;
-	__u16 wHubCharacteristics;
+	__u8  wHubCharacteristics[2];	/* __u16 but not aligned! */
 	__u8  bPwrOn2PwrGood;
 	__u8  bHubContrCurrent;
 	/* DeviceRemovable and PortPwrCtrlMask want to be variable-length 
@@ -219,9 +250,10 @@
 struct usb_operations {
 	struct usb_device *(*allocate)(struct usb_device *);
 	int (*deallocate)(struct usb_device *);
-	int (*control_msg)(struct usb_device *, unsigned int, void *, void *, int);
+	int (*control_msg)(struct usb_device *, unsigned int, devrequest *, void *, int);
 	int (*bulk_msg)(struct usb_device *, unsigned int, void *, int,unsigned long *);
 	int (*request_irq)(struct usb_device *, unsigned int, usb_device_irq, int, void *);
+	int (*remove_irq)(struct usb_device *, unsigned int, usb_device_irq, int, void *);
 };
 
 /*
@@ -239,22 +271,23 @@
 #define USB_MAXCHILDREN (8)
 
 struct usb_device {
-	int devnum;						/* Device number on USB bus */
-	int slow;						/* Slow device? */
-	int maxpacketsize;					/* Maximum packet size */
-	__u16 toggle;						/* one bit for each endpoint */
-	struct usb_config_descriptor *actconfig;		/* the active configuration */
-	int epmaxpacket[16];					/* endpoint specific maximums */
-	int ifnum;						/* active interface number */
-	struct usb_bus *bus;					/* Bus we're apart of */
-	struct usb_driver *driver;				/* Driver */
-	struct usb_device_descriptor descriptor;		/* Descriptor */
-	struct usb_config_descriptor *config;			/* All of the configs */
+	int devnum;			/* Device number on USB bus */
+	int slow;			/* Slow device? */
+	int maxpacketsize;		/* Maximum packet size */
+	int toggle;			/* one bit for each endpoint */
+	int halted;			/* endpoint halts */
+	struct usb_config_descriptor *actconfig;/* the active configuration */
+	int epmaxpacket[16];		/* endpoint specific maximums */
+	int ifnum;			/* active interface number */
+	struct usb_bus *bus;		/* Bus we're apart of */
+	struct usb_driver *driver;	/* Driver */
+	struct usb_device_descriptor descriptor;/* Descriptor */
+	struct usb_config_descriptor *config;	/* All of the configs */
 	struct usb_device *parent;
-	char *stringtable;					/* Strings (multiple, null term) */
-	char **stringindex;					/* pointers to strings */
-	int maxstring;						/* max valid index */
-  
+	char *stringtable;		/* Strings (multiple, null term) */
+	char **stringindex;		/* pointers to strings */
+	int maxstring;			/* max valid index */
+
 	/*
 	 * Child devices - these can be either new devices
 	 * (if this is a hub device), or different instances
@@ -341,7 +374,12 @@
 /* The D0/D1 toggle bits */
 #define usb_gettoggle(dev, ep) (((dev)->toggle >> ep) & 1)
 #define	usb_dotoggle(dev, ep)	((dev)->toggle ^= (1 <<	ep))
-#define usb_settoggle(dev, ep, bit) ((dev)->toggle = ((dev)->toggle & (0xfffe << ep)) | (bit << ep))
+#define usb_settoggle(dev, ep, bit) ((dev)->toggle = ((dev)->toggle & ~(1 << ep)) | ((bit) << ep))
+
+/* Endpoint halt */
+#define usb_endpoint_halt(dev, ep) ((dev)->halted |= (1 << (ep)))
+#define usb_endpoint_running(dev, ep) ((dev)->halted &= ~(1 << (ep)))
+#define usb_endpoint_halted(dev, ep) ((dev)->halted & (1 << (ep)))
 
 static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)
 {
@@ -381,6 +419,14 @@
 int usb_set_idle(struct usb_device *dev, int duration, int report_id);
 int usb_set_configuration(struct usb_device *dev, int configuration);
 int usb_get_report(struct usb_device *dev);
+int usb_clear_halt(struct usb_device *dev, int endp);
+static inline char * usb_string(struct usb_device* dev, int index)
+{
+	if (index <= dev->maxstring && dev->stringindex && dev->stringindex[index])
+		return dev->stringindex[index];
+	else
+		return NULL;
+}
 
 /*
  * Debugging helpers..
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/usb_scsi.c linux/drivers/usb/usb_scsi.c
--- v2.3.5/linux/drivers/usb/usb_scsi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/usb_scsi.c	Mon Jun  7 20:04:01 1999
@@ -0,0 +1,1098 @@
+
+/* Driver for USB scsi like devices
+ * 
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ * This driver is scitzoid  - it makes a USB device appear as both a SCSI device
+ * and a character device. The latter is only available if the device has an
+ * interrupt endpoint, and is used specifically to receive interrupt events.
+ *
+ * In order to support various 'strange' devices, this module supports plug in
+ * device specific filter modules, which can do their own thing when required.
+ *
+ * Further reference.
+ *	This driver is based on the 'USB Mass Storage Class' document. This
+ *	describes in detail the transformation of SCSI command blocks to the
+ *	equivalent USB control and data transfer required.
+ *	It is important to note that in a number of cases this class exhibits
+ *	class-specific exemptions from the USB specification. Notably the
+ *	usage of NAK, STALL and ACK differs from the norm, in that they are
+ *	used to communicate wait, failed and OK on SCSI commands.
+ *	Also, for certain devices, the interrupt endpoint is used to convey
+ *	status of a command.
+ *
+ *	Basically, this stuff is WIERD!!
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+
+#include <asm/spinlock.h>
+#include <linux/smp_lock.h>
+
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+
+#include "usb.h"
+#include "usb_scsi.h"
+
+/* direction table (what a pain) */
+
+unsigned char us_direction[256/8] = {
+
+#include "usb_scsi_dt.c"
+
+};
+
+/*
+ * Per device data
+ */
+
+static int my_host_number;
+
+int usbscsi_debug = 1;
+
+struct us_data {
+	struct us_data	*next;			/* next device */
+	struct usb_device 	*pusb_dev;
+	struct usb_scsi_filter  *filter;		/* filter driver */
+	void 			*fdata;			/* filter data */
+	unsigned int		flags;			/* from filter initially*/
+	__u8			ep_in;			/* in endpoint */
+	__u8			ep_out;			/* out ....... */
+	__u8			ep_int;			/* interrupt . */
+	__u8			subclass;		/* as in overview */
+	__u8			protocol;		/* .............. */
+	int (*pop)(Scsi_Cmnd *);			/* protocol specific do cmd */
+	GUID(guid);					/* unique dev id */
+	struct Scsi_Host	*host;			/* our dummy host data */
+	Scsi_Host_Template	*htmplt;		/* own host template */
+	int			host_number;		/* to find us */
+	int			host_no;		/* allocated by scsi */
+	int			fixedlength;		/* expand commands */
+	Scsi_Cmnd		*srb;			/* current srb */
+	int			action;			/* what to do */
+	wait_queue_head_t	waitq;			/* thread waits */
+	wait_queue_head_t	ip_waitq;		/* for CBI interrupts */
+	__u16			ip_data;		/* interrupt data */
+	int			ip_wanted;		/* needed */
+	int			pid;			/* control thread */
+	struct semaphore      	*notify; 		/* wait for thread to begin */
+};
+
+/*
+ * kernel thread actions
+ */
+
+#define US_ACT_COMMAND		1
+#define US_ACT_ABORT		2
+#define US_ACT_DEVICE_RESET	3
+#define US_ACT_BUS_RESET	4
+#define US_ACT_HOST_RESET	5
+
+static struct proc_dir_entry proc_usb_scsi =
+{
+    PROC_SCSI_USB_SCSI,
+    0,
+    NULL,
+    S_IFDIR | S_IRUGO | S_IXUGO,
+    2
+};
+
+static struct us_data *us_list;
+
+static struct usb_scsi_filter *filters;
+
+static int scsi_probe(struct usb_device *dev);
+static void scsi_disconnect(struct usb_device *dev);
+static struct usb_driver scsi_driver = {
+	"usb_scsi",
+	scsi_probe,
+	scsi_disconnect,
+	{ NULL, NULL }
+};
+
+/* Data handling, using SG if required */
+
+static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length)
+{
+    int max_size = usb_maxpacket(us->pusb_dev, pipe) * 16;
+    int this_xfer;
+    int result;
+    unsigned long partial;
+    int maxtry = 100;
+    while (length) {
+	this_xfer = length > max_size ? max_size : length;
+	length -= this_xfer;
+	do {
+	    US_DEBUGP("Bulk xfer %x(%d)\n", (unsigned int)buf, this_xfer);
+	    result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev, pipe, buf, 
+						    this_xfer, &partial);
+
+	    /* we want to retry if the device reported NAK */
+	    if (result == USB_ST_TIMEOUT) {
+		if (!maxtry--)
+		    break;
+		this_xfer -= partial;
+		buf += partial;
+	    } else if (!result && partial != this_xfer) {
+		/* short data - assume end */
+		result = USB_ST_DATAUNDERRUN;
+		break;
+	    } else
+		break;
+	} while ( this_xfer );
+	if (result)
+	    return result;
+	buf += this_xfer;
+    }
+    return 0;
+
+}
+static int us_transfer(Scsi_Cmnd *srb, int dir_in)
+{
+    struct us_data *us = (struct us_data *)srb->host_scribble;
+    int i;
+    int result = -1;
+
+    if (srb->use_sg) {
+	struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+
+	for (i = 0; i < srb->use_sg; i++) {
+	    result = us_one_transfer(us, dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) :
+						  usb_sndbulkpipe(us->pusb_dev, us->ep_out),
+				     sg[i].address, sg[i].length);
+	    if (result)
+		break;
+	}
+	return result;
+    }
+    else
+	return us_one_transfer(us, dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) :
+					    usb_sndbulkpipe(us->pusb_dev, us->ep_out),
+			       srb->request_buffer, srb->request_bufflen);
+}
+
+static unsigned int us_transfer_length(Scsi_Cmnd *srb)
+{
+    int i;
+    unsigned int total = 0;
+
+    /* always zero for some commands */
+    switch (srb->cmnd[0]) {
+    case SEEK_6:
+    case SEEK_10:
+    case REZERO_UNIT:
+    case ALLOW_MEDIUM_REMOVAL:
+    case START_STOP:
+    case TEST_UNIT_READY:
+	return 0;
+
+    default:
+	break;
+    }
+
+    if (srb->use_sg) {
+	struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+
+	for (i = 0; i < srb->use_sg; i++) {
+	    total += sg[i].length;
+	}
+	return total;
+    }
+    else
+	return srb->request_bufflen;
+
+}
+
+static int pop_CBI_irq(int state, void *buffer, void *dev_id)
+{
+    struct us_data *us = (struct us_data *)dev_id;
+
+    if (state != USB_ST_REMOVED) {
+	us->ip_data = *(__u16 *)buffer;
+	us->ip_wanted = 0;
+    }
+    wake_up(&us->ip_waitq);
+
+    /* we dont want another interrupt */
+
+    return 0;
+}
+static int pop_CB_command(Scsi_Cmnd *srb)
+{
+    struct us_data *us = (struct us_data *)srb->host_scribble;
+    devrequest dr;
+    unsigned char cmd[16];
+    int result;
+    int retry = 1;
+    int done_start = 0;
+
+    while (retry--) {
+	dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE;
+	dr.request = US_CBI_ADSC;
+	dr.value = 0;
+	dr.index = us->pusb_dev->ifnum;
+	dr.length = srb->cmd_len;
+    
+	if (us->flags & US_FL_FIXED_COMMAND) {
+	    dr.length = us->fixedlength;
+	    memset(cmd, 0, us->fixedlength);
+
+	    /* fix some commands */
+
+	    switch (srb->cmnd[0]) {
+	    case WRITE_6:
+	    case READ_6:
+		cmd[0] = srb->cmnd[0] | 0x20;
+		cmd[1] = srb->cmnd[1] & 0xE0;
+		cmd[2] = 0;
+		cmd[3] = srb->cmnd[1] & 0x1F;
+		cmd[4] = srb->cmnd[2];
+		cmd[5] = srb->cmnd[3];
+		cmd[8] = srb->cmnd[4];
+		break;
+
+	    case MODE_SENSE:
+	    case MODE_SELECT:
+		cmd[0] = srb->cmnd[0] | 0x40;
+		cmd[1] = srb->cmnd[1];
+		cmd[2] = srb->cmnd[2];
+		cmd[8] = srb->cmnd[4];
+		break;
+
+	    default:
+		memcpy(cmd, srb->cmnd, srb->cmd_len);
+		break;
+	    }
+	    result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, 
+						  usb_sndctrlpipe(us->pusb_dev,0),
+						  &dr, cmd, us->fixedlength);
+	    if (!done_start && us->subclass == US_SC_UFI && cmd[0] == TEST_UNIT_READY && result) {
+		/* as per spec try a start command, wait and retry */
+
+		done_start++;
+		cmd[0] = START_STOP;
+		cmd[4] = 1;		/* start */
+		result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, 
+						      usb_sndctrlpipe(us->pusb_dev,0),
+						      &dr, cmd, us->fixedlength);
+		wait_ms(100);
+		retry++;
+		continue;
+	    }
+	} else
+	    result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, 
+						  usb_sndctrlpipe(us->pusb_dev,0),
+						  &dr, srb->cmnd, srb->cmd_len);
+	if (result != USB_ST_STALL && result != USB_ST_TIMEOUT)
+	    return result;
+    }
+    return result;
+}
+
+/* Protocol command handlers */
+
+static int pop_CBI(Scsi_Cmnd *srb)
+{
+    struct us_data *us = (struct us_data *)srb->host_scribble;
+    int result;
+
+    /* run the command */
+
+    if ((result = pop_CB_command(srb))) {
+	US_DEBUGP("CBI command %x\n", result);
+	if (result == USB_ST_STALL || result == USB_ST_TIMEOUT)
+	    return (DID_OK << 16) | 2;
+	return DID_ABORT << 16;
+    }
+
+    /* transfer the data */
+
+    if (us_transfer_length(srb)) {
+	result = us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+	if (result && result != USB_ST_DATAUNDERRUN) {
+	    US_DEBUGP("CBI  transfer %x\n", result);
+	    return DID_ABORT << 16;
+	}
+    }
+
+    /* get status */
+
+    if (us->protocol == US_PR_CBI) {
+	/* get from interrupt pipe */
+
+	/* add interrupt transfer, marked for removal */
+	us->ip_wanted = 1;
+	result = us->pusb_dev->bus->op->request_irq(us->pusb_dev, 
+						    usb_rcvctrlpipe(us->pusb_dev, us->ep_int),
+						     pop_CBI_irq, 0, (void *)us);
+	if (result) {
+	    US_DEBUGP("No interrupt for CBI %x\n", result);
+	    return DID_ABORT << 16;
+	}
+	sleep_on(&us->ip_waitq);
+	if (us->ip_wanted) {
+	    US_DEBUGP("Did not get interrupt on CBI\n");
+	    us->ip_wanted = 0;
+	    return DID_ABORT << 16;
+	}
+
+	US_DEBUGP("Got interrupt data %x\n", us->ip_data);
+
+	/* sort out what it means */
+
+	if (us->subclass == US_SC_UFI) {
+	    /* gives us asc and ascq, as per request sense */
+
+	    if (srb->cmnd[0] == REQUEST_SENSE ||
+		srb->cmnd[0] == INQUIRY)
+		return DID_OK << 16;
+	    else
+		return (DID_OK << 16) + ((us->ip_data & 0xff) ? 2 : 0);
+	}
+	if (us->ip_data & 0xff) {
+	    US_DEBUGP("Bad CBI interrupt data %x\n", us->ip_data);
+	    return DID_ABORT << 16;
+	}
+	return (DID_OK << 16) + ((us->ip_data & 0x300) ? 2 : 0);
+    } else {
+       /* get from where? */
+    }
+    return DID_ERROR << 16;
+}
+
+static int pop_Bulk_reset(struct us_data *us)
+{
+    devrequest dr;
+    int result;
+
+    dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE;
+    dr.request = US_BULK_RESET;
+    dr.value = US_BULK_RESET_SOFT;
+    dr.index = 0;
+    dr.length = 0;
+
+    US_DEBUGP("Bulk soft reset\n");
+    result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0);
+    if (result) {
+	US_DEBUGP("Bulk soft reset failed %d\n", result);
+	dr.value = US_BULK_RESET_HARD;
+	result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0);
+	if (result)
+	    US_DEBUGP("Bulk hard reset failed %d\n", result);
+    }
+    usb_clear_halt(us->pusb_dev, us->ep_in | 0x80);
+    usb_clear_halt(us->pusb_dev, us->ep_out);
+    return result;
+}
+/*
+ * The bulk only protocol handler.
+ * 	Uses the in and out endpoints to transfer commands and data (nasty)
+ */
+static int pop_Bulk(Scsi_Cmnd *srb)
+{
+    struct us_data *us = (struct us_data *)srb->host_scribble;
+    struct bulk_cb_wrap bcb;
+    struct bulk_cs_wrap bcs;
+    int result;
+    unsigned long partial;
+    int stall;
+
+    /* set up the command wrapper */
+
+    bcb.Signature = US_BULK_CB_SIGN;
+    bcb.DataTransferLength = us_transfer_length(srb);;
+    bcb.Flags = US_DIRECTION(srb->cmnd[0]) << 7;
+    bcb.Tag = srb->serial_number;
+    bcb.Lun = 0;
+    memset(bcb.CDB, 0, sizeof(bcb.CDB));
+    memcpy(bcb.CDB, srb->cmnd, srb->cmd_len);
+    if (us->flags & US_FL_FIXED_COMMAND) {
+	bcb.Length = us->fixedlength;
+    } else {
+	bcb.Length = srb->cmd_len;
+    }
+
+    /* send it to out endpoint */
+
+    US_DEBUGP("Bulk command S %x T %x L %d F %d CL %d\n", bcb.Signature,
+	      bcb.Tag, bcb.DataTransferLength, bcb.Flags, bcb.Length);
+    result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev,
+		     usb_sndbulkpipe(us->pusb_dev, us->ep_out), &bcb, 
+					    US_BULK_CB_WRAP_LEN, &partial);
+    if (result) {
+	US_DEBUGP("Bulk command result %x\n", result);
+	return DID_ABORT << 16;
+    }
+
+    //return DID_BAD_TARGET << 16;
+    /* send/receive data */
+
+    if (bcb.DataTransferLength) {
+	result = us_transfer(srb, bcb.Flags);
+	if (result && result != USB_ST_DATAUNDERRUN && result != USB_ST_STALL) {
+	    US_DEBUGP("Bulk transfer result %x\n", result);
+	    return DID_ABORT << 16;
+	}
+    }
+
+    /* get status */
+
+
+    stall = 0;
+    do {
+	//usb_settoggle(us->pusb_dev, us->ep_in, 0);	/* AAARgh!! */
+	US_DEBUGP("Toggle is %d\n", usb_gettoggle(us->pusb_dev, us->ep_in));
+	result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev,
+			 usb_rcvbulkpipe(us->pusb_dev, us->ep_in), &bcs, 
+						US_BULK_CS_WRAP_LEN, &partial);
+	if (result == USB_ST_STALL || result == USB_ST_TIMEOUT)
+	    stall++;
+	else
+	    break;
+    } while ( stall < 3);
+    if (result && result != USB_ST_DATAUNDERRUN) {
+	US_DEBUGP("Bulk status result = %x\n", result);
+	return DID_ABORT << 16;
+    }
+
+    /* check bulk status */
+
+    US_DEBUGP("Bulk status S %x T %x R %d V %x\n", bcs.Signature, bcs.Tag,
+	      bcs.Residue, bcs.Status);
+    if (bcs.Signature != US_BULK_CS_SIGN || bcs.Tag != bcb.Tag ||
+	bcs.Status > US_BULK_STAT_PHASE) {
+	US_DEBUGP("Bulk logical error\n");
+	return DID_ABORT << 16;
+    }
+    switch (bcs.Status) {
+    case US_BULK_STAT_OK:
+	return DID_OK << 16;
+
+    case US_BULK_STAT_FAIL:
+	/* check for underrun - dont report */
+	if (bcs.Residue)
+	    return DID_OK << 16;
+	//pop_Bulk_reset(us);
+	break;
+
+    case US_BULK_STAT_PHASE:
+	return DID_ERROR << 16;
+    }
+    return (DID_OK << 16) | 2;		/* check sense required */
+
+}
+
+/* Host functions */
+
+/* detect adapter (always true ) */
+static int us_detect(struct SHT *sht)
+{
+    /* FIXME - not nice at all, but how else ? */
+    struct us_data *us = (struct us_data *)sht->proc_dir;
+    char name[32];
+
+    sprintf(name, "usbscsi%d", us->host_number);
+    proc_usb_scsi.namelen = strlen(name);
+    proc_usb_scsi.name = kmalloc(proc_usb_scsi.namelen+1, GFP_KERNEL);
+    if (!proc_usb_scsi.name)
+	return 0;
+    strcpy((char *)proc_usb_scsi.name, name);
+    sht->proc_dir = kmalloc(sizeof(*sht->proc_dir), GFP_KERNEL);
+    if (!sht->proc_dir) {
+	kfree(proc_usb_scsi.name);
+	return 0;
+    }
+    *sht->proc_dir = proc_usb_scsi;
+    sht->name = proc_usb_scsi.name;
+    us->host = scsi_register(sht, sizeof(us));
+    if (us->host) {
+	us->host->hostdata[0] = (unsigned long)us;
+	us->host_no = us->host->host_no;
+	return 1;
+    }
+    kfree(proc_usb_scsi.name);
+    kfree(sht->proc_dir);
+    return 0;
+}
+
+/* release - must be here to stop scsi
+ *	from trying to release IRQ etc.
+ *	Kill off our data
+ */
+static int us_release(struct Scsi_Host *psh)
+{
+    struct us_data *us = (struct us_data *)psh->hostdata[0];
+    struct us_data *prev = (struct us_data *)&us_list;
+
+    if (us->filter)
+	us->filter->release(us->fdata);
+    if (us->pusb_dev)
+	usb_deregister(&scsi_driver);
+
+    /* FIXME - leaves hanging host template copy */
+    /* (bacause scsi layer uses it after removal !!!) */
+    while(prev->next != us)
+	prev = prev->next;
+    prev->next = us->next;
+    return 0;
+}
+
+/* run command */
+static int us_command( Scsi_Cmnd *srb )
+{
+    US_DEBUGP("Bad use of us_command\n");
+
+    return DID_BAD_TARGET << 16;
+}
+
+/* run command */
+static int us_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
+{
+    struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+
+    US_DEBUGP("Command wakeup\n");
+    srb->host_scribble = (unsigned char *)us;
+    us->srb = srb;
+    srb->scsi_done = done;
+    us->action = US_ACT_COMMAND;
+
+    /* wake up the process task */
+
+    wake_up_interruptible(&us->waitq);
+
+    return 0;
+}
+
+static int us_abort( Scsi_Cmnd *srb )
+{
+    return 0;
+}
+
+static int us_device_reset( Scsi_Cmnd *srb )
+{
+    return 0;
+}
+
+static int us_host_reset( Scsi_Cmnd *srb )
+{
+    return 0;
+}
+
+static int us_bus_reset( Scsi_Cmnd *srb )
+{
+    return 0;
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); }
+
+int usb_scsi_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout)
+{
+    struct us_data *us = us_list;
+    char *pos = buffer;
+    char *vendor;
+    char *product;
+    char *style = "";
+
+    /* find our data from hostno */
+
+    while (us) {
+	if (us->host_no == hostno)
+	    break;
+	us = us->next;
+    }
+
+    if (!us)
+	return -ESRCH;
+
+    /* null on outward */
+
+    if (inout)
+	return length;
+
+    if (!(vendor = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iManufacturer)))
+	vendor = "?";
+    if (!(product = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iProduct)))
+	product = "?";
+
+    switch (us->protocol) {
+    case US_PR_CB:
+	style = "Control/Bulk";
+	break;
+
+    case US_PR_CBI:
+	style = "Control/Bulk/Interrupt";
+	break;
+
+    case US_PR_ZIP:
+	style = "Bulk only";
+	break;
+
+    }
+    SPRINTF ("Host scsi%d: usb-scsi\n", hostno);
+    SPRINTF ("Device: %s %s - GUID " GUID_FORMAT "\n", vendor, product, GUID_ARGS(us->guid) );
+    SPRINTF ("Style: %s\n", style);
+
+    /*
+     * Calculate start of next buffer, and return value.
+     */
+    *start = buffer + offset;
+
+    if ((pos - buffer) < offset)
+	return (0);
+    else if ((pos - buffer - offset) < length)
+	return (pos - buffer - offset);
+    else
+	return (length);
+}
+
+/*
+ * this defines our 'host'
+ */
+
+static Scsi_Host_Template my_host_template = {
+    NULL,			/* next */
+    NULL,			/* module */
+    NULL,			/* proc_dir */
+    usb_scsi_proc_info,
+    NULL,			/* name - points to unique */
+    us_detect,
+    us_release,
+    NULL,			/* info */
+    NULL,			/* ioctl */
+    us_command,
+    us_queuecommand,
+    NULL,			/* eh_strategy */
+    us_abort,
+    us_device_reset,
+    us_bus_reset,
+    us_host_reset,
+    NULL,			/* abort */
+    NULL,			/* reset */
+    NULL,			/* slave_attach */
+    NULL,			/* bios_param */
+    1,				/* can_queue */
+    -1,				/* this_id */
+    SG_ALL,			/* sg_tablesize */
+    1,				/* cmd_per_lun */
+    0,				/* present */
+    FALSE,			/* unchecked_isa_dma */
+    FALSE,			/* use_clustering */
+    TRUE,			/* use_new_eh_code */
+    TRUE			/* emulated */
+};
+
+static int usbscsi_control_thread(void * __us)
+{
+    struct us_data *us = (struct us_data *)__us;
+    int action;
+
+    lock_kernel();
+
+    /*
+     * This thread doesn't need any user-level access,
+     * so get rid of all our resources..
+     */
+    exit_mm(current);
+    exit_files(current);
+    //exit_fs(current);
+
+    sprintf(current->comm, "usbscsi%d", us->host_no);
+    
+    unlock_kernel();
+
+    up(us->notify);
+
+    for(;;) {
+	    siginfo_t info;
+	    int unsigned long signr;
+
+	    interruptible_sleep_on(&us->waitq);
+
+	    action = us->action;
+	    us->action = 0;
+
+	    switch (action) {
+	    case US_ACT_COMMAND       :
+		if (!us->pusb_dev || us->srb->target || us->srb->lun) {
+		    /* bad device */
+		    US_DEBUGP( "Bad device number (%d/%d) or dev %x\n", us->srb->target, us->srb->lun, (unsigned int)us->pusb_dev);
+		    us->srb->result = DID_BAD_TARGET << 16;
+		} else {
+		    US_DEBUG(us_show_command(us->srb));
+		    if (us->filter && us->filter->command)
+		        us->srb->result = us->filter->command(us->fdata, us->srb);
+		    else
+		        us->srb->result = us->pop(us->srb);
+		}
+		us->srb->scsi_done(us->srb);
+		break;
+
+	    case US_ACT_ABORT         :
+		break;
+
+	    case US_ACT_DEVICE_RESET  :
+		break;
+
+	    case US_ACT_BUS_RESET     :
+		break;
+
+	    case US_ACT_HOST_RESET    :
+		break;
+
+	    }
+
+	    if(signal_pending(current)) {
+		    /* sending SIGUSR1 makes us print out some info */
+		    spin_lock_irq(&current->sigmask_lock);
+		    signr = dequeue_signal(&current->blocked, &info);
+		    spin_unlock_irq(&current->sigmask_lock);
+
+		    if (signr == SIGUSR2) {
+			    printk("USBSCSI debug toggle\n");
+			    usbscsi_debug = !usbscsi_debug;
+		    } else {
+			    break;
+		    }
+	    }
+    }
+
+    MOD_DEC_USE_COUNT;
+
+    printk("usbscsi_control_thread exiting\n");
+
+    return 0;
+}	
+
+static int scsi_probe(struct usb_device *dev)
+{
+    struct usb_interface_descriptor *interface;
+    int i;
+    char *mf;		/* manufacturer */
+    char *prod;		/* product */
+    char *serial;		/* serial number */
+    struct us_data *ss = NULL;
+    struct usb_scsi_filter *filter = filters;
+    void *fdata = NULL;
+    unsigned int flags = 0;
+    GUID(guid);
+    struct us_data *prev;
+    Scsi_Host_Template *htmplt;
+    int protocol = 0;
+    int subclass = 0;
+
+    GUID_CLEAR(guid);
+    mf = usb_string(dev, dev->descriptor.iManufacturer);
+    prod = usb_string(dev, dev->descriptor.iProduct);
+    serial = usb_string(dev, dev->descriptor.iSerialNumber);
+
+    /* probe with filters first */
+
+    if (mf && prod)	{
+	    while (filter) {
+		    if ((fdata = filter->probe(dev, mf, prod, serial)) != NULL) {
+			    flags = filter->flags;
+			    printk(KERN_INFO "USB Scsi filter %s\n", filter->name);
+			    break;
+		    }
+		    filter = filter->next;
+	    }
+    }
+
+    /* generic devices next */
+
+    if (fdata == NULL) {
+
+	/* some exceptions */
+	if (dev->descriptor.idVendor == 0x04e6 &&
+	    dev->descriptor.idProduct == 0x0001) {
+	    /* shuttle E-USB */
+	    protocol = US_PR_ZIP;
+	    subclass = US_SC_8070;	/* an assumption */
+	} else if (dev->descriptor.bDeviceClass != 0 ||
+	    dev->config->altsetting->interface->bInterfaceClass != 8 ||
+	    dev->config->altsetting->interface->bInterfaceSubClass < US_SC_MIN ||
+	    dev->config->altsetting->interface->bInterfaceSubClass > US_SC_MAX) {
+	    return -1;
+	}
+
+	/* now check if we have seen it before */
+
+	if (dev->descriptor.iSerialNumber && 
+	    usb_string(dev, dev->descriptor.iSerialNumber) ) {
+	    make_guid(guid, dev->descriptor.idVendor, dev->descriptor.idProduct,
+		      usb_string(dev, dev->descriptor.iSerialNumber));
+	    for (ss = us_list; ss; ss = ss->next) {
+		if (GUID_EQUAL(guid, ss->guid))	{
+		    US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
+		    break;
+		}
+	    }
+	}
+    }
+
+    if (!ss) {
+	if ((ss = (struct us_data *)kmalloc(sizeof(*ss), GFP_KERNEL)) == NULL) {
+	    printk(KERN_WARNING USB_SCSI "Out of memory\n");
+	    if (filter)
+		    filter->release(fdata);
+	    return -1;
+	}
+	memset(ss, 0, sizeof(struct us_data));
+    }
+
+    interface = dev->config->altsetting->interface;
+    ss->filter = filter;
+    ss->fdata = fdata;
+    ss->flags = flags;
+    if (subclass) {
+	ss->subclass = subclass;
+	ss->protocol = protocol;
+    } else {
+	ss->subclass = interface->bInterfaceSubClass;
+	ss->protocol = interface->bInterfaceProtocol;
+    }
+
+    /* set the protocol op */
+
+    US_DEBUGP("Protocol ");
+    switch (ss->protocol) {
+    case US_PR_CB:
+	US_DEBUGPX("Control/Bulk\n");
+	ss->pop = pop_CBI;
+	break;
+
+    case US_PR_CBI:
+	US_DEBUGPX("Control/Bulk/Interrupt\n");
+	ss->pop = pop_CBI;
+	break;
+
+    default:
+	US_DEBUGPX("Bulk\n");
+	ss->pop = pop_Bulk;
+	break;
+    }
+
+    /* 
+     * we are expecting a minimum of 2 endpoints - in and out (bulk)
+     * an optional interrupt is OK (necessary for CBI protocol)
+     * we will ignore any others
+     */
+
+    for (i = 0; i < interface->bNumEndpoints; i++) { 
+	    if (interface->endpoint[i].bmAttributes == 0x02) {
+		    if (interface->endpoint[i].bEndpointAddress & 0x80)
+			    ss->ep_in = interface->endpoint[i].bEndpointAddress & 0x0f;
+		    else
+			    ss->ep_out = interface->endpoint[i].bEndpointAddress & 0x0f;
+	    } else if (interface->endpoint[i].bmAttributes == 0x03) {
+		    ss->ep_int = interface->endpoint[i].bEndpointAddress & 0x0f;
+	    }
+    }
+    US_DEBUGP("Endpoints In %d Out %d Int %d\n", ss->ep_in, ss->ep_out, ss->ep_int);
+
+    /* exit if strange looking */
+
+    if (usb_set_configuration(dev, dev->config[0].bConfigurationValue) ||
+	!ss->ep_in || !ss->ep_out || (ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
+	US_DEBUGP("Problems with device\n");
+	if (ss->host) {
+	    scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt);
+	    kfree(ss->htmplt->name);
+	    kfree(ss->htmplt);
+	}
+	if (filter)
+		filter->release(fdata);
+	kfree(ss);
+	return -1;			/* no endpoints */
+    }
+
+    if (dev->config[0].iConfiguration && usb_string(dev, dev->config[0].iConfiguration))
+	US_DEBUGP("Configuration %s\n", usb_string(dev, dev->config[0].iConfiguration));
+    if (interface->iInterface && usb_string(dev, interface->iInterface))
+	US_DEBUGP("Interface %s\n", usb_string(dev, interface->iInterface));
+
+    ss->pusb_dev = dev;
+
+    /* Now generate a scsi host definition, and register with scsi above us */
+
+    if (!ss->host) {
+
+	/* make unique id if possible */
+
+	if (dev->descriptor.iSerialNumber && 
+	    usb_string(dev, dev->descriptor.iSerialNumber) ) {
+	    make_guid(ss->guid, dev->descriptor.idVendor, dev->descriptor.idProduct,
+		      usb_string(dev, dev->descriptor.iSerialNumber));
+	}
+
+	US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
+
+	/* set class specific stuff */
+
+	US_DEBUGP("SubClass ");
+	switch (ss->subclass) {
+	case US_SC_RBC:
+	    US_DEBUGPX("Reduced Block Commands\n");
+	    break;
+	case US_SC_8020:
+	    US_DEBUGPX("8020\n");
+	    break;
+	case US_SC_QIC:
+	    US_DEBUGPX("QIC157\n");
+	    break;
+	case US_SC_8070:
+	    US_DEBUGPX("8070\n");
+	    ss->flags |= US_FL_FIXED_COMMAND;
+	    ss->fixedlength = 12;
+	    break;
+	case US_SC_SCSI:
+	    US_DEBUGPX("Transparent SCSI\n");
+	    break;
+	case US_SC_UFI:
+	    US_DEBUGPX(" UFF\n");
+	    ss->flags |= US_FL_FIXED_COMMAND;
+	    ss->fixedlength = 12;
+	    break;
+
+	default:
+	    break;
+	}
+
+	/* create unique host template */
+
+	if ((htmplt = (Scsi_Host_Template *)kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) {
+	    printk(KERN_WARNING USB_SCSI "Out of memory\n");
+	    if (filter)
+		    filter->release(fdata);
+	    kfree(ss);
+	    return -1;
+	}
+	memcpy(htmplt, &my_host_template, sizeof(my_host_template));
+	ss->host_number = my_host_number++;
+
+
+	(struct us_data *)htmplt->proc_dir = ss;
+	if (ss->protocol == US_PR_CBI)
+	    init_waitqueue_head(&ss->ip_waitq);
+
+	/* start up our thread */
+
+	{
+	    DECLARE_MUTEX_LOCKED(sem);
+
+	    init_waitqueue_head(&ss->waitq);
+    
+	    ss->notify = &sem;
+	    ss->pid = kernel_thread(usbscsi_control_thread, ss,
+		    CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	    if (ss->pid < 0) {
+		printk(KERN_WARNING USB_SCSI "Unable to start control thread\n");
+		kfree(htmplt);
+		if (filter)
+			filter->release(fdata);
+		kfree(ss);
+		return -1;
+	    }
+
+	    /* wait for it to start */
+
+	    down(&sem);
+	}
+
+	/* now register - our detect function will be called */
+
+	scsi_register_module(MODULE_SCSI_HA, htmplt);
+
+	/* put us in the list */
+
+	prev = (struct us_data *)&us_list;
+	while (prev->next)
+	    prev = prev->next;
+	prev->next = ss;
+
+    }
+
+
+    printk(KERN_INFO "USB SCSI device found at address %d\n", dev->devnum);
+
+    dev->private = ss;
+    return 0;
+}
+
+static void scsi_disconnect(struct usb_device *dev)
+{
+	struct us_data *ss = dev->private;
+
+	if (!ss)
+	    return;
+	if (ss->filter)
+		ss->filter->release(ss->fdata);
+	ss->pusb_dev = NULL;
+	dev->private = NULL;		/* just in case */
+	MOD_DEC_USE_COUNT;
+}
+
+int usb_scsi_init(void)
+{
+
+	MOD_INC_USE_COUNT;
+#ifdef CONFIG_USB_HP4100
+	hp4100_init();
+#endif
+#ifdef CONFIG_USB_ZIP
+	usb_zip_init();
+#endif
+	usb_register(&scsi_driver);
+	printk(KERN_INFO "USB SCSI support registered.\n");
+	return 0;
+}
+
+
+int usb_scsi_register(struct usb_scsi_filter *filter)
+{
+	struct usb_scsi_filter *prev = (struct usb_scsi_filter *)&filters;
+
+	while (prev->next)
+		prev = prev->next;
+	prev->next = filter;
+	return 0;
+}
+
+void usb_scsi_deregister(struct usb_scsi_filter *filter)
+{
+	struct usb_scsi_filter *prev = (struct usb_scsi_filter *)&filters;
+
+	while (prev->next && prev->next != filter)
+		prev = prev->next;
+	if (prev->next)
+		prev->next = filter->next;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+
+	return usb_scsi_init();
+}
+
+void cleanup_module(void)
+{
+	unsigned int offset;
+
+	usb_deregister(&scsi_driver);
+}
+#endif
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/usb_scsi.h linux/drivers/usb/usb_scsi.h
--- v2.3.5/linux/drivers/usb/usb_scsi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/usb_scsi.h	Mon Jun  7 20:04:01 1999
@@ -0,0 +1,145 @@
+/* Driver for USB scsi - include file
+ * 
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ * This driver is scitzoid  - it make a USB scanner appear as both a SCSI device
+ * and a character device. The latter is only available if the device has an
+ * interrupt endpoint, and is used specifically to receive interrupt events.
+ *
+ * In order to support various 'strange' scanners, this module supports plug in
+ * device specific filter modules, which can do their own thing when required.
+ *
+ */
+
+#define USB_SCSI "usbscsi: "
+
+extern int usbscsi_debug;
+
+#ifdef CONFIG_USB_SCSI_DEBUG
+void us_show_command(Scsi_Cmnd *srb);
+#define US_DEBUGP(x...) { if(usbscsi_debug) printk( KERN_DEBUG USB_SCSI ## x ); }
+#define US_DEBUGPX(x...) { if(usbscsi_debug) printk( ## x ); }
+#define US_DEBUG(x)  { if(usbscsi_debug) x; }
+#else
+#define US_DEBUGP(x...)
+#define US_DEBUGPX(x...)
+#define US_DEBUG(x)
+#endif
+
+/* bit set if input */
+extern unsigned char us_direction[256/8];
+#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1)
+
+/* Sub Classes */
+
+#define US_SC_RBC	1		/* Typically, flash devices */
+#define US_SC_8020	2		/* CD-ROM */
+#define US_SC_QIC	3		/* QIC-157 Tapes */
+#define US_SC_UFI	4		/* Floppy */
+#define US_SC_8070	5		/* Removable media */
+#define US_SC_SCSI	6		/* Transparent */
+#define US_SC_MIN	US_SC_RBC
+#define US_SC_MAX	US_SC_SCSI
+
+/* Protocols */
+
+#define US_PR_CB	1		/* Control/Bulk w/o interrupt */
+#define US_PR_CBI	0		/* Control/Bulk/Interrupt */
+#define US_PR_ZIP	0x50		/* bulk only */
+/* #define US_PR_BULK	?? */
+
+/*
+ * Bulk only data structures (Zip 100, for example)
+ */
+
+struct bulk_cb_wrap {
+    __u32	Signature;		/* contains 'USBC' */
+    __u32	Tag;			/* unique per command id */
+    __u32	DataTransferLength;	/* size of data */
+    __u8	Flags;	       		/* direction in bit 0 */
+    __u8	Lun;			/* LUN normally 0 */
+    __u8	Length;			/* of of the CDB */
+    __u8	CDB[16];		/* max command */
+};
+
+#define US_BULK_CB_WRAP_LEN 	31
+#define US_BULK_CB_SIGN		0x43425355
+#define US_BULK_FLAG_IN		1
+#define US_BULK_FLAG_OUT	0
+
+struct bulk_cs_wrap {
+    __u32	Signature;		/* should = 'USBS' */
+    __u32	Tag;			/* same as original command */
+    __u32	Residue;		/* amount not transferred */
+    __u8	Status;			/* see below */
+    __u8	Filler[18];
+};
+
+#define US_BULK_CS_WRAP_LEN	31
+#define US_BULK_CS_SIGN		0x53425355
+#define US_BULK_STAT_OK		0
+#define US_BULK_STAT_FAIL	1
+#define US_BULK_STAT_PHASE	2
+
+#define US_BULK_RESET		0xff
+#define US_BULK_RESET_SOFT	1
+#define US_BULK_RESET_HARD	0
+
+/*
+ * CBI style
+ */
+
+#define US_CBI_ADSC		0
+
+/*
+ * Filter device definitions
+ */
+struct usb_scsi_filter {
+
+	struct usb_scsi_filter * next;	/* usb_scsi driver only */
+	char *name;			/* not really required */
+
+        unsigned int flags;                     			/* Filter flags */
+        void * (* probe) (struct usb_device *, char *, char *, char *);	/* probe device */
+	void (* release)(void *);					/* device gone */
+        int (* command)(void *, Scsi_Cmnd *);  /* all commands */
+};
+
+#define GUID(x) __u32 x[3]
+#define GUID_EQUAL(x, y) (x[0] == y[0] && x[1] == y[1] && x[2] == y[2])
+#define GUID_CLEAR(x) x[0] = x[1] = x[2] = 0;
+#define GUID_NONE(x) (!x[0] && !x[1] && !x[2])
+#define GUID_FORMAT "%08x%08x%08x"
+#define GUID_ARGS(x) x[0], x[1], x[2]
+
+static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *serial)
+{
+	pg[0] = (vendor << 16) | product;
+	pg[1] = pg[2] = 0;
+	while (*serial) {
+		pg[1] <<= 4;
+		pg[1] |= pg[2] >> 28;
+		pg[2] <<= 4;
+		if (*serial >= 'a')
+		    *serial -= 'a' - 'A';
+		pg[2] |= (*serial <= '9' && *serial >= '0') ? *serial - '0'
+						    	    : *serial - 'A' + 10;
+		serial++;
+	}
+}
+
+/* Flag definitions */
+#define US_FL_IP_STATUS		0x00000001		/* status uses interrupt */
+#define US_FL_FIXED_COMMAND	0x00000002		/* expand commands to fixed size */
+
+/*
+ * Called by filters to register/unregister the mini driver
+ *
+ * WARNING - the supplied probe function may be called before exiting this fn
+ */
+int usb_scsi_register(struct usb_scsi_filter *);
+void usb_scsi_deregister(struct usb_scsi_filter *);
+
+#ifdef CONFIG_USB_HP4100
+int hp4100_init(void);
+#endif
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/usb_scsi_debug.c linux/drivers/usb/usb_scsi_debug.c
--- v2.3.5/linux/drivers/usb/usb_scsi_debug.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/usb_scsi_debug.c	Mon Jun  7 20:04:01 1999
@@ -0,0 +1,104 @@
+
+/* Driver for USB scsi like devices
+ * 
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+
+#include <asm/spinlock.h>
+
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+
+#include "usb.h"
+#include "usb_scsi.h"
+
+void us_show_command(Scsi_Cmnd *srb)
+{
+    char *what;
+
+    switch (srb->cmnd[0]) {
+    case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
+    case REZERO_UNIT: what = "REZERO_UNIT"; break;
+    case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
+    case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
+    case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
+    case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
+    case READ_6: what = "READ_6"; break;
+    case WRITE_6: what = "WRITE_6"; break;
+    case SEEK_6: what = "SEEK_6"; break;
+    case READ_REVERSE: what = "READ_REVERSE"; break;
+    case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
+    case SPACE: what = "SPACE"; break;
+    case INQUIRY: what = "INQUIRY"; break;
+    case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
+    case MODE_SELECT: what = "MODE_SELECT"; break;
+    case RESERVE: what = "RESERVE"; break;
+    case RELEASE: what = "RELEASE"; break;
+    case COPY: what = "COPY"; break;
+    case ERASE: what = "ERASE"; break;
+    case MODE_SENSE: what = "MODE_SENSE"; break;
+    case START_STOP: what = "START_STOP"; break;
+    case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
+    case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
+    case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
+    case SET_WINDOW: what = "SET_WINDOW"; break;
+    case READ_CAPACITY: what = "READ_CAPACITY"; break;
+    case READ_10: what = "READ_10"; break;
+    case WRITE_10: what = "WRITE_10"; break;
+    case SEEK_10: what = "SEEK_10"; break;
+    case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
+    case VERIFY: what = "VERIFY"; break;
+    case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
+    case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
+    case SEARCH_LOW: what = "SEARCH_LOW"; break;
+    case SET_LIMITS: what = "SET_LIMITS"; break;
+    case READ_POSITION: what = "READ_POSITION"; break;
+    case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
+    case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
+    case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
+    case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
+    case COMPARE: what = "COMPARE"; break;
+    case COPY_VERIFY: what = "COPY_VERIFY"; break;
+    case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
+    case READ_BUFFER: what = "READ_BUFFER"; break;
+    case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
+    case READ_LONG: what = "READ_LONG"; break;
+    case WRITE_LONG: what = "WRITE_LONG"; break;
+    case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
+    case WRITE_SAME: what = "WRITE_SAME"; break;
+    case READ_TOC: what = "READ_TOC"; break;
+    case LOG_SELECT: what = "LOG_SELECT"; break;
+    case LOG_SENSE: what = "LOG_SENSE"; break;
+    case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
+    case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
+    case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
+    case READ_12: what = "READ_12"; break;
+    case WRITE_12: what = "WRITE_12"; break;
+    case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
+    case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
+    case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
+    case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
+    case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
+    case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
+    case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
+    default: what = "??"; break;
+    }
+    printk(KERN_DEBUG USB_SCSI "Command %s (%d bytes)\n", what, srb->cmd_len);
+    printk(KERN_DEBUG USB_SCSI "  %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+	   srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5], 
+           srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]);
+}
diff -u --recursive --new-file v2.3.5/linux/drivers/usb/usb_scsi_dt.c linux/drivers/usb/usb_scsi_dt.c
--- v2.3.5/linux/drivers/usb/usb_scsi_dt.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/usb_scsi_dt.c	Mon Jun  7 20:04:01 1999
@@ -0,0 +1,4 @@
+0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, 
+0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
diff -u --recursive --new-file v2.3.5/linux/drivers/video/atyfb.c linux/drivers/video/atyfb.c
--- v2.3.5/linux/drivers/video/atyfb.c	Thu Apr 22 19:30:08 1999
+++ linux/drivers/video/atyfb.c	Wed Jun  9 14:44:25 1999
@@ -1,4 +1,4 @@
-/*  $Id: atyfb.c,v 1.106 1999/04/16 11:20:49 geert Exp $
+/*  $Id: atyfb.c,v 1.107 1999/06/08 19:59:03 geert Exp $
  *  linux/drivers/video/atyfb.c -- Frame buffer device for ATI Mach64
  *
  *	Copyright (C) 1997-1998  Geert Uytterhoeven
@@ -200,6 +200,7 @@
     struct atyfb_par default_par;
     struct atyfb_par current_par;
     u32 total_vram;
+    u32 ref_clk_per;
     u32 pll_per;
     u32 mclk_per;
     u16 chip_type;
@@ -326,9 +327,7 @@
 static void init_engine(const struct atyfb_par *par, struct fb_info_aty *info);
 static void aty_st_514(int offset, u8 val, const struct fb_info_aty *info);
 static void aty_st_pll(int offset, u8 val, const struct fb_info_aty *info);
-#if defined(__sparc__) || defined(DEBUG)
 static u8 aty_ld_pll(int offset, const struct fb_info_aty *info);
-#endif
 static void aty_set_crtc(const struct fb_info_aty *info,
 			 const struct crtc *crtc);
 static int aty_var_to_crtc(const struct fb_info_aty *info,
@@ -341,7 +340,8 @@
 			   const struct pll_gx *pll);
 static int aty_var_to_pll_18818(u32 vclk_per, struct pll_gx *pll);
 static int aty_var_to_pll_514(u32 vclk_per, struct pll_gx *pll);
-static int aty_pll_gx_to_var(const struct pll_gx *pll, u32 *vclk_per);
+static int aty_pll_gx_to_var(const struct pll_gx *pll, u32 *vclk_per,
+			     const struct fb_info_aty *info);
 static void aty_set_pll_ct(const struct fb_info_aty *info,
 			   const struct pll_ct *pll);
 static int aty_dsp_gt(const struct fb_info_aty *info, u8 mclk_fb_div,
@@ -349,7 +349,8 @@
 		      u8 bpp, struct pll_ct *pll);
 static int aty_var_to_pll_ct(const struct fb_info_aty *info, u32 vclk_per,
 			     u8 bpp, struct pll_ct *pll);
-static int aty_pll_ct_to_var(const struct pll_ct *pll, u32 *vclk_per);
+static int aty_pll_ct_to_var(const struct pll_ct *pll, u32 *vclk_per,
+			     const struct fb_info_aty *info);
 static void atyfb_set_par(const struct atyfb_par *par,
 			  struct fb_info_aty *info);
 static int atyfb_decode_var(const struct fb_var_screeninfo *var,
@@ -405,8 +406,6 @@
 static int default_pll __initdata = 0;
 static int default_mclk __initdata = 0;
 
-static const u32 ref_clk_per = 1000000000000ULL/14318180;
-
 #if defined(CONFIG_PPC)
 static int default_vmode __initdata = VMODE_NVRAM;
 static int default_cmode __initdata = CMODE_NVRAM;
@@ -689,7 +688,6 @@
     aty_st_8(CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN, info);
 }
 
-#if defined(__sparc__) || defined(DEBUG)
 static u8 aty_ld_pll(int offset, const struct fb_info_aty *info)
 {
     u8 res;
@@ -702,7 +700,6 @@
     eieio();
     return res;
 }
-#endif
 
 #if defined(CONFIG_PPC)
 
@@ -1455,7 +1452,8 @@
 
     /* FIXME: ATI18818?? */
 
-static int aty_pll_gx_to_var(const struct pll_gx *pll, u32 *vclk_per)
+static int aty_pll_gx_to_var(const struct pll_gx *pll, u32 *vclk_per,
+			     const struct fb_info_aty *info)
 {
     u8 df, vco_div_count, ref_div_count;
 
@@ -1463,7 +1461,7 @@
     vco_div_count = pll->m & 0x3f;
     ref_div_count = pll->n;
 
-    *vclk_per = ((ref_clk_per*ref_div_count)<<(3-df))/(vco_div_count+65);
+    *vclk_per = ((info->ref_clk_per*ref_div_count)<<(3-df))/(vco_div_count+65);
 
     return 0;
 }
@@ -1579,10 +1577,10 @@
 
     pll->pll_vclk_cntl = 0x03;	/* VCLK = PLL_VCLK/VCLKx_POST */
 
-    pll_ref_div = info->pll_per*2*255/ref_clk_per;
+    pll_ref_div = info->pll_per*2*255/info->ref_clk_per;
 
     /* FIXME: use the VTB/GTB /3 post divider if it's better suited */
-    q = ref_clk_per*pll_ref_div*4/info->mclk_per;	/* actually 8*q */
+    q = info->ref_clk_per*pll_ref_div*4/info->mclk_per;	/* actually 8*q */
     if (q < 16*8 || q > 255*8)
 	FAIL("mclk out of range");
     else if (q < 32*8)
@@ -1596,7 +1594,7 @@
     mclk_fb_div = q*mclk_post_div/8;
 
     /* FIXME: use the VTB/GTB /{3,6,12} post dividers if they're better suited */
-    q = ref_clk_per*pll_ref_div*4/vclk_per;	/* actually 8*q */
+    q = info->ref_clk_per*pll_ref_div*4/vclk_per;	/* actually 8*q */
     if (q < 16*8 || q > 255*8)
 	FAIL("vclk out of range");
     else if (q < 32*8)
@@ -1677,7 +1675,8 @@
     return 0;
 }
 
-static int aty_pll_ct_to_var(const struct pll_ct *pll, u32 *vclk_per)
+static int aty_pll_ct_to_var(const struct pll_ct *pll, u32 *vclk_per,
+			     const struct fb_info_aty *info)
 {
     u8 pll_ref_div = pll->pll_ref_div;
     u8 vclk_fb_div = pll->vclk_fb_div;
@@ -1691,7 +1690,7 @@
 				    (vclk_post_div & 3)];
     if (vpostdiv == 0)
 	return -EINVAL;
-    *vclk_per = pll_ref_div*vpostdiv*ref_clk_per/vclk_fb_div/2;
+    *vclk_per = pll_ref_div*vpostdiv*info->ref_clk_per/vclk_fb_div/2;
     return 0;
 }
 
@@ -1845,9 +1844,9 @@
     if ((err = aty_crtc_to_var(&par->crtc, var)))
 	return err;
     if ((Gx == GX_CHIP_ID) || (Gx == CX_CHIP_ID))
-	err = aty_pll_gx_to_var(&par->pll.gx, &var->pixclock);
+	err = aty_pll_gx_to_var(&par->pll.gx, &var->pixclock, info);
     else
-	err = aty_pll_ct_to_var(&par->pll.ct, &var->pixclock);
+	err = aty_pll_ct_to_var(&par->pll.ct, &var->pixclock, info);
     if (err)
 	return err;
 
@@ -2432,11 +2431,12 @@
     int j, k;
     struct fb_var_screeninfo var;
     struct display *disp;
-    const char *chipname = NULL, *ramname = NULL;
+    const char *chipname = NULL, *ramname = NULL, *xtal;
     int pll, mclk, gtb_memsize;
 #if defined(CONFIG_PPC)
     int sense;
 #endif
+    u8 pll_ref_div;
 
     info->aty_cmap_regs = (struct aty_cmap_regs *)(info->ati_regbase+0xc0);
     chip_id = aty_ld_le32(CONFIG_CHIP_ID, info);
@@ -2524,6 +2524,25 @@
 	}
     }
 
+    info->ref_clk_per = 1000000000000ULL/14318180;
+    xtal = "14.31818";
+    if (!(Gx == GX_CHIP_ID || Gx == CX_CHIP_ID || Gx == CT_CHIP_ID ||
+	  Gx == ET_CHIP_ID ||
+	  ((Gx == VT_CHIP_ID || Gx == GT_CHIP_ID) && !(Rev & 0x07))) &&
+	(pll_ref_div = aty_ld_pll(PLL_REF_DIV, info))) {
+	int diff1, diff2;
+	diff1 = 510*14/pll_ref_div-pll;
+	diff2 = 510*29/pll_ref_div-pll;
+	if (diff1 < 0)
+	    diff1 = -diff1;
+	if (diff2 < 0)
+	    diff2 = -diff2;
+	if (diff2 < diff1) {
+	    info->ref_clk_per = 1000000000000ULL/29498928;
+	    xtal = "29.498928";
+	}
+    }
+
     i = aty_ld_le32(MEM_CNTL, info);
     gtb_memsize = !(Gx == GX_CHIP_ID || Gx == CX_CHIP_ID || Gx == CT_CHIP_ID ||
 		    Gx == ET_CHIP_ID ||
@@ -2602,9 +2621,9 @@
     if (default_mclk)
 	mclk = default_mclk;
 
-    printk("%d%c %s, %d MHz PLL, %d Mhz MCLK\n", 
+    printk("%d%c %s, %s MHz XTAL, %d MHz PLL, %d Mhz MCLK\n", 
     	   info->total_vram == 0x80000 ? 512 : (info->total_vram >> 20), 
-    	   info->total_vram == 0x80000 ? 'K' : 'M', ramname, pll, mclk);
+    	   info->total_vram == 0x80000 ? 'K' : 'M', ramname, xtal, pll, mclk);
 
     if (mclk < 44)
 	info->mem_refresh_rate = 0;	/* 000 = 10 Mhz - 43 Mhz */
diff -u --recursive --new-file v2.3.5/linux/drivers/video/igafb.c linux/drivers/video/igafb.c
--- v2.3.5/linux/drivers/video/igafb.c	Sun Mar  7 10:39:03 1999
+++ linux/drivers/video/igafb.c	Wed Jun  9 14:44:26 1999
@@ -630,6 +630,7 @@
         struct fb_info_iga *info;
         unsigned long addr;
         extern int con_is_present(void);
+	int iga2000 = 0;
 
         /* Do not attach when we have a serial console. */
         if (!con_is_present())
@@ -637,8 +638,13 @@
 
         pdev = pci_find_device(PCI_VENDOR_ID_INTERG, 
                                PCI_DEVICE_ID_INTERG_1682, 0);
-        if(pdev == NULL)
-                return;
+	if (pdev == NULL) {
+        	pdev = pci_find_device(PCI_VENDOR_ID_INTERG, 
+                               0x2000, 0);
+        	if(pdev == NULL)
+        	        return;
+		iga2000 = 1;
+	}
 
         info = kmalloc(sizeof(struct fb_info_iga), GFP_ATOMIC);
         if (!info) {
@@ -648,8 +654,10 @@
         memset(info, 0, sizeof(struct fb_info_iga));
 
 	info->frame_buffer = pdev->base_address[0];
-	if (!info->frame_buffer)
+	if (!info->frame_buffer) {
+		kfree(info);
 		return;
+	}
 
         pcibios_read_config_dword(0, pdev->devfn,
                                   PCI_BASE_ADDRESS_0, 
@@ -659,12 +667,23 @@
 	info->frame_buffer_phys = addr & PCI_BASE_ADDRESS_MEM_MASK;
 
 #ifdef __sparc__
-	
+
 	info->io_base_phys = info->frame_buffer_phys;
-	
-	/* Obtain virtual address and correct physical by PCIC shift */
-	info->io_base = pcic_alloc_io(&info->io_base_phys);
+
+	/*
+	 * The right test would be to look if there is a base I/O address.
+	 * But it appears that IGA 1682 reuses _memory_ address as a base
+	 * for I/O accesses.
+	 */
+	if (iga2000) {
+		info->io_base = (int) sparc_alloc_io(info->frame_buffer_phys |
+		    0x00800000, NULL, 0x1000, "iga", 0, 0);
+	} else {
+		/* Obtain virtual address and correct physical by PCIC shift */
+		info->io_base = pcic_alloc_io(&info->io_base_phys);
+	}
 	if (!info->io_base) {
+                kfree(info);
 		return;
 	}
 
@@ -679,6 +698,7 @@
 	info->mmap_map = kmalloc(4 * sizeof(*info->mmap_map), GFP_ATOMIC);
 	if (!info->mmap_map) {
                 printk("igafb_init: can't alloc mmap_map\n");
+		/* XXX Here we left I/O allocated */
                 kfree(info);
 		return;
 	}
@@ -730,11 +750,11 @@
             }
 
 #endif
-            if (!iga_init(info)) {
-		    if (info->mmap_map)
-			    kfree(info->mmap_map);
-		    kfree(info);
-            }
+        if (!iga_init(info)) {
+		if (info->mmap_map)
+			kfree(info->mmap_map);
+		kfree(info);
+        }
 
 #ifdef __sparc__
 	    /*
diff -u --recursive --new-file v2.3.5/linux/fs/Config.in linux/fs/Config.in
--- v2.3.5/linux/fs/Config.in	Mon May 31 22:28:06 1999
+++ linux/fs/Config.in	Wed Jun  9 18:42:08 1999
@@ -73,9 +73,9 @@
   fi
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
     tristate 'NFS server support' CONFIG_NFSD
-  fi
-  if [ "$CONFIG_NFSD" != "n" ]; then
-    bool '   Emulate SUN NFS server' CONFIG_NFSD_SUN
+    if [ "$CONFIG_NFSD" != "n" ]; then
+      bool '   Emulate SUN NFS server' CONFIG_NFSD_SUN
+    fi
   fi
   if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then
     define_bool CONFIG_SUNRPC y
diff -u --recursive --new-file v2.3.5/linux/fs/adfs/super.c linux/fs/adfs/super.c
--- v2.3.5/linux/fs/adfs/super.c	Fri Mar 26 13:57:41 1999
+++ linux/fs/adfs/super.c	Tue Jun  8 10:47:58 1999
@@ -300,7 +300,7 @@
 	 */
 	sb->s_op = &adfs_sops;
 	sb->u.adfs_sb.s_root = adfs_inode_generate(dr->root, 0);
-	sb->s_root = d_alloc_root(iget(sb, sb->u.adfs_sb.s_root), NULL);
+	sb->s_root = d_alloc_root(iget(sb, sb->u.adfs_sb.s_root));
 
 	if (!sb->s_root) {
 		for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
@@ -312,8 +312,7 @@
 	return sb;
 
 error_free_bh:
-	if (bh)
-		brelse(bh);
+	brelse(bh);
 error_unlock:
 	unlock_super(sb);
 error_dec_use:
diff -u --recursive --new-file v2.3.5/linux/fs/affs/super.c linux/fs/affs/super.c
--- v2.3.5/linux/fs/affs/super.c	Sun Mar  7 15:25:23 1999
+++ linux/fs/affs/super.c	Tue Jun  8 10:47:58 1999
@@ -543,7 +543,7 @@
 	root_inode = iget(s,root_block);
 	if (!root_inode)
 		goto out_no_root;
-	s->s_root  = d_alloc_root(root_inode, NULL);
+	s->s_root  = d_alloc_root(root_inode);
 	if (!s->s_root)
 		goto out_no_root;
 	s->s_root->d_op = &affs_dentry_operations;
diff -u --recursive --new-file v2.3.5/linux/fs/autofs/inode.c linux/fs/autofs/inode.c
--- v2.3.5/linux/fs/autofs/inode.c	Mon Jan 11 15:03:30 1999
+++ linux/fs/autofs/inode.c	Tue Jun  8 10:47:58 1999
@@ -176,7 +176,7 @@
 	 * Get the root inode and dentry, but defer checking for errors.
 	 */
 	root_inode = iget(s, AUTOFS_ROOT_INO);
-	root = d_alloc_root(root_inode, NULL);
+	root = d_alloc_root(root_inode);
 	pipe = NULL;
 
 	/*
diff -u --recursive --new-file v2.3.5/linux/fs/binfmt_aout.c linux/fs/binfmt_aout.c
--- v2.3.5/linux/fs/binfmt_aout.c	Wed Jun  2 14:44:39 1999
+++ linux/fs/binfmt_aout.c	Tue Jun  8 23:01:35 1999
@@ -49,9 +49,7 @@
 	end = PAGE_ALIGN(end);
 	if (end <= start)
 		return;
-	do_mmap(NULL, start, end - start,
-		PROT_READ | PROT_WRITE | PROT_EXEC,
-		MAP_FIXED | MAP_PRIVATE, 0);
+	do_brk(start, end - start);
 }
 
 /*
@@ -372,14 +370,10 @@
 #ifdef __sparc__
 	if (N_MAGIC(ex) == NMAGIC) {
 		/* Fuck me plenty... */
-		error = do_mmap(NULL, N_TXTADDR(ex), ex.a_text,
-				PROT_READ|PROT_WRITE|PROT_EXEC,
-				MAP_FIXED|MAP_PRIVATE, 0);
+		error = do_brk(N_TXTADDR(ex), ex.a_text);
 		read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex),
 			  ex.a_text, 0);
-		error = do_mmap(NULL, N_DATADDR(ex), ex.a_data,
-				PROT_READ|PROT_WRITE|PROT_EXEC,
-				MAP_FIXED|MAP_PRIVATE, 0);
+		error = do_brk(N_DATADDR(ex), ex.a_data);
 		read_exec(bprm->dentry, fd_offset + ex.a_text, (char *) N_DATADDR(ex),
 			  ex.a_data, 0);
 		goto beyond_if;
@@ -388,16 +382,12 @@
 
 	if (N_MAGIC(ex) == OMAGIC) {
 #if defined(__alpha__) || defined(__sparc__)
-		do_mmap(NULL, N_TXTADDR(ex) & PAGE_MASK,
-			ex.a_text+ex.a_data + PAGE_SIZE - 1,
-			PROT_READ|PROT_WRITE|PROT_EXEC,
-			MAP_FIXED|MAP_PRIVATE, 0);
+		do_brk(N_TXTADDR(ex) & PAGE_MASK,
+			ex.a_text+ex.a_data + PAGE_SIZE - 1);
 		read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex),
 			  ex.a_text+ex.a_data, 0);
 #else
-		do_mmap(NULL, 0, ex.a_text+ex.a_data,
-			PROT_READ|PROT_WRITE|PROT_EXEC,
-			MAP_FIXED|MAP_PRIVATE, 0);
+		do_brk(0, ex.a_text+ex.a_data);
 		read_exec(bprm->dentry, 32, (char *) 0, ex.a_text+ex.a_data, 0);
 #endif
 		flush_icache_range((unsigned long) 0,
@@ -421,9 +411,7 @@
 
 		if (!file->f_op || !file->f_op->mmap || ((fd_offset & ~PAGE_MASK) != 0)) {
 			sys_close(fd);
-			do_mmap(NULL, 0, ex.a_text+ex.a_data,
-				PROT_READ|PROT_WRITE|PROT_EXEC,
-				MAP_FIXED|MAP_PRIVATE, 0);
+			do_brk(0, ex.a_text+ex.a_data);
 			read_exec(bprm->dentry, fd_offset,
 				  (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0);
 			flush_icache_range((unsigned long) N_TXTADDR(ex),
@@ -574,9 +562,7 @@
 	len = PAGE_ALIGN(ex.a_text + ex.a_data);
 	bss = ex.a_text + ex.a_data + ex.a_bss;
 	if (bss > len) {
-		error = do_mmap(NULL, start_addr + len, bss - len,
-				PROT_READ | PROT_WRITE | PROT_EXEC,
-				MAP_PRIVATE | MAP_FIXED, 0);
+		error = do_brk(start_addr + len, bss - len);
 		retval = error;
 		if (error != start_addr + len)
 			goto out_putf;
diff -u --recursive --new-file v2.3.5/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v2.3.5/linux/fs/binfmt_elf.c	Wed Jun  2 14:44:39 1999
+++ linux/fs/binfmt_elf.c	Thu Jun  3 23:15:29 1999
@@ -77,9 +77,7 @@
 	end = ELF_PAGEALIGN(end);
 	if (end <= start)
 		return;
-	do_mmap(NULL, start, end - start,
-		PROT_READ | PROT_WRITE | PROT_EXEC,
-		MAP_FIXED | MAP_PRIVATE, 0);
+	do_brk(start, end - start);
 }
 
 
@@ -328,9 +326,7 @@
 
 	/* Map the last of the bss segment */
 	if (last_bss > elf_bss)
-		do_mmap(NULL, elf_bss, last_bss - elf_bss,
-			PROT_READ|PROT_WRITE|PROT_EXEC,
-			MAP_FIXED|MAP_PRIVATE, 0);
+		do_brk(elf_bss, last_bss - elf_bss);
 
 	*interp_load_addr = load_addr;
 	error = ((unsigned long) interp_elf_ex->e_entry) + load_addr;
@@ -370,17 +366,15 @@
 		goto out;
 	}
 
-	do_mmap(NULL, 0, text_data,
-		PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0);
+	do_brk(0, text_data);
 	retval = read_exec(interpreter_dentry, offset, addr, text_data, 0);
 	if (retval < 0)
 		goto out;
 	flush_icache_range((unsigned long)addr,
 	                   (unsigned long)addr + text_data);
 
-	do_mmap(NULL, ELF_PAGESTART(text_data + ELF_EXEC_PAGESIZE - 1),
-		interp_ex->a_bss,
-		PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0);
+	do_brk(ELF_PAGESTART(text_data + ELF_EXEC_PAGESIZE - 1),
+		interp_ex->a_bss);
 	elf_entry = interp_ex->a_entry;
 
 out:
@@ -885,9 +879,7 @@
 				ELF_EXEC_PAGESIZE - 1);
 	bss = elf_phdata->p_memsz + elf_phdata->p_vaddr;
 	if (bss > len)
-		do_mmap(NULL, len, bss - len,
-			PROT_READ|PROT_WRITE|PROT_EXEC,
-			MAP_FIXED|MAP_PRIVATE, 0);
+		do_brk(len, bss - len);
 	error = 0;
 
 out_free_ph:
diff -u --recursive --new-file v2.3.5/linux/fs/buffer.c linux/fs/buffer.c
--- v2.3.5/linux/fs/buffer.c	Sat May 15 23:46:04 1999
+++ linux/fs/buffer.c	Tue Jun  8 10:47:58 1999
@@ -90,7 +90,7 @@
 /* The dummy values in this structure are left in there for compatibility
  * with old programs that play with the /proc entries.
  */
-union bdflush_param{
+union bdflush_param {
 	struct {
 		int nfract;  /* Percentage of buffer cache dirty to 
 				activate bdflush */
diff -u --recursive --new-file v2.3.5/linux/fs/coda/inode.c linux/fs/coda/inode.c
--- v2.3.5/linux/fs/coda/inode.c	Wed Mar 10 17:03:52 1999
+++ linux/fs/coda/inode.c	Tue Jun  8 10:47:58 1999
@@ -115,7 +115,7 @@
 	printk("coda_read_super: rootinode is %ld dev %d\n", 
 	       root->i_ino, root->i_dev);
 	sbi->sbi_root = root;
-	sb->s_root = d_alloc_root(root, NULL);
+	sb->s_root = d_alloc_root(root);
 	unlock_super(sb);
 	EXIT;  
         return sb;
@@ -145,7 +145,7 @@
         sb->s_dev = 0;
 	coda_cache_clear_all(sb);
 	sb_info = coda_sbp(sb);
-	sb_info->sbi_vcomm->vc_inuse = 0;
+/*	sb_info->sbi_vcomm->vc_inuse = 0; You can not do this: psdev_release would see usagecount == 0 and would refuse to decrease MOD_USE_COUNT --pavel */ 
 	coda_super_info.sbi_sb = NULL;
 	printk("Coda: Bye bye.\n");
 	memset(sb_info, 0, sizeof(* sb_info));
diff -u --recursive --new-file v2.3.5/linux/fs/dcache.c linux/fs/dcache.c
--- v2.3.5/linux/fs/dcache.c	Sun Apr 25 23:17:56 1999
+++ linux/fs/dcache.c	Tue Jun  8 10:47:58 1999
@@ -546,7 +546,7 @@
 	entry->d_inode = inode;
 }
 
-struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root)
+struct dentry * d_alloc_root(struct inode * root_inode)
 {
 	struct dentry *res = NULL;
 
diff -u --recursive --new-file v2.3.5/linux/fs/devpts/inode.c linux/fs/devpts/inode.c
--- v2.3.5/linux/fs/devpts/inode.c	Wed Jun  2 14:44:39 1999
+++ linux/fs/devpts/inode.c	Tue Jun  8 10:47:58 1999
@@ -163,7 +163,7 @@
 	 * Get the root inode and dentry, but defer checking for errors.
 	 */
 	root_inode = iget(s, 1); /* inode 1 == root directory */
-	root = d_alloc_root(root_inode, NULL);
+	root = d_alloc_root(root_inode);
 
 	/*
 	 * Check whether somebody else completed the super block.
diff -u --recursive --new-file v2.3.5/linux/fs/efs/super.c linux/fs/efs/super.c
--- v2.3.5/linux/fs/efs/super.c	Mon May 31 22:28:06 1999
+++ linux/fs/efs/super.c	Tue Jun  8 10:47:58 1999
@@ -204,7 +204,7 @@
 	}
 	s->s_op   = &efs_superblock_operations;
 	s->s_dev  = dev;
-	s->s_root = d_alloc_root(iget(s, EFS_ROOTINODE), NULL);
+	s->s_root = d_alloc_root(iget(s, EFS_ROOTINODE));
 	unlock_super(s);
  
 	if (!(s->s_root)) {
diff -u --recursive --new-file v2.3.5/linux/fs/ext2/super.c linux/fs/ext2/super.c
--- v2.3.5/linux/fs/ext2/super.c	Mon Apr 12 10:03:45 1999
+++ linux/fs/ext2/super.c	Tue Jun  8 10:47:58 1999
@@ -629,7 +629,7 @@
 	 */
 	sb->s_dev = dev;
 	sb->s_op = &ext2_sops;
-	sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO), NULL);
+	sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO));
 	if (!sb->s_root) {
 		sb->s_dev = 0;
 		for (i = 0; i < db_count; i++)
diff -u --recursive --new-file v2.3.5/linux/fs/ext2/symlink.c linux/fs/ext2/symlink.c
--- v2.3.5/linux/fs/ext2/symlink.c	Sat Sep 19 13:38:18 1998
+++ linux/fs/ext2/symlink.c	Wed Jun  9 20:46:53 1999
@@ -102,7 +102,6 @@
 		i++;
 	if (copy_to_user(buffer, link, i))
 		i = -EFAULT;
- 	UPDATE_ATIME(inode);
 	if (bh)
 		brelse (bh);
 	return i;
diff -u --recursive --new-file v2.3.5/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v2.3.5/linux/fs/fat/inode.c	Sat May 15 23:46:04 1999
+++ linux/fs/fat/inode.c	Tue Jun  8 10:47:58 1999
@@ -650,7 +650,7 @@
 	root_inode->i_ino = MSDOS_ROOT_INO;
 	fat_read_root(root_inode);
 	insert_inode_hash(root_inode);
-	sb->s_root = d_alloc_root(root_inode, NULL);
+	sb->s_root = d_alloc_root(root_inode);
 	if (!sb->s_root)
 		goto out_no_root;
 	if(i>=0) {
diff -u --recursive --new-file v2.3.5/linux/fs/fcntl.c linux/fs/fcntl.c
--- v2.3.5/linux/fs/fcntl.c	Fri Nov 13 10:07:26 1998
+++ linux/fs/fcntl.c	Mon Jun  7 12:10:22 1999
@@ -183,26 +183,15 @@
 	return err;
 }
 
-static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa)
+static void send_sigio_to_task(struct task_struct *p,
+			       struct fown_struct *fown, struct fasync_struct *fa)
 {
-	struct task_struct * p;
-	int   pid	= fown->pid;
-	uid_t uid	= fown->uid;
-	uid_t euid	= fown->euid;
-	
-	read_lock(&tasklist_lock);
-	for_each_task(p) {
-		int match = p->pid;
-		if (pid < 0)
-			match = -p->pgrp;
-		if (pid != match)
-			continue;
-		if ((euid != 0) &&
-		    (euid ^ p->suid) && (euid ^ p->uid) &&
-		    (uid ^ p->suid) && (uid ^ p->uid))
-			continue;
-		switch (fown->signum) {
-			siginfo_t si;
+	if ((fown->euid != 0) &&
+	    (fown->euid ^ p->suid) && (fown->euid ^ p->uid) &&
+	    (fown->uid ^ p->suid) && (fown->uid ^ p->uid))
+		return;
+	switch (fown->signum) {
+		siginfo_t si;
 		default:
 			/* Queue a rt signal with the appropriate fd as its
 			   value.  We use SI_SIGIO as the source, not 
@@ -213,16 +202,36 @@
 			si.si_signo = fown->signum;
 			si.si_errno = 0;
 		        si.si_code  = SI_SIGIO;
-			si.si_pid   = pid;
-			si.si_uid   = uid;
+			si.si_pid   = fown->pid;
+			si.si_uid   = fown->uid;
 			si.si_fd    = fa->fa_fd;
 			if (!send_sig_info(fown->signum, &si, p))
 				break;
 		/* fall-through: fall back on the old plain SIGIO signal */
 		case 0:
 			send_sig(SIGIO, p, 1);
-		}
 	}
+}
+
+static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa)
+{
+	struct task_struct * p;
+	int   pid	= fown->pid;
+	
+	read_lock(&tasklist_lock);
+	if ( (pid > 0) && (p = find_task_by_pid(pid)) ) {
+		send_sigio_to_task(p, fown, fa);
+		goto out;
+	}
+	for_each_task(p) {
+		int match = p->pid;
+		if (pid < 0)
+			match = -p->pgrp;
+		if (pid != match)
+			continue;
+		send_sigio_to_task(p, fown, fa);
+	}
+out:
 	read_unlock(&tasklist_lock);
 }
 
diff -u --recursive --new-file v2.3.5/linux/fs/hfs/bnode.c linux/fs/hfs/bnode.c
--- v2.3.5/linux/fs/hfs/bnode.c	Mon May 17 09:55:22 1999
+++ linux/fs/hfs/bnode.c	Fri Jun  4 13:30:25 1999
@@ -123,6 +123,8 @@
 	bnode->tree = tree;
 	bnode->node = node;
 	bnode->sticky = sticky;
+	hfs_init_waitqueue(&bnode->rqueue);
+	hfs_init_waitqueue(&bnode->wqueue);
 
 	if (sticky == HFS_NOT_STICKY) {
 		/* Insert it in the cache if appropriate */
diff -u --recursive --new-file v2.3.5/linux/fs/hfs/btree.c linux/fs/hfs/btree.c
--- v2.3.5/linux/fs/hfs/btree.c	Fri May 14 18:55:24 1999
+++ linux/fs/hfs/btree.c	Fri Jun  4 13:30:25 1999
@@ -173,7 +173,7 @@
 	bt->sys_mdb = mdb->sys_mdb;
 	bt->reserved = 0;
 	bt->lock = 0;
-	init_waitqueue_head(&bt->wait);
+	hfs_init_waitqueue(&bt->wait);
 	bt->dirt = 0;
 	memset(bt->cache, 0, sizeof(bt->cache));
 
diff -u --recursive --new-file v2.3.5/linux/fs/hfs/catalog.c linux/fs/hfs/catalog.c
--- v2.3.5/linux/fs/hfs/catalog.c	Mon May 17 09:55:22 1999
+++ linux/fs/hfs/catalog.c	Fri Jun  4 13:30:25 1999
@@ -302,6 +302,8 @@
 		entry->modify_date = hfs_get_nl(cat->u.dir.MdDat);
 		entry->backup_date = hfs_get_nl(cat->u.dir.BkDat);
 		dir->dirs = dir->files = 0;
+		hfs_init_waitqueue(&dir->read_wait);
+		hfs_init_waitqueue(&dir->write_wait);
 	} else if (cat->cdrType == HFS_CDR_FIL) {
 		struct hfs_file *fil = &entry->u.file;
 
diff -u --recursive --new-file v2.3.5/linux/fs/hfs/mdb.c linux/fs/hfs/mdb.c
--- v2.3.5/linux/fs/hfs/mdb.c	Wed Dec 23 14:10:36 1998
+++ linux/fs/hfs/mdb.c	Fri Jun  4 13:30:25 1999
@@ -99,7 +99,9 @@
 	memset(mdb, 0, sizeof(*mdb));
 	mdb->magic = HFS_MDB_MAGIC;
 	mdb->sys_mdb = sys_mdb;
-	INIT_LIST_HEAD(&mdb->entry_dirty);  
+	INIT_LIST_HEAD(&mdb->entry_dirty);
+	hfs_init_waitqueue(&mdb->rename_wait);
+	hfs_init_waitqueue(&mdb->bitmap_wait);
 
 	/* See if this is an HFS filesystem */
 	buf = hfs_buffer_get(sys_mdb, part_start + HFS_MDB_BLK, 1);
diff -u --recursive --new-file v2.3.5/linux/fs/hfs/super.c linux/fs/hfs/super.c
--- v2.3.5/linux/fs/hfs/super.c	Fri May 14 18:55:24 1999
+++ linux/fs/hfs/super.c	Tue Jun  8 10:47:58 1999
@@ -466,7 +466,7 @@
 	if (!root_inode) 
 		goto bail_no_root;
 	  
-	s->s_root = d_alloc_root(root_inode, NULL);
+	s->s_root = d_alloc_root(root_inode);
 	if (!s->s_root) 
 		goto bail_no_root;
 
diff -u --recursive --new-file v2.3.5/linux/fs/hpfs/alloc.c linux/fs/hpfs/alloc.c
--- v2.3.5/linux/fs/hpfs/alloc.c	Mon May 17 09:55:22 1999
+++ linux/fs/hpfs/alloc.c	Fri Jun  4 01:06:29 1999
@@ -164,8 +164,11 @@
 	if (near && near < s->s_hpfs_fs_size)
 		if ((sec = alloc_in_bmp(s, near, n, f_p ? forward : forward/4))) goto ret;
 	if (b != -1) {
-		if (b < 0x10000000) if ((sec = alloc_in_bmp(s, b<<14, n, f_p ? forward : forward/2))) goto ret;
-		else if ((sec = alloc_in_bmp(s, (b&0xfffffff)<<14, n, f_p ? forward : 0))) goto ret;
+		if ((sec = alloc_in_bmp(s, b<<14, n, f_p ? forward : forward/2))) {
+			b &= 0x0fffffff;
+			goto ret;
+		}
+		if (b > 0x10000000) if ((sec = alloc_in_bmp(s, (b&0xfffffff)<<14, n, f_p ? forward : 0))) goto ret;
 	}	
 	n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14;
 	for (i = 0; i < n_bmps / 2; i++) {
diff -u --recursive --new-file v2.3.5/linux/fs/hpfs/hpfs_fn.h linux/fs/hpfs/hpfs_fn.h
--- v2.3.5/linux/fs/hpfs/hpfs_fn.h	Mon May 17 09:55:22 1999
+++ linux/fs/hpfs/hpfs_fn.h	Fri Jun  4 01:06:29 1999
@@ -36,7 +36,7 @@
 #define ANODE_ALLOC_FWD	512
 #define FNODE_ALLOC_FWD	0
 #define ALLOC_FWD_MIN	16
-#define ALLOC_FWD_MAX	512
+#define ALLOC_FWD_MAX	128
 #define ALLOC_M		1
 #define FNODE_RD_AHEAD	16
 #define ANODE_RD_AHEAD	16
diff -u --recursive --new-file v2.3.5/linux/fs/hpfs/namei.c linux/fs/hpfs/namei.c
--- v2.3.5/linux/fs/hpfs/namei.c	Wed Jun  2 14:44:39 1999
+++ linux/fs/hpfs/namei.c	Fri Jun  4 01:06:29 1999
@@ -482,14 +482,9 @@
 	de.hidden = new_name[0] == '.';
 
 	if (new_inode) {
-		hpfs_brelse4(&qbh);
-		if ((nde = map_dirent(new_dir, new_dir->i_hpfs_dno, (char *)new_name, new_len, NULL, &qbh1))) {
-			int r;
-			if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) {
-				if (!(nde = map_dirent(new_dir, new_dir->i_hpfs_dno, (char *)new_name, new_len, NULL, &qbh1))) {
-					hpfs_error(new_dir->i_sb, "hpfs_rename: could not find dirent #2");
-					goto end1;
-				}
+		int r;
+		if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) {
+			if ((nde = map_dirent(new_dir, new_dir->i_hpfs_dno, (char *)new_name, new_len, NULL, &qbh1))) {
 				new_inode->i_nlink = 0;
 				copy_de(nde, &de);
 				memcpy(nde->name, new_name, new_len);
@@ -497,11 +492,11 @@
 				hpfs_brelse4(&qbh1);
 				goto end;
 			}
-			err = r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
+			hpfs_error(new_dir->i_sb, "hpfs_rename: could not find dirent");
+			err = -EFSERROR;
 			goto end1;
 		}
-		hpfs_error(new_dir->i_sb, "hpfs_rename: could not find dirent");
-		err = -EFSERROR;
+		err = r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
 		goto end1;
 	}
 
diff -u --recursive --new-file v2.3.5/linux/fs/hpfs/super.c linux/fs/hpfs/super.c
--- v2.3.5/linux/fs/hpfs/super.c	Wed Jun  2 14:44:39 1999
+++ linux/fs/hpfs/super.c	Tue Jun  8 10:47:58 1999
@@ -528,7 +528,7 @@
 	brelse(bh0);
 
 	hpfs_lock_iget(s, 1);
-	s->s_root = d_alloc_root(iget(s, s->s_hpfs_root), NULL);
+	s->s_root = d_alloc_root(iget(s, s->s_hpfs_root));
 	hpfs_unlock_iget(s);
 	unlock_super(s);
 	if (!s->s_root || !s->s_root->d_inode) {
diff -u --recursive --new-file v2.3.5/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.3.5/linux/fs/isofs/inode.c	Mon May 31 22:28:06 1999
+++ linux/fs/isofs/inode.c	Tue Jun  8 10:47:58 1999
@@ -835,7 +835,7 @@
 	if (!inode->i_op)
 		goto out_bad_root;
 	/* get the root dentry */
-	s->s_root = d_alloc_root(inode, NULL);
+	s->s_root = d_alloc_root(inode);
 	if (!(s->s_root))
 		goto out_no_root;
 
diff -u --recursive --new-file v2.3.5/linux/fs/minix/bitmap.c linux/fs/minix/bitmap.c
--- v2.3.5/linux/fs/minix/bitmap.c	Tue Oct 20 14:08:14 1998
+++ linux/fs/minix/bitmap.c	Tue Jun  8 10:47:58 1999
@@ -140,7 +140,7 @@
 	if (!ino || ino > inode->i_sb->u.minix_sb.s_ninodes) {
 		printk("Bad inode number on dev %s: %d is out of range\n",
 		       kdevname(inode->i_dev), ino);
-		return 0;
+		return NULL;
 	}
 	block = (2 + inode->i_sb->u.minix_sb.s_imap_blocks +
 		 inode->i_sb->u.minix_sb.s_zmap_blocks +
@@ -148,7 +148,7 @@
 	bh = bread(inode->i_dev, block, BLOCK_SIZE);
 	if (!bh) {
 		printk("unable to read i-node block\n");
-		return 0;
+		return NULL;
 	}
 	raw_inode = ((struct minix_inode *)bh->b_data +
 		     (ino - 1) % MINIX_INODES_PER_BLOCK);
@@ -168,7 +168,7 @@
 	if (!ino || ino > inode->i_sb->u.minix_sb.s_ninodes) {
 		printk("Bad inode number on dev %s: %d is out of range\n",
 		       kdevname(inode->i_dev), ino);
-		return 0;
+		return NULL;
 	}
 	block = (2 + inode->i_sb->u.minix_sb.s_imap_blocks +
 		 inode->i_sb->u.minix_sb.s_zmap_blocks +
@@ -176,7 +176,7 @@
 	bh = bread(inode->i_dev, block, BLOCK_SIZE);
 	if (!bh) {
 		printk("unable to read i-node block\n");
-		return 0;
+		return NULL;
 	}
 	raw_inode = ((struct minix2_inode *) bh->b_data +
 		     (ino - 1) % MINIX2_INODES_PER_BLOCK);
diff -u --recursive --new-file v2.3.5/linux/fs/minix/inode.c linux/fs/minix/inode.c
--- v2.3.5/linux/fs/minix/inode.c	Fri May 14 18:55:25 1999
+++ linux/fs/minix/inode.c	Tue Jun  8 10:47:58 1999
@@ -266,7 +266,7 @@
 	if (errmsg)
 		goto out_bad_root;
 
-	s->s_root = d_alloc_root(root_inode, NULL);
+	s->s_root = d_alloc_root(root_inode);
 	if (!s->s_root)
 		goto out_iput;
 
diff -u --recursive --new-file v2.3.5/linux/fs/minix/namei.c linux/fs/minix/namei.c
--- v2.3.5/linux/fs/minix/namei.c	Mon May 17 09:55:23 1999
+++ linux/fs/minix/namei.c	Tue Jun  8 10:47:58 1999
@@ -184,12 +184,7 @@
 			dir->i_size = block*bh->b_size + offset;
 			mark_inode_dirty(dir);
 		}
-		if (de->inode) {
-			if (namecompare(namelen, info->s_namelen, name, de->name)) {
-				brelse(bh);
-				return -EEXIST;
-			}
-		} else {
+		if (!de->inode) {
 			dir->i_mtime = dir->i_ctime = CURRENT_TIME;
 			mark_inode_dirty(dir);
 			for (i = 0; i < info->s_namelen ; i++)
diff -u --recursive --new-file v2.3.5/linux/fs/ncpfs/inode.c linux/fs/ncpfs/inode.c
--- v2.3.5/linux/fs/ncpfs/inode.c	Sat May 15 23:46:04 1999
+++ linux/fs/ncpfs/inode.c	Tue Jun  8 10:47:58 1999
@@ -431,7 +431,7 @@
         if (!root_inode)
 		goto out_no_root;
 	DPRINTK(KERN_DEBUG "ncp_read_super: root vol=%d\n", NCP_FINFO(root_inode)->volNumber);
-        server->root_dentry = sb->s_root = d_alloc_root(root_inode, NULL);
+        server->root_dentry = sb->s_root = d_alloc_root(root_inode);
         if (!sb->s_root)
 		goto out_no_root;
 	server->root_dentry->d_op = &ncp_dentry_operations;
diff -u --recursive --new-file v2.3.5/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.3.5/linux/fs/nfs/dir.c	Fri May 14 18:55:26 1999
+++ linux/fs/nfs/dir.c	Tue Jun  8 17:58:03 1999
@@ -14,8 +14,10 @@
  *              Following Linus comments on my original hack, this version
  *              depends only on the dcache stuff and doesn't touch the inode
  *              layer (iput() and friends).
+ *  6 Jun 1999	Cache readdir lookups in the page cache. -DaveM
  */
 
+#define NFS_NEED_XDR_TYPES
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/stat.h>
@@ -24,31 +26,16 @@
 #include <linux/kernel.h>
 #include <linux/malloc.h>
 #include <linux/mm.h>
-#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/clnt.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs.h>
+#include <linux/pagemap.h>
 
 #include <asm/segment.h>	/* for fs functions */
 
 #define NFS_PARANOIA 1
 /* #define NFS_DEBUG_VERBOSE 1 */
 
-/*
- * Head for a dircache entry. Currently still very simple; when
- * the cache grows larger, we will need a LRU list.
- */
-struct nfs_dirent {
-	dev_t			dev;		/* device number */
-	ino_t			ino;		/* inode number */
-	u32			cookie;		/* cookie of first entry */
-	unsigned short		valid  : 1,	/* data is valid */
-				locked : 1;	/* entry locked */
-	unsigned int		size;		/* # of entries */
-	unsigned long		age;		/* last used */
-	unsigned long		mtime;		/* last attr stamp */
-	wait_queue_head_t	wait;
-	__u32 *			entry;		/* three __u32's per entry */
-};
-
 static int nfs_safe_remove(struct dentry *);
 
 static ssize_t nfs_dir_read(struct file *, char *, size_t, loff_t *);
@@ -107,253 +94,326 @@
 	return -EISDIR;
 }
 
-static struct nfs_dirent	dircache[NFS_MAX_DIRCACHE];
+/* Each readdir response is composed of entries which look
+ * like the following, as per the NFSv2 RFC:
+ *
+ *	__u32	not_end			zero if end of response
+ *	__u32	file ID			opaque ino_t
+ *	__u32	namelen			size of name string
+ *	VAR	name string		the string, padded to modulo 4 bytes
+ *	__u32	cookie			opaque ID of next entry
+ *
+ * When you hit not_end being zero, the next __u32 is non-zero if
+ * this is the end of the complete set of readdir entires for this
+ * directory.  This can be used, for example, to initiate pre-fetch.
+ *
+ * In order to know what to ask the server for, we only need to know
+ * the final cookie of the previous page, and offset zero has cookie
+ * zero, so we cache cookie to page offset translations in chunks.
+ */
+#define COOKIES_PER_CHUNK (8 - ((sizeof(void *) / sizeof(__u32))))
+struct nfs_cookie_table {
+	struct nfs_cookie_table *next;
+	__u32	cookies[COOKIES_PER_CHUNK];
+};
+static kmem_cache_t *nfs_cookie_cachep;
 
-/*
- * We need to do caching of directory entries to prevent an
- * incredible amount of RPC traffic.  Only the most recent open
- * directory is cached.  This seems sufficient for most purposes.
- * Technically, we ought to flush the cache on close but this is
- * not a problem in practice.
+/* Since a cookie of zero is declared special by the NFS
+ * protocol, we easily can tell if a cookie in an existing
+ * table chunk is valid or not.
  *
- * XXX: Do proper directory caching by stuffing data into the
- * page cache (may require some fiddling for rsize < PAGE_SIZE).
+ * NOTE: The cookies are indexed off-by-one because zero
+ *       need not an entry.
  */
+static __inline__ __u32 *find_cookie(struct inode *inode, unsigned long off)
+{
+	static __u32 cookie_zero = 0;
+	struct nfs_cookie_table *p;
+	__u32 *ret;
+
+	if (!off)
+		return &cookie_zero;
+	off -= 1;
+	p = NFS_COOKIES(inode);
+	while(off >= COOKIES_PER_CHUNK && p) {
+		off -= COOKIES_PER_CHUNK;
+		p = p->next;
+	}
+	ret = NULL;
+	if (p) {
+		ret = &p->cookies[off];
+		if (!*ret)
+			ret = NULL;
+	}
+	return ret;
+}
 
-static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+/* Now we cache directories properly, by stuffing the dirent
+ * data directly in the page cache.
+ *
+ * Inode invalidation due to refresh etc. takes care of
+ * _everything_, no sloppy entry flushing logic, no extraneous
+ * copying, network direct to page cache, the way it was meant
+ * to be.
+ *
+ * NOTE: Dirent information verification is done always by the
+ *	 page-in of the RPC reply, nowhere else, this simplies
+ *	 things substantially.
+ */
+#define NFS_NAMELEN_ALIGN(__len) ((((__len)+3)>>2)<<2)
+static u32 find_midpoint(__u32 *p, u32 doff)
 {
-	struct dentry 		*dentry = filp->f_dentry;
-	struct inode 		*inode = dentry->d_inode;
-	static DECLARE_WAIT_QUEUE_HEAD(readdir_wait);
-	wait_queue_head_t	*waitp = NULL;
-	struct nfs_dirent	*cache, *free;
-	unsigned long		age, dead;
-	u32			cookie;
-	int			ismydir, result;
-	int			i, j, index = 0;
-	__u32			*entry;
-	char			*name, *start;
+	u32 walk = doff & PAGE_MASK;
 
-	dfprintk(VFS, "NFS: nfs_readdir(%s/%s)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name);
+	while(*p++ != 0) {
+		__u32 skip;
 
-	result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
-	if (result < 0)
-		goto out;
+		p++; /* skip fileid */
 
-	/*
-	 * Try to find the entry in the cache
-	 */
-again:
-	if (waitp) {
-		interruptible_sleep_on(waitp);
-		if (signal_pending(current))
-			return -ERESTARTSYS;
-		waitp = NULL;
+		/* Skip len, name, and cookie. */
+		skip = NFS_NAMELEN_ALIGN(*p++);
+		p += (skip >> 2) + 1;
+		walk += skip + (4 * sizeof(__u32));
+		if (walk >= doff)
+			break;
 	}
+	return walk;
+}
 
-	cookie = filp->f_pos;
-	entry  = NULL;
-	free   = NULL;
-	age    = ~(unsigned long) 0;
-	dead   = jiffies - NFS_ATTRTIMEO(inode);
+static int create_cookie(__u32 cookie, unsigned long off, struct inode *inode)
+{
+	struct nfs_cookie_table **cpp;
 
-	for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) {
-		/*
-		dprintk("NFS: dircache[%d] valid %d locked %d\n",
-					i, cache->valid, cache->locked);
-		 */
-		ismydir = (cache->dev == inode->i_dev
-				&& cache->ino == inode->i_ino);
-		if (cache->locked) {
-			if (!ismydir || cache->cookie != cookie)
-				continue;
-			dfprintk(DIRCACHE, "NFS: waiting on dircache entry\n");
-			waitp = &cache->wait;
-			goto again;
+	cpp = (struct nfs_cookie_table **) &NFS_COOKIES(inode);
+	while (off >= COOKIES_PER_CHUNK && *cpp) {
+		off -= COOKIES_PER_CHUNK;
+		cpp = &(*cpp)->next;
+	}
+	if (*cpp) {
+		(*cpp)->cookies[off] = cookie;
+	} else {
+		struct nfs_cookie_table *new;
+		int i;
+
+		new = kmem_cache_alloc(nfs_cookie_cachep, SLAB_ATOMIC);
+		if(!new)
+			return -1;
+		*cpp = new;
+		new->next = NULL;
+		for(i = 0; i < COOKIES_PER_CHUNK; i++) {
+			if (i == off) {
+				new->cookies[i] = cookie;
+			} else {
+				new->cookies[i] = 0;
+			}
 		}
+	}
+	return 0;
+}
 
-		if (ismydir && cache->mtime != inode->i_mtime)
-			cache->valid = 0;
-
-		if (!cache->valid || cache->age < dead) {
-			free = cache;
-			age  = 0;
-		} else if (cache->age < age) {
-			free = cache;
-			age  = cache->age;
-		}
+static struct page *try_to_get_dirent_page(struct file *, unsigned long, int);
 
-		if (!ismydir || !cache->valid)
-			continue;
+/* Recover from a revalidation flush.  The case here is that
+ * the inode for the directory got invalidated somehow, and
+ * all of our cached information is lost.  In order to get
+ * a correct cookie for the current readdir request from the
+ * user, we must (re-)fetch older readdir page cache entries.
+ */
+static int refetch_to_readdir_off(struct file *file, struct inode *inode, u32 off)
+{
+	u32 cur_off, goal_off = off & PAGE_MASK;
 
-		if (cache->cookie == cookie && cache->size > 0) {
-			entry = cache->entry + (index = 0);
-			cache->locked = 1;
-			break;
-		}
-		for (j = 0; j < cache->size; j++) {
-			__u32 *this_ent = cache->entry + j*3;
+again:
+	cur_off = 0;
+	while (cur_off < goal_off) {
+		struct page *page;
+
+		page = find_page(inode, cur_off);
+		if (page) {
+			if (PageLocked(page))
+				__wait_on_page(page);
+			if (!PageUptodate(page))
+				return -1;
+		} else {
+			page = try_to_get_dirent_page(file, cur_off, 0);
+			if (!page) {
+				if (!cur_off)
+					return -1;
 
-			if (*(this_ent+1) != cookie)
-				continue;
-			if (j < cache->size - 1) {
-				index = j + 1;
-				entry = this_ent + 3;
-			} else if (*(this_ent+2) & (1 << 15)) {
-				/* eof */
-				return 0;
+				/* Someone touched the dir on us. */
+				goto again;
 			}
-			break;
-		}
-		if (entry) {
-			dfprintk(DIRCACHE, "NFS: found dircache entry %d\n",
-						(int)(cache - dircache));
-			cache->locked = 1;
-			break;
-		}
-	}
-
-	/*
-	 * Okay, entry not present in cache, or locked and inaccessible.
-	 * Set up the cache entry and attempt a READDIR call.
-	 */
-	if (entry == NULL) {
-		if ((cache = free) == NULL) {
-			dfprintk(DIRCACHE, "NFS: dircache contention\n");
-			waitp = &readdir_wait;
-			goto again;
-		}
-		dfprintk(DIRCACHE, "NFS: using free dircache entry %d\n",
-				(int)(free - dircache));
-		cache->cookie = cookie;
-		cache->locked = 1;
-		cache->valid  = 0;
-		cache->dev    = inode->i_dev;
-		cache->ino    = inode->i_ino;
-		init_waitqueue_head(&cache->wait);
-		if (!cache->entry) {
-			result = -ENOMEM;
-			cache->entry = (__u32 *) get_free_page(GFP_KERNEL);
-			if (!cache->entry)
-				goto done;
+			page_cache_release(page);
 		}
 
-		result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(dentry),
-					cookie, PAGE_SIZE, cache->entry);
-		if (result <= 0)
-			goto done;
-		cache->size  = result;
-		cache->valid = 1;
-		entry = cache->entry + (index = 0);
+		cur_off += PAGE_SIZE;
 	}
-	cache->mtime = inode->i_mtime;
-	cache->age = jiffies;
 
-	/*
-	 * Yowza! We have a cache entry...
-	 */
-	start = (char *) cache->entry;
-	while (index < cache->size) {
-		__u32	fileid  = *entry++;
-		__u32	nextpos = *entry++; /* cookie */
-		__u32	length  = *entry++;
+	return 0;
+}
 
-		/*
-		 * Unpack the eof flag, offset, and length
-		 */
-		result = length & (1 << 15); /* eof flag */
-		name = start + ((length >> 16) & 0xFFFF);
-		length &= 0x7FFF;
-		/*
-		dprintk("NFS: filldir(%p, %.*s, %d, %d, %x, eof %x)\n", entry,
-				(int) length, name, length,
-				(unsigned int) filp->f_pos,
-				fileid, result);
-		 */
+static struct page *try_to_get_dirent_page(struct file *file, unsigned long offset, int refetch_ok)
+{
+	struct nfs_readdirargs rd_args;
+	struct nfs_readdirres rd_res;
+	struct dentry *dentry = file->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	struct page *page, **hash;
+	unsigned long page_cache;
+	__u32 *cookiep;
+
+	page = NULL;
+	page_cache = page_cache_alloc();
+	if (!page_cache)
+		goto out;
 
-		if (filldir(dirent, name, length, cookie, fileid) < 0)
-			break;
-		cookie = nextpos;
-		index++;
+	while ((cookiep = find_cookie(inode, offset)) == NULL) {
+		if (!refetch_ok ||
+		    refetch_to_readdir_off(file, inode, file->f_pos))
+			goto out;
 	}
-	filp->f_pos = cookie;
-	result = 0;
-
-	/* XXX: May want to kick async readdir-ahead here. Not too hard
-	 * to do. */
 
-done:
-	dfprintk(DIRCACHE, "NFS: nfs_readdir complete\n");
-	cache->locked = 0;
-	wake_up(&cache->wait);
-	wake_up(&readdir_wait);
+	hash = page_hash(inode, offset);
+	page = __find_page(inode, offset, *hash);
+	if (page) {
+		page_cache_free(page_cache);
+		goto out;
+	}
 
+	page = page_cache_entry(page_cache);
+	atomic_inc(&page->count);
+	page->flags = ((page->flags &
+			~((1 << PG_uptodate) | (1 << PG_error))) |
+		       ((1 << PG_referenced) | (1 << PG_locked)));
+	page->offset = offset;
+	add_page_to_inode_queue(inode, page);
+	__add_page_to_hash_queue(page, hash);
+
+	rd_args.fh = NFS_FH(dentry);
+	rd_res.buffer = (char *)page_cache;
+	rd_res.bufsiz = PAGE_CACHE_SIZE;
+	rd_res.cookie = *cookiep;
+	do {
+		rd_args.buffer = rd_res.buffer;
+		rd_args.bufsiz = rd_res.bufsiz;
+		rd_args.cookie = rd_res.cookie;
+		if (rpc_call(NFS_CLIENT(inode),
+			     NFSPROC_READDIR, &rd_args, &rd_res, 0) < 0)
+			goto error;
+	} while(rd_res.bufsiz > 0);
+
+	if (rd_res.bufsiz < 0)
+		NFS_DIREOF(inode) =
+			(offset << PAGE_CACHE_SHIFT) + -(rd_res.bufsiz);
+	else if (create_cookie(rd_res.cookie, offset, inode))
+		goto error;
+
+	set_bit(PG_uptodate, &page->flags);
+unlock_out:
+	clear_bit(PG_locked, &page->flags);
+	wake_up(&page->wait);
 out:
-	return result;
+	return page;
+
+error:
+	set_bit(PG_error, &page->flags);
+	goto unlock_out;
 }
 
-/*
- * Invalidate dircache entries for an inode.
- */
-void
-nfs_invalidate_dircache(struct inode *inode)
+static __inline__ u32 nfs_do_filldir(__u32 *p, u32 doff,
+				     void *dirent, filldir_t filldir)
 {
-	struct nfs_dirent *cache = dircache;
-	dev_t		dev = inode->i_dev;
-	ino_t		ino = inode->i_ino;
-	int		i;
-
-	dfprintk(DIRCACHE, "NFS: invalidate dircache for %x/%ld\n", dev, (long)ino);
-	for (i = NFS_MAX_DIRCACHE; i--; cache++) {
-		if (cache->ino != ino)
-			continue;
-		if (cache->dev != dev)
-			continue;
-		if (cache->locked) {
-			printk("NFS: cache locked for %s/%ld\n",
-				kdevname(dev), (long) ino);
-			continue;
-		}
-		cache->valid = 0;	/* brute force */
+	u32 end;
+
+	if (doff & ~PAGE_CACHE_MASK) {
+		doff = find_midpoint(p, doff);
+		p += (doff & ~PAGE_CACHE_MASK) >> 2;
+	}
+	while((end = *p++) != 0) {
+		__u32 fileid = *p++;
+		__u32 len = *p++;
+		__u32 skip = NFS_NAMELEN_ALIGN(len);
+		char *name = (char *) p;
+
+		/* Skip the cookie. */
+		p = ((__u32 *) (name + skip)) + 1;
+		if (filldir(dirent, name, len, doff, fileid) < 0)
+			goto out;
+		doff += (skip + (4 * sizeof(__u32)));
 	}
+	if (!*p)
+		doff = PAGE_CACHE_ALIGN(doff);
+out:
+	return doff;
 }
 
-/*
- * Invalidate the dircache for a super block (or all caches),
- * and release the cache memory.
+/* The file offset position is represented in pure bytes, to
+ * make the page cache interface straight forward.
+ *
+ * However, some way is needed to make the connection between the
+ * opaque NFS directory entry cookies and our offsets, so a per-inode
+ * cookie cache table is used.
  */
-void
-nfs_invalidate_dircache_sb(struct super_block *sb)
+static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
-	struct nfs_dirent *cache = dircache;
-	int		i;
+	struct dentry *dentry = filp->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	struct page *page, **hash;
+	unsigned long offset;
+	int res;
+
+	res = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+	if (res < 0)
+		return res;
+
+	if (NFS_DIREOF(inode) && filp->f_pos >= NFS_DIREOF(inode))
+		return 0;
+
+	offset = filp->f_pos >> PAGE_CACHE_SHIFT;
+	hash = page_hash(inode, offset);
+	page = __find_page(inode, offset, *hash);
+	if (!page)
+		goto no_dirent_page;
+	if (PageLocked(page))
+		goto dirent_locked_wait;
+	if (!PageUptodate(page))
+		goto dirent_read_error;
+success:
+	filp->f_pos = nfs_do_filldir((__u32 *) page_address(page),
+				     filp->f_pos, dirent, filldir);
+	page_cache_release(page);
+	return 0;
 
-	for (i = NFS_MAX_DIRCACHE; i--; cache++) {
-		if (sb && sb->s_dev != cache->dev)
-			continue;
-		if (cache->locked) {
-			printk("NFS: cache locked at umount %s\n",
-				(cache->entry ? "(lost a page!)" : ""));
-			continue;
-		}
-		cache->valid = 0;	/* brute force */
-		if (cache->entry) {
-			free_page((unsigned long) cache->entry);
-			cache->entry = NULL;
-		}
-	}
+no_dirent_page:
+	page = try_to_get_dirent_page(filp, offset, 1);
+	if (!page)
+		goto no_page;
+
+dirent_locked_wait:
+	wait_on_page(page);
+	if (PageUptodate(page))
+		goto success;
+dirent_read_error:
+	page_cache_release(page);
+no_page:
+	return -EIO;
 }
 
-/*
- * Free directory cache memory
- * Called from cleanup_module
+/* Invalidate directory cookie caches and EOF marker
+ * for an inode.
  */
-void
-nfs_free_dircache(void)
+__inline__ void nfs_invalidate_dircache(struct inode *inode)
 {
-	dfprintk(DIRCACHE, "NFS: freeing dircache\n");
-	nfs_invalidate_dircache_sb(NULL);
+	struct nfs_cookie_table *p = NFS_COOKIES(inode);
+
+	if (p != NULL) {
+		NFS_COOKIES(inode) = NULL;
+		do {	struct nfs_cookie_table *next = p->next;
+			kmem_cache_free(nfs_cookie_cachep, p);
+			p = next;
+		} while (p != NULL);
+	}
+	NFS_DIREOF(inode) = 0;
 }
 
 /*
@@ -475,10 +535,15 @@
 out_valid:
 	return 1;
 out_bad:
-	if (dentry->d_parent->d_inode)
+	/* Purge readdir caches. */
+	if (dentry->d_parent->d_inode) {
+		invalidate_inode_pages(dentry->d_parent->d_inode);
 		nfs_invalidate_dircache(dentry->d_parent->d_inode);
-	if (inode && S_ISDIR(inode->i_mode))
+	}
+	if (inode && S_ISDIR(inode->i_mode)) {
+		invalidate_inode_pages(inode);
 		nfs_invalidate_dircache(inode);
+	}
 	return 0;
 }
 
@@ -522,13 +587,25 @@
 #endif
 }
 
+static kmem_cache_t *nfs_fh_cachep;
+
+__inline__ struct nfs_fh *nfs_fh_alloc(void)
+{
+	return kmem_cache_alloc(nfs_fh_cachep, SLAB_KERNEL);
+}
+
+__inline__ void nfs_fh_free(struct nfs_fh *p)
+{
+	kmem_cache_free(nfs_fh_cachep, p);
+}
+
 /*
  * Called when the dentry is being freed to release private memory.
  */
 static void nfs_dentry_release(struct dentry *dentry)
 {
 	if (dentry->d_fsdata)
-		kfree(dentry->d_fsdata);
+		nfs_fh_free(dentry->d_fsdata);
 }
 
 struct dentry_operations nfs_dentry_operations = {
@@ -579,7 +656,7 @@
 
 	error = -ENOMEM;
 	if (!dentry->d_fsdata) {
-		dentry->d_fsdata = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+		dentry->d_fsdata = nfs_fh_alloc();
 		if (!dentry->d_fsdata)
 			goto out;
 	}
@@ -661,6 +738,7 @@
 	/*
 	 * Invalidate the dir cache before the operation to avoid a race.
 	 */
+	invalidate_inode_pages(dir);
 	nfs_invalidate_dircache(dir);
 	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 			dentry->d_name.name, &sattr, &fhandle, &fattr);
@@ -690,6 +768,7 @@
 		sattr.size = rdev; /* get out your barf bag */
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
 
+	invalidate_inode_pages(dir);
 	nfs_invalidate_dircache(dir);
 	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 				dentry->d_name.name, &sattr, &fhandle, &fattr);
@@ -724,6 +803,7 @@
 	 * depending on potentially bogus information.
 	 */
 	d_drop(dentry);
+	invalidate_inode_pages(dir);
 	nfs_invalidate_dircache(dir);
 	error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent),
 				dentry->d_name.name, &sattr, &fhandle, &fattr);
@@ -744,6 +824,7 @@
 dentry->d_inode->i_count, dentry->d_inode->i_nlink);
 #endif
 
+	invalidate_inode_pages(dir);
 	nfs_invalidate_dircache(dir);
 	error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 				dentry->d_name.name);
@@ -871,6 +952,7 @@
 			goto out;
 	} while(sdentry->d_inode != NULL); /* need negative lookup */
 
+	invalidate_inode_pages(dir);
 	nfs_invalidate_dircache(dir);
 	error = nfs_proc_rename(NFS_SERVER(dir),
 				NFS_FH(dentry->d_parent), dentry->d_name.name,
@@ -940,6 +1022,7 @@
 			inode->i_nlink --;
 		d_delete(dentry);
 	}
+	invalidate_inode_pages(dir);
 	nfs_invalidate_dircache(dir);
 	error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 				dentry->d_name.name);
@@ -1006,6 +1089,7 @@
 	 * can't instantiate the new inode.
 	 */
 	d_drop(dentry);
+	invalidate_inode_pages(dir);
 	nfs_invalidate_dircache(dir);
 	error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 				dentry->d_name.name, symname, &sattr);
@@ -1036,6 +1120,7 @@
 	 * we can't use the existing dentry.
 	 */
 	d_drop(dentry);
+	invalidate_inode_pages(dir);
 	nfs_invalidate_dircache(dir);
 	error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry),
 				NFS_FH(dentry->d_parent), dentry->d_name.name);
@@ -1181,7 +1266,9 @@
 		d_delete(new_dentry);
 	}
 
+	invalidate_inode_pages(new_dir);
 	nfs_invalidate_dircache(new_dir);
+	invalidate_inode_pages(old_dir);
 	nfs_invalidate_dircache(old_dir);
 	error = nfs_proc_rename(NFS_DSERVER(old_dentry),
 			NFS_FH(old_dentry->d_parent), old_dentry->d_name.name,
@@ -1199,6 +1286,25 @@
 	if (dentry)
 		dput(dentry);
 	return error;
+}
+
+int nfs_init_fhcache(void)
+{
+	nfs_fh_cachep = kmem_cache_create("nfs_fh",
+					  sizeof(struct nfs_fh),
+					  0, SLAB_HWCACHE_ALIGN,
+					  NULL, NULL);
+	if (nfs_fh_cachep == NULL)
+		return -ENOMEM;
+
+	nfs_cookie_cachep = kmem_cache_create("nfs_dcookie",
+					      sizeof(struct nfs_cookie_table),
+					      0, SLAB_HWCACHE_ALIGN,
+					      NULL, NULL);
+	if (nfs_cookie_cachep == NULL)
+		return -ENOMEM;
+
+	return 0;
 }
 
 /*
diff -u --recursive --new-file v2.3.5/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v2.3.5/linux/fs/nfs/inode.c	Fri May 14 18:55:26 1999
+++ linux/fs/nfs/inode.c	Tue Jun  8 17:58:03 1999
@@ -137,10 +137,6 @@
 	if (!(server->flags & NFS_MOUNT_NONLM))
 		lockd_down();	/* release rpc.lockd */
 	rpciod_down();		/* release rpciod */
-	/*
-	 * Invalidate the dircache for this superblock.
-	 */
-	nfs_invalidate_dircache_sb(sb);
 
 	kfree(server->hostname);
 
@@ -185,6 +181,9 @@
 	return bsize;
 }
 
+extern struct nfs_fh *nfs_fh_alloc(void);
+extern void nfs_fh_free(struct nfs_fh *p);
+
 /*
  * The way this works is that the mount process passes a structure
  * in the data argument which contains the server's IP address
@@ -291,7 +290,7 @@
 	 * Keep the super block locked while we try to get 
 	 * the root fh attributes.
 	 */
-	root_fh = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+	root_fh = nfs_fh_alloc();
 	if (!root_fh)
 		goto out_no_fh;
 	*root_fh = data->root;
@@ -302,7 +301,7 @@
 	root_inode = __nfs_fhget(sb, &fattr);
 	if (!root_inode)
 		goto out_no_root;
-	sb->s_root = d_alloc_root(root_inode, NULL);
+	sb->s_root = d_alloc_root(root_inode);
 	if (!sb->s_root)
 		goto out_no_root;
 	sb->s_root->d_op = &nfs_dentry_operations;
@@ -325,7 +324,7 @@
 out_no_fattr:
 	printk("nfs_read_super: get root fattr failed\n");
 out_free_fh:
-	kfree(root_fh);
+	nfs_fh_free(root_fh);
 out_no_fh:
 	rpciod_down();
 	goto out_shutdown;
@@ -432,10 +431,9 @@
 	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
 	NFS_CACHEINV(inode);
 
+	invalidate_inode_pages(inode);
 	if (S_ISDIR(inode->i_mode))
 		nfs_invalidate_dircache(inode);
-	else
-		invalidate_inode_pages(inode);
 }
 
 /*
@@ -479,6 +477,8 @@
 		inode->i_size  = fattr->size;
 		inode->i_mtime = fattr->mtime.seconds;
 		NFS_OLDMTIME(inode) = fattr->mtime.seconds;
+		NFS_COOKIES(inode) = NULL;
+		NFS_WRITEBACK(inode) = NULL;
 	}
 	nfs_refresh_inode(inode, fattr);
 }
@@ -881,12 +881,25 @@
 	NULL
 };
 
+extern int nfs_init_fhcache(void);
+extern int nfs_init_wreqcache(void);
+
 /*
  * Initialize NFS
  */
 int
 init_nfs_fs(void)
 {
+	int err;
+
+	err = nfs_init_fhcache();
+	if (err)
+		return err;
+
+	err = nfs_init_wreqcache();
+	if (err)
+		return err;
+
 #ifdef CONFIG_PROC_FS
 	rpc_register_sysctl();
 	rpc_proc_init();
@@ -917,6 +930,5 @@
 	rpc_proc_unregister("nfs");
 #endif
 	unregister_filesystem(&nfs_fs_type);
-	nfs_free_dircache();
 }
 #endif
diff -u --recursive --new-file v2.3.5/linux/fs/nfs/nfs2xdr.c linux/fs/nfs/nfs2xdr.c
--- v2.3.5/linux/fs/nfs/nfs2xdr.c	Sat Mar  6 14:21:13 1999
+++ linux/fs/nfs/nfs2xdr.c	Tue Jun  8 22:11:58 1999
@@ -56,11 +56,12 @@
 #define NFS_linkargs_sz		NFS_fhandle_sz+NFS_diropargs_sz
 #define NFS_symlinkargs_sz	NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz
 #define NFS_readdirargs_sz	NFS_fhandle_sz+2
+#define NFS_readlinkargs_sz	NFS_fhandle_sz
 
 #define NFS_dec_void_sz		0
 #define NFS_attrstat_sz		1+NFS_fattr_sz
 #define NFS_diropres_sz		1+NFS_fhandle_sz+NFS_fattr_sz
-#define NFS_readlinkres_sz	1+NFS_path_sz
+#define NFS_readlinkres_sz	1
 #define NFS_readres_sz		1+NFS_fattr_sz+1
 #define NFS_stat_sz		1
 #define NFS_readdirres_sz	1
@@ -198,7 +199,6 @@
 	*p++ = htonl(args->count);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
-#if 1
 	/* set up reply iovec */
 	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
 	buflen = req->rq_rvec[0].iov_len;
@@ -209,10 +209,6 @@
 	req->rq_rvec[2].iov_len  = buflen - replen;
 	req->rq_rlen = args->count + buflen;
 	req->rq_rnr = 3;
-#else
-	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
-	req->rq_rvec[0].iov_len  = replen;
-#endif
 
 	return 0;
 }
@@ -359,133 +355,107 @@
 {
 	struct rpc_task	*task = req->rq_task;
 	struct rpc_auth	*auth = task->tk_auth;
-	u32		bufsiz = args->bufsiz;
+	int		bufsiz = args->bufsiz;
 	int		replen;
 
-	/*
-	 * Some servers (e.g. HP OS 9.5) seem to expect the buffer size
+	p = xdr_encode_fhandle(p, args->fh);
+	*p++ = htonl(args->cookie);
+
+	/* Some servers (e.g. HP OS 9.5) seem to expect the buffer size
 	 * to be in longwords ... check whether to convert the size.
 	 */
 	if (task->tk_client->cl_flags & NFS_CLNTF_BUFSIZE)
-		bufsiz = bufsiz >> 2;
+		*p++ = htonl(bufsiz >> 2);
+	else
+		*p++ = htonl(bufsiz);
 
-	p = xdr_encode_fhandle(p, args->fh);
-	*p++ = htonl(args->cookie);
-	*p++ = htonl(bufsiz); /* see above */
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
 	/* set up reply iovec */
 	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
-	/*
-	dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n",
-		RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen);
-	 */
 	req->rq_rvec[0].iov_len  = replen;
 	req->rq_rvec[1].iov_base = args->buffer;
-	req->rq_rvec[1].iov_len  = args->bufsiz;
-	req->rq_rlen = replen + args->bufsiz;
+	req->rq_rvec[1].iov_len  = bufsiz;
+	req->rq_rlen = replen + bufsiz;
 	req->rq_rnr = 2;
 
-	/*
-	dprintk("RPC:      readdirargs set up reply vec:\n");
-	dprintk("          rvec[0] = %p/%d\n",
-			req->rq_rvec[0].iov_base,
-			req->rq_rvec[0].iov_len);
-	dprintk("          rvec[1] = %p/%d\n",
-			req->rq_rvec[1].iov_base,
-			req->rq_rvec[1].iov_len);
-	 */
-
 	return 0;
 }
 
 /*
- * Decode the result of a readdir call. We decode the result in place
- * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
- * After decoding, the layout in memory looks like this:
- *	entry1 entry2 ... entryN <space> stringN ... string2 string1
- * Each entry consists of three __u32 values, the same space as NFS uses.
- * Note that the strings are not null-terminated so that the entire number
- * of entries returned by the server should fit into the buffer.
+ * Decode the result of a readdir call.
  */
+#define NFS_DIRENT_MAXLEN	(5 * sizeof(u32) + (NFS_MAXNAMLEN + 1))
 static int
 nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
 {
 	struct iovec		*iov = req->rq_rvec;
 	int			 status, nr;
-	char			*string, *start;
-	u32			*end, *entry, len, fileid, cookie;
+	u32			*end;
+	u32			last_cookie = res->cookie;
 
-	if ((status = ntohl(*p++)))
-		return -nfs_stat_to_errno(status);
+	status = ntohl(*p++);
+	if (status) {
+		nr = -nfs_stat_to_errno(status);
+		goto error;
+	}
 	if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
 		/* Unexpected reply header size. Punt. */
 		printk("NFS: Odd RPC header size in readdirres reply\n");
-		return -errno_NFSERR_IO;
+		nr = -errno_NFSERR_IO;
+		goto error;
 	}
 
-	/* Get start and end address of XDR data */
+	/* Get start and end address of XDR readdir response. */
 	p   = (u32 *) iov[1].iov_base;
 	end = (u32 *) ((u8 *) p + iov[1].iov_len);
-
-	/* Get start and end of dirent buffer */
-	entry  = (u32 *) res->buffer;
-	start  = (char *) res->buffer;
-	string = (char *) res->buffer + res->bufsiz;
 	for (nr = 0; *p++; nr++) {
-		fileid = ntohl(*p++);
+		__u32 len;
+
+		/* Convert fileid. */
+		*p = ntohl(*p);
+		p++;
+
+		/* Convert and capture len */
+		len = *p = ntohl(*p);
+		p++;
 
-		len = ntohl(*p++);
-		/*
-		 * Check whether the server has exceeded our reply buffer,
-		 * and set a flag to convert the size to longwords.
-		 */
 		if ((p + QUADLEN(len) + 3) > end) {
 			struct rpc_clnt *clnt = req->rq_task->tk_client;
-			printk(KERN_WARNING
-				"NFS: server %s, readdir reply truncated\n",
-				clnt->cl_server);
-			printk(KERN_WARNING "NFS: nr=%d, slots=%d, len=%d\n",
-				nr, (end - p), len);
+
 			clnt->cl_flags |= NFS_CLNTF_BUFSIZE;
+			p -= 2;
+			p[-1] = 0;
+			p[0] = 0;
 			break;
 		}
 		if (len > NFS_MAXNAMLEN) {
-			printk("NFS: giant filename in readdir (len %x)!\n",
-						len);
-			return -errno_NFSERR_IO;
+			nr = -errno_NFSERR_IO;
+			goto error;
 		}
-		string -= len;
-		if ((void *) (entry+3) > (void *) string) {
-			/* 
-			 * This error is impossible as long as the temp
-			 * buffer is no larger than the user buffer. The 
-			 * current packing algorithm uses the same amount
-			 * of space in the user buffer as in the XDR data,
-			 * so it's guaranteed to fit.
-			 */
-			printk("NFS: incorrect buffer size in %s!\n",
-				__FUNCTION__);
-			break;
-		}
-
-		memmove(string, p, len);
 		p += QUADLEN(len);
-		cookie = ntohl(*p++);
-		/*
-		 * To make everything fit, we encode the length, offset,
-		 * and eof flag into 32 bits. This works for filenames
-		 * up to 32K and PAGE_SIZE up to 64K.
-		 */
-		status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */
-		*entry++ = fileid;
-		*entry++ = cookie;
-		*entry++ = ((string - start) << 16) | status | (len & 0x7FFF);
+
+		/* Convert and capture cookie. */
+		last_cookie = *p = ntohl(*p);
+		p++;
 	}
-#ifdef NFS_PARANOIA
-printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n",
-nr, ((char *) entry - start), (start + res->bufsiz - string));
-#endif
+	p -= 1;
+	status = ((end - p) << 2);
+	if (!p[1] && (status >= NFS_DIRENT_MAXLEN)) {
+		status = ((__u8 *)p - (__u8 *)iov[1].iov_base);
+		res->buffer += status;
+		res->bufsiz -= status;
+	} else if (p[1]) {
+		status = (int)((long)p & ~PAGE_CACHE_MASK);
+		res->bufsiz = -status;
+	} else {
+		res->bufsiz = 0;
+	}
+	res->cookie = last_cookie;
+	return nr;
+
+error:
+	res->bufsiz = 0;
 	return nr;
 }
 
@@ -553,20 +523,56 @@
 }
 
 /*
+ * Encode arguments to readlink call
+ */
+static int nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args)
+{
+	struct rpc_task *task = req->rq_task;
+	struct rpc_auth *auth = task->tk_auth;
+	int bufsiz = NFS_MAXPATHLEN;
+	int replen;
+
+	p = xdr_encode_fhandle(p, args->fh);
+	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2;
+	req->rq_rvec[0].iov_len = replen;
+	req->rq_rvec[1].iov_base = (void *) args->buffer;
+	req->rq_rvec[1].iov_len = bufsiz;
+	req->rq_rlen = replen + bufsiz;
+	req->rq_rnr = 2;
+
+	return 0;
+}
+
+/*
  * Decode READLINK reply
  */
 static int
-nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
+nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, void *dummy)
 {
-	int	status;
+	struct iovec	*iov = req->rq_rvec;
+	int		status,	len;
+	char		*name;
 
-	if ((status = ntohl(*p++)))
+	/* Verify OK status. */
+	if ((status = ntohl(*p++)) != 0)
 		return -nfs_stat_to_errno(status);
-	xdr_decode_string2(p, res->string, res->lenp, res->maxlen);
 
-	/* Caller takes over the buffer here to avoid extra copy */
-	res->buffer = req->rq_task->tk_buffer;
-	req->rq_task->tk_buffer = NULL;
+	/* Verify OK response length. */
+	if ((__u8 *)p != ((u8 *) iov->iov_base + iov->iov_len))
+		return -errno_NFSERR_IO;
+
+	/* Convert and verify that string length is in range. */
+	p = iov[1].iov_base;
+	len = *p = ntohl(*p);
+	p++;
+	if (len > iov[1].iov_len)
+		return -errno_NFSERR_IO;
+
+	/* NULL terminate the string we got. */
+	name = (char *) p;
+	name[len] = 0;
+
 	return 0;
 }
 
@@ -653,7 +659,7 @@
     PROC(setattr,	sattrargs,	attrstat),
     PROC(root,		enc_void,	dec_void),
     PROC(lookup,	diropargs,	diropres),
-    PROC(readlink,	fhandle,	readlinkres),
+    PROC(readlink,	readlinkargs,	readlinkres),
     PROC(read,		readargs,	readres),
     PROC(writecache,	enc_void,	dec_void),
     PROC(write,		writeargs,	attrstat),
diff -u --recursive --new-file v2.3.5/linux/fs/nfs/proc.c linux/fs/nfs/proc.c
--- v2.3.5/linux/fs/nfs/proc.c	Tue Jan 13 10:03:40 1998
+++ linux/fs/nfs/proc.c	Tue Jun  8 22:11:58 1999
@@ -91,24 +91,6 @@
 }
 
 int
-nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
-			void **p0, char **string, unsigned int *len,
-			unsigned int maxlen)
-{
-	struct nfs_readlinkres	res = { string, len, maxlen, NULL };
-	int			status;
-
-	dprintk("NFS call  readlink\n");
-	status = rpc_call(server->client, NFSPROC_READLINK, fhandle, &res, 0);
-	dprintk("NFS reply readlink: %d\n", status);
-	if (!status)
-		*p0 = res.buffer;
-	else if (res.buffer)
-		kfree(res.buffer);
-	return status;
-}
-
-int
 nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle, int swap,
 			  unsigned long offset, unsigned int count,
 			  void *buffer, struct nfs_fattr *fattr)
@@ -231,61 +213,6 @@
 	dprintk("NFS call  rmdir %s\n", name);
 	status = rpc_call(server->client, NFSPROC_RMDIR, &arg, NULL, 0);
 	dprintk("NFS reply rmdir: %d\n", status);
-	return status;
-}
-
-/*
- * The READDIR implementation is somewhat hackish - we pass a temporary
- * buffer to the encode function, which installs it in the receive
- * iovec. The dirent buffer itself is passed in the result struct.
- */
-int
-nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
-			u32 cookie, unsigned int size, __u32 *entry)
-{
-	struct nfs_readdirargs	arg;
-	struct nfs_readdirres	res;
-	void *			buffer;
-	unsigned int		buf_size = PAGE_SIZE;
-	int			status;
-
-	/* First get a temp buffer for the readdir reply */
-	/* N.B. does this really need to be cleared? */
-	status = -ENOMEM;
-	buffer = (void *) get_free_page(GFP_KERNEL);
-	if (!buffer)
-		goto out;
-
-	/*
-	 * Calculate the effective size the buffer.  To make sure
-	 * that the returned data will fit into the user's buffer,
-	 * we decrease the buffer size as necessary.
-	 *
-	 * Note: NFS returns three __u32 values for each entry,
-	 * and we assume that the data is packed into the user
-	 * buffer with the same efficiency. 
-	 */
-	if (size < buf_size)
-		buf_size = size;
-	if (server->rsize < buf_size)
-		buf_size = server->rsize;
-#if 0
-printk("nfs_proc_readdir: user size=%d, rsize=%d, buf_size=%d\n",
-size, server->rsize, buf_size);
-#endif
-
-	arg.fh = fhandle;
-	arg.cookie = cookie;
-	arg.buffer = buffer;
-	arg.bufsiz = buf_size;
-	res.buffer = entry;
-	res.bufsiz = size;
-
-	dprintk("NFS call  readdir %d\n", cookie);
-	status = rpc_call(server->client, NFSPROC_READDIR, &arg, &res, 0);
-	dprintk("NFS reply readdir: %d\n", status);
-	free_page((unsigned long) buffer);
-out:
 	return status;
 }
 
diff -u --recursive --new-file v2.3.5/linux/fs/nfs/read.c linux/fs/nfs/read.c
--- v2.3.5/linux/fs/nfs/read.c	Tue Dec 22 09:37:43 1998
+++ linux/fs/nfs/read.c	Mon Jun  7 13:25:49 1999
@@ -257,6 +257,7 @@
 
 out_error:
 	clear_bit(PG_locked, &page->flags);
+	wake_up(&page->wait);
 out_free:
 	free_page(page_address(page));
 out:
diff -u --recursive --new-file v2.3.5/linux/fs/nfs/symlink.c linux/fs/nfs/symlink.c
--- v2.3.5/linux/fs/nfs/symlink.c	Mon Apr 12 09:57:11 1999
+++ linux/fs/nfs/symlink.c	Tue Jun  8 22:11:58 1999
@@ -5,12 +5,18 @@
  *
  *  Optimization changes Copyright (C) 1994 Florian La Roche
  *
+ *  Jun 7 1999, cache symlink lookups in the page cache.  -DaveM
+ *
  *  nfs symlink handling code
  */
 
+#define NFS_NEED_XDR_TYPES
 #include <linux/sched.h>
 #include <linux/errno.h>
+#include <linux/sunrpc/clnt.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs.h>
+#include <linux/pagemap.h>
 #include <linux/stat.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
@@ -44,63 +50,128 @@
 	NULL			/* permission */
 };
 
-static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
+/* Symlink caching in the page cache is even more simplistic
+ * and straight-forward than readdir caching.
+ */
+static struct page *try_to_get_symlink_page(struct dentry *dentry, struct inode *inode)
 {
-	int error;
-	unsigned int len;
-	char *res;
-	void *mem;
-
-	dfprintk(VFS, "nfs: readlink(%s/%s)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name);
-
-	error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
-					&mem, &res, &len, NFS_MAXPATHLEN);
-	if (! error) {
-		if (len > buflen)
-			len = buflen;
-		copy_to_user(buffer, res, len);
-		error = len;
-		kfree(mem);
+	struct nfs_readlinkargs rl_args;
+	struct page *page, **hash;
+	unsigned long page_cache;
+
+	page = NULL;
+	page_cache = page_cache_alloc();
+	if (!page_cache)
+		goto out;
+
+	hash = page_hash(inode, 0);
+	page = __find_page(inode, 0, *hash);
+	if (page) {
+		page_cache_free(page_cache);
+		goto out;
 	}
-	return error;
+
+	page = page_cache_entry(page_cache);
+	atomic_inc(&page->count);
+	page->flags = ((page->flags &
+			~((1 << PG_uptodate) | (1 << PG_error))) |
+		       ((1 << PG_referenced) | (1 << PG_locked)));
+	page->offset = 0;
+	add_page_to_inode_queue(inode, page);
+	__add_page_to_hash_queue(page, hash);
+
+	/* We place the length at the beginning of the page,
+	 * in host byte order, followed by the string.  The
+	 * XDR response verification will NULL terminate it.
+	 */
+	rl_args.fh = NFS_FH(dentry);
+	rl_args.buffer = (const void *)page_cache;
+	if (rpc_call(NFS_CLIENT(inode), NFSPROC_READLINK,
+		     &rl_args, NULL, 0) < 0)
+		goto error;
+	set_bit(PG_uptodate, &page->flags);
+unlock_out:
+	clear_bit(PG_locked, &page->flags);
+	wake_up(&page->wait);
+out:
+	return page;
+
+error:
+	set_bit(PG_error, &page->flags);
+	goto unlock_out;
+}
+
+static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+	struct inode *inode = dentry->d_inode;
+	struct page *page, **hash;
+	u32 *p, len;
+
+	/* Caller revalidated the directory inode already. */
+	hash = page_hash(inode, 0);
+	page = __find_page(inode, 0, *hash);
+	if (!page)
+		goto no_readlink_page;
+	if (PageLocked(page))
+		goto readlink_locked_wait;
+	if (!PageUptodate(page))
+		goto readlink_read_error;
+success:
+	p = (u32 *) page_address(page);
+	len = *p++;
+	if (len > buflen)
+		len = buflen;
+	copy_to_user(buffer, p, len);
+	page_cache_release(page);
+	return len;
+
+no_readlink_page:
+	page = try_to_get_symlink_page(dentry, inode);
+	if (!page)
+		goto no_page;
+readlink_locked_wait:
+	wait_on_page(page);
+	if (PageUptodate(page))
+		goto success;
+readlink_read_error:
+	page_cache_release(page);
+no_page:
+	return -EIO;
 }
 
 static struct dentry *
-nfs_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow)
+nfs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow)
 {
-	int error;
-	unsigned int len;
-	char *res;
-	void *mem;
-	char *path;
 	struct dentry *result;
-
-	dfprintk(VFS, "nfs: follow_link(%s/%s)\n",
-		dentry->d_parent->d_name.name, dentry->d_name.name);
-
-	error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
-				 &mem, &res, &len, NFS_MAXPATHLEN);
-	result = ERR_PTR(error);
-	if (error)
-		goto out_dput;
-
-	result = ERR_PTR(-ENOMEM);
-	path = kmalloc(len + 1, GFP_KERNEL);
-	if (!path)
-		goto out_mem;
-	memcpy(path, res, len);
-	path[len] = 0;
-	kfree(mem);
-
-	result = lookup_dentry(path, base, follow);
-	kfree(path);
-out:
+	struct inode *inode = dentry->d_inode;
+	struct page *page, **hash;
+	u32 *p;
+
+	/* Caller revalidated the directory inode already. */
+	hash = page_hash(inode, 0);
+	page = __find_page(inode, 0, *hash);
+	if (!page)
+		goto no_followlink_page;
+	if (PageLocked(page))
+		goto followlink_locked_wait;
+	if (!PageUptodate(page))
+		goto followlink_read_error;
+success:
+	p = (u32 *) page_address(page);
+	result = lookup_dentry((char *) (p + 1), base, follow);
+	page_cache_release(page);
 	return result;
 
-out_mem:
-	kfree(mem);
-out_dput:
-	dput(base);
-	goto out;
+no_followlink_page:
+	page = try_to_get_symlink_page(dentry, inode);
+	if (!page)
+		goto no_page;
+followlink_locked_wait:
+	wait_on_page(page);
+	if (PageUptodate(page))
+		goto success;
+followlink_read_error:
+	page_cache_release(page);
+no_page:
+	return ERR_PTR(-EIO);
 }
diff -u --recursive --new-file v2.3.5/linux/fs/nfs/write.c linux/fs/nfs/write.c
--- v2.3.5/linux/fs/nfs/write.c	Wed Jun  2 14:44:39 1999
+++ linux/fs/nfs/write.c	Tue Jun  8 17:58:03 1999
@@ -250,11 +250,24 @@
 	return 1;
 }
 
+static kmem_cache_t *nfs_wreq_cachep;
+
+int nfs_init_wreqcache(void)
+{
+	nfs_wreq_cachep = kmem_cache_create("nfs_wreq",
+					    sizeof(struct nfs_wreq),
+					    0, SLAB_HWCACHE_ALIGN,
+					    NULL, NULL);
+	if (nfs_wreq_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
 static inline void
 free_write_request(struct nfs_wreq * req)
 {
 	if (!--req->wb_count)
-		kfree(req);
+		kmem_cache_free(nfs_wreq_cachep, req);
 }
 
 /*
@@ -274,7 +287,7 @@
 		page->offset + offset, bytes);
 
 	/* FIXME: Enforce hard limit on number of concurrent writes? */
-	wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_KERNEL);
+	wreq = kmem_cache_alloc(nfs_wreq_cachep, SLAB_KERNEL);
 	if (!wreq)
 		goto out_fail;
 	memset(wreq, 0, sizeof(*wreq));
@@ -306,7 +319,7 @@
 
 out_req:
 	rpc_release_task(task);
-	kfree(wreq);
+	kmem_cache_free(nfs_wreq_cachep, wreq);
 out_fail:
 	return NULL;
 }
diff -u --recursive --new-file v2.3.5/linux/fs/nfsd/export.c linux/fs/nfsd/export.c
--- v2.3.5/linux/fs/nfsd/export.c	Fri May 14 18:55:26 1999
+++ linux/fs/nfsd/export.c	Tue Jun  8 10:47:58 1999
@@ -168,9 +168,8 @@
 					}
 				} while (NULL != (exp = exp->ex_next));
 		} while (nfsd_parentdev(&xdev));
-		if (xdentry == xdentry->d_parent) {
+		if (IS_ROOT(xdentry))
 			break;
-		}
 	} while ((xdentry = xdentry->d_parent));
 	exp = NULL;
 out:
@@ -204,7 +203,7 @@
 #endif
 						goto out;
 					}
-					if (ndentry == ndentry->d_parent)
+					if (IS_ROOT(ndentry))
 						break;
 				}
 		} while (NULL != (exp = exp->ex_next));
diff -u --recursive --new-file v2.3.5/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- v2.3.5/linux/fs/nfsd/nfsfh.c	Sat May 15 23:46:04 1999
+++ linux/fs/nfsd/nfsfh.c	Tue Jun  8 10:47:58 1999
@@ -436,7 +436,7 @@
 		dir = iget(sb, dirino);
 		if (!dir)
 			goto out_root;
-		dentry = d_alloc_root(dir, NULL);
+		dentry = d_alloc_root(dir);
 		if (!dentry)
 			goto out_iput;
 
@@ -531,7 +531,7 @@
 	 * Add the parent to the dir cache before releasing the dentry,
 	 * and check whether to save a copy of the dentry's path.
 	 */
-	if (dentry != dentry->d_parent) {
+	if (!IS_ROOT(dentry)) {
 		struct dentry *parent = dget(dentry->d_parent);
 		if (add_to_fhcache(parent, NFSD_DIR_CACHE))
 			nfsd_nr_verified++;
@@ -1140,7 +1140,7 @@
 				error = nfserr_stale;
 dprintk("fh_verify: no root_squashed access.\n");
 			}
-		} while ((tdentry != tdentry->d_parent));
+		} while (!IS_ROOT(tdentry));
 		if (exp->ex_dentry != tdentry) {
 			error = nfserr_stale;
 			printk("nfsd Security: %s/%s bad export.\n",
diff -u --recursive --new-file v2.3.5/linux/fs/ntfs/fs.c linux/fs/ntfs/fs.c
--- v2.3.5/linux/fs/ntfs/fs.c	Fri Apr 23 21:20:38 1999
+++ linux/fs/ntfs/fs.c	Tue Jun  8 10:47:58 1999
@@ -978,7 +978,7 @@
 
 	ntfs_debug(DEBUG_OTHER, "Getting RootDir\n");
 	/* Get the root directory */
-	if(!(sb->s_root=d_alloc_root(iget(sb,FILE_ROOT),NULL))){
+	if(!(sb->s_root=d_alloc_root(iget(sb,FILE_ROOT)))){
 		ntfs_error("Could not get root dir inode\n");
 		goto ntfs_read_super_mft;
 	}
diff -u --recursive --new-file v2.3.5/linux/fs/pipe.c linux/fs/pipe.c
--- v2.3.5/linux/fs/pipe.c	Fri May 14 18:55:26 1999
+++ linux/fs/pipe.c	Tue Jun  8 10:47:58 1999
@@ -486,7 +486,7 @@
 	j = error;
 
 	error = -ENOMEM;
-	f1->f_dentry = f2->f_dentry = dget(d_alloc_root(inode, NULL));
+	f1->f_dentry = f2->f_dentry = dget(d_alloc_root(inode));
 	if (!f1->f_dentry)
 		goto close_f12_inode_i_j;
 
diff -u --recursive --new-file v2.3.5/linux/fs/proc/Makefile linux/fs/proc/Makefile
--- v2.3.5/linux/fs/proc/Makefile	Wed Jun 24 14:30:10 1998
+++ linux/fs/proc/Makefile	Mon Jun  7 12:20:50 1999
@@ -9,7 +9,7 @@
 
 O_TARGET := proc.o
 O_OBJS   := inode.o root.o base.o generic.o mem.o link.o fd.o array.o \
-		kmsg.o scsi.o proc_tty.o
+		kmsg.o scsi.o proc_tty.o sysvipc.o
 ifdef CONFIG_OMIRR
 O_OBJS   := $(O_OBJS) omirr.o
 endif
diff -u --recursive --new-file v2.3.5/linux/fs/proc/inode.c linux/fs/proc/inode.c
--- v2.3.5/linux/fs/proc/inode.c	Fri May  8 18:10:30 1998
+++ linux/fs/proc/inode.c	Tue Jun  8 10:47:58 1999
@@ -319,7 +319,7 @@
 	root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
 	if (!root_inode)
 		goto out_no_root;
-	s->s_root = d_alloc_root(root_inode, NULL);
+	s->s_root = d_alloc_root(root_inode);
 	if (!s->s_root)
 		goto out_no_root;
 	parse_options(data, &root_inode->i_uid, &root_inode->i_gid);
diff -u --recursive --new-file v2.3.5/linux/fs/proc/link.c linux/fs/proc/link.c
--- v2.3.5/linux/fs/proc/link.c	Mon Apr 12 16:18:26 1999
+++ linux/fs/proc/link.c	Tue Jun  8 10:47:58 1999
@@ -146,7 +146,7 @@
 	/* Check for special dentries.. */
 	pattern = NULL;
 	inode = dentry->d_inode;
-	if (inode && dentry->d_parent == dentry) {
+	if (inode && IS_ROOT(dentry)) {
 		if (S_ISSOCK(inode->i_mode))
 			pattern = "socket:[%lu]";
 		if (S_ISFIFO(inode->i_mode))
diff -u --recursive --new-file v2.3.5/linux/fs/proc/root.c linux/fs/proc/root.c
--- v2.3.5/linux/fs/proc/root.c	Wed Apr 28 08:47:39 1999
+++ linux/fs/proc/root.c	Mon Jun  7 12:20:50 1999
@@ -155,7 +155,7 @@
 	&proc_root, NULL
 };
 
-struct proc_dir_entry *proc_net, *proc_scsi, *proc_bus;
+struct proc_dir_entry *proc_net, *proc_scsi, *proc_bus, *proc_sysvipc;
 
 #ifdef CONFIG_MCA
 struct proc_dir_entry proc_mca = {
@@ -681,6 +681,9 @@
 	proc_register(&proc_root, &proc_root_self);
 	proc_net = create_proc_entry("net", S_IFDIR, 0);
 	proc_scsi = create_proc_entry("scsi", S_IFDIR, 0);
+#ifdef CONFIG_SYSVIPC
+	proc_sysvipc = create_proc_entry("sysvipc", S_IFDIR, 0);
+#endif
 #ifdef CONFIG_SYSCTL
 	proc_register(&proc_root, &proc_sys_root);
 #endif
diff -u --recursive --new-file v2.3.5/linux/fs/proc/sysvipc.c linux/fs/proc/sysvipc.c
--- v2.3.5/linux/fs/proc/sysvipc.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/proc/sysvipc.c	Mon Jun  7 12:20:50 1999
@@ -0,0 +1,138 @@
+/*
+ *  linux/fs/proc/sysvipc.c
+ *
+ *  Copyright (c) 1999 Dragos Acostachioaie
+ *
+ *  This code is derived from linux/fs/proc/generic.c,
+ *  which is Copyright (C) 1991, 1992 Linus Torvalds.
+ *
+ *  /proc/sysvipc directory handling functions
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+
+#include <asm/uaccess.h>
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/* 4K page size but our output routines use some slack for overruns */
+#define PROC_BLOCK_SIZE	(3*1024)
+
+static ssize_t
+proc_sysvipc_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
+{
+	struct inode * inode = file->f_dentry->d_inode;
+	char 	*page;
+	ssize_t	retval=0;
+	int	eof=0;
+	ssize_t	n, count;
+	char	*start;
+	struct proc_dir_entry * dp;
+
+	dp = (struct proc_dir_entry *) inode->u.generic_ip;
+	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+
+	while ((nbytes > 0) && !eof)
+	{
+		count = MIN(PROC_BLOCK_SIZE, nbytes);
+
+		start = NULL;
+		if (dp->get_info) {
+			/*
+			 * Handle backwards compatibility with the old net
+			 * routines.
+			 * 
+			 * XXX What gives with the file->f_flags & O_ACCMODE
+			 * test?  Seems stupid to me....
+			 */
+			n = dp->get_info(page, &start, *ppos, count,
+				 (file->f_flags & O_ACCMODE) == O_RDWR);
+			if (n < count)
+				eof = 1;
+		} else if (dp->read_proc) {
+			n = dp->read_proc(page, &start, *ppos,
+					  count, &eof, dp->data);
+		} else
+			break;
+			
+		if (!start) {
+			/*
+			 * For proc files that are less than 4k
+			 */
+			start = page + *ppos;
+			n -= *ppos;
+			if (n <= 0)
+				break;
+			if (n > count)
+				n = count;
+		}
+		if (n == 0)
+			break;	/* End of file */
+		if (n < 0) {
+			if (retval == 0)
+				retval = n;
+			break;
+		}
+		
+		/* This is a hack to allow mangling of file pos independent
+ 		 * of actual bytes read.  Simply place the data at page,
+ 		 * return the bytes, and set `start' to the desired offset
+ 		 * as an unsigned int. - Paul.Russell@rustcorp.com.au
+		 */
+ 		n -= copy_to_user(buf, start < page ? page : start, n);
+		if (n == 0) {
+			if (retval == 0)
+				retval = -EFAULT;
+			break;
+		}
+
+		*ppos += start < page ? (long)start : n; /* Move down the file */
+		nbytes -= n;
+		buf += n;
+		retval += n;
+	}
+	free_page((unsigned long) page);
+	return retval;
+}
+
+static struct file_operations proc_sysvipc_operations = {
+    NULL,		/* lseek   */
+    proc_sysvipc_read,	/* read	   */
+    NULL,		/* write   */
+    NULL,		/* readdir */
+    NULL,		/* poll    */
+    NULL,		/* ioctl   */
+    NULL,		/* mmap	   */
+    NULL,		/* no special open code	   */
+    NULL,		/* no special release code */
+    NULL		/* can't fsync */
+};
+
+/*
+ * proc directories can do almost nothing..
+ */
+struct inode_operations proc_sysvipc_inode_operations = {
+	&proc_sysvipc_operations,	/* default net file-ops */
+	NULL,				/* create */
+	NULL,				/* lookup */
+	NULL,				/* link */
+	NULL,				/* unlink */
+	NULL,				/* symlink */
+	NULL,				/* mkdir */
+	NULL,				/* rmdir */
+	NULL,				/* mknod */
+	NULL,				/* rename */
+	NULL,				/* readlink */
+	NULL,				/* follow_link */
+	NULL,				/* readpage */
+	NULL,				/* writepage */
+	NULL,				/* bmap */
+	NULL,				/* truncate */
+	NULL				/* permission */
+};
diff -u --recursive --new-file v2.3.5/linux/fs/qnx4/inode.c linux/fs/qnx4/inode.c
--- v2.3.5/linux/fs/qnx4/inode.c	Wed Jun  2 14:44:39 1999
+++ linux/fs/qnx4/inode.c	Tue Jun  8 10:47:58 1999
@@ -338,7 +338,7 @@
 	s->u.qnx4_sb.sb_buf = bh;
 	s->u.qnx4_sb.sb = (struct qnx4_super_block *) bh->b_data;
 	s->s_root =
-	    d_alloc_root(iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK), NULL);
+	    d_alloc_root(iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK));
 	if (s->s_root == NULL) {
 		printk("qnx4: get inode failed\n");
 		goto out;
diff -u --recursive --new-file v2.3.5/linux/fs/romfs/inode.c linux/fs/romfs/inode.c
--- v2.3.5/linux/fs/romfs/inode.c	Fri Apr 23 21:20:38 1999
+++ linux/fs/romfs/inode.c	Tue Jun  8 10:47:58 1999
@@ -132,7 +132,7 @@
 	brelse(bh);
 
 	s->s_op	= &romfs_ops;
-	s->s_root = d_alloc_root(iget(s, sz), NULL);
+	s->s_root = d_alloc_root(iget(s, sz));
 
 	if (!s->s_root)
 		goto outnobh;
diff -u --recursive --new-file v2.3.5/linux/fs/smbfs/dir.c linux/fs/smbfs/dir.c
--- v2.3.5/linux/fs/smbfs/dir.c	Sat May  8 17:56:37 1999
+++ linux/fs/smbfs/dir.c	Tue Jun  8 10:47:58 1999
@@ -318,7 +318,7 @@
 	for (;;)
 	{
 		dentry->d_time = jiffies;
-		if (dentry == dentry->d_parent)
+		if (IS_ROOT(dentry))
 			break;
 		dentry = dentry->d_parent;
 	}
diff -u --recursive --new-file v2.3.5/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c
--- v2.3.5/linux/fs/smbfs/inode.c	Sat May 15 23:46:04 1999
+++ linux/fs/smbfs/inode.c	Tue Jun  8 10:47:58 1999
@@ -402,7 +402,7 @@
 	if (!root_inode)
 		goto out_no_root;
 
-	sb->s_root = d_alloc_root(root_inode, NULL);
+	sb->s_root = d_alloc_root(root_inode);
 	if (!sb->s_root)
 		goto out_no_root;
 
diff -u --recursive --new-file v2.3.5/linux/fs/super.c linux/fs/super.c
--- v2.3.5/linux/fs/super.c	Mon May 17 09:55:23 1999
+++ linux/fs/super.c	Tue Jun  8 10:47:58 1999
@@ -169,20 +169,20 @@
 
 int register_filesystem(struct file_system_type * fs)
 {
-        struct file_system_type ** tmp;
+	struct file_system_type ** tmp;
 
-        if (!fs)
-                return -EINVAL;
-        if (fs->next)
-                return -EBUSY;
-        tmp = &file_systems;
-        while (*tmp) {
-                if (strcmp((*tmp)->name, fs->name) == 0)
-                        return -EBUSY;
-                tmp = &(*tmp)->next;
-        }
-        *tmp = fs;
-        return 0;
+	if (!fs)
+		return -EINVAL;
+	if (fs->next)
+		return -EBUSY;
+	tmp = &file_systems;
+	while (*tmp) {
+		if (strcmp((*tmp)->name, fs->name) == 0)
+			return -EBUSY;
+		tmp = &(*tmp)->next;
+	}
+	*tmp = fs;
+	return 0;
 }
 
 #ifdef CONFIG_MODULES
diff -u --recursive --new-file v2.3.5/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
--- v2.3.5/linux/fs/sysv/inode.c	Fri May 14 18:55:26 1999
+++ linux/fs/sysv/inode.c	Tue Jun  8 10:47:58 1999
@@ -503,7 +503,7 @@
 	sb->s_dev = dev;
 	sb->s_op = &sysv_sops;
 	root_inode = iget(sb,SYSV_ROOT_INO);
-	sb->s_root = d_alloc_root(root_inode, NULL);
+	sb->s_root = d_alloc_root(root_inode);
 	if (!sb->s_root) {
 		printk("SysV FS: get root inode failed\n");
 		sysv_put_super(sb);
diff -u --recursive --new-file v2.3.5/linux/fs/ufs/super.c linux/fs/ufs/super.c
--- v2.3.5/linux/fs/ufs/super.c	Mon Apr 12 10:03:45 1999
+++ linux/fs/ufs/super.c	Tue Jun  8 10:47:58 1999
@@ -746,7 +746,7 @@
 	sb->u.ufs_sb.s_flags = flags;
 	sb->u.ufs_sb.s_swab = swab;
 	 	                                                          
-	sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO), NULL);
+	sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO));
 
 
 	/*
diff -u --recursive --new-file v2.3.5/linux/fs/umsdos/check.c linux/fs/umsdos/check.c
--- v2.3.5/linux/fs/umsdos/check.c	Fri Dec 18 07:09:36 1998
+++ linux/fs/umsdos/check.c	Tue Jun  8 10:47:58 1999
@@ -212,7 +212,7 @@
 	
 	while (dentry && count < 10) {
 		check_dent_int (dentry, count++);
-		if (dentry == dentry->d_parent) {
+		if (IS_ROOT(dentry)) {
 			printk (KERN_DEBUG "*** end checking dentry (root reached ok)\n");
 			break;
 		}
diff -u --recursive --new-file v2.3.5/linux/include/asm-alpha/softirq.h linux/include/asm-alpha/softirq.h
--- v2.3.5/linux/include/asm-alpha/softirq.h	Sun Dec 27 15:20:32 1998
+++ linux/include/asm-alpha/softirq.h	Thu Jun  3 14:32:26 1999
@@ -5,6 +5,16 @@
 #include <asm/atomic.h>
 #include <asm/hardirq.h>
 
+/*
+ * This works but is wrong - on SMP it should disable only on the
+ * current CPU and shouldn't synchronize like the heavy global
+ * disable does. Oh, well.
+ *
+ * See the x86 version for an example.
+ */
+#define local_bh_enable()	start_bh_atomic()
+#define local_bh_disable()	end_bh_atomic()
+
 extern unsigned int local_bh_count[NR_CPUS];
 
 #define get_active_bhs()	(bh_mask & bh_active)
diff -u --recursive --new-file v2.3.5/linux/include/asm-alpha/spinlock.h linux/include/asm-alpha/spinlock.h
--- v2.3.5/linux/include/asm-alpha/spinlock.h	Sun Jan 10 09:59:59 1999
+++ linux/include/asm-alpha/spinlock.h	Thu Jun  3 14:25:57 1999
@@ -3,6 +3,35 @@
 
 #include <asm/system.h>
 
+/*
+ * These are the generic versions of the spinlocks
+ * and read-write locks.. We should actually do a
+ * <linux/spinlock.h> with all of this. Oh, well.
+ */
+#define spin_lock_irqsave(lock, flags)		do { local_irq_save(flags);       spin_lock(lock); } while (0)
+#define spin_lock_irq(lock)			do { local_irq_disable();         spin_lock(lock); } while (0)
+#define spin_lock_bh(lock)			do { local_bh_disable();          spin_lock(lock); } while (0)
+
+#define read_lock_irqsave(lock, flags)		do { local_irq_save(flags);       read_lock(lock); } while (0)
+#define read_lock_irq(lock)			do { local_irq_disable();         read_lock(lock); } while (0)
+#define read_lock_bh(lock)			do { local_bh_disable();          read_lock(lock); } while (0)
+
+#define write_lock_irqsave(lock, flags)		do { local_irq_save(flags);      write_lock(lock); } while (0)
+#define write_lock_irq(lock)			do { local_irq_disable();        write_lock(lock); } while (0)
+#define write_lock_bh(lock)			do { local_bh_disable();         write_lock(lock); } while (0)
+
+#define spin_unlock_irqrestore(lock, flags)	do { spin_unlock(lock);  local_irq_restore(flags); } while (0)
+#define spin_unlock_irq(lock)			do { spin_unlock(lock);  local_irq_enable();       } while (0)
+#define spin_unlock_bh(lock)			do { spin_unlock(lock);  local_bh_enable();        } while (0)
+
+#define read_unlock_irqrestore(lock, flags)	do { read_unlock(lock);  local_irq_restore(flags); } while (0)
+#define read_unlock_irq(lock)			do { read_unlock(lock);  local_irq_enable();       } while (0)
+#define read_unlock_bh(lock)			do { read_unlock(lock);  local_bh_enable();        } while (0)
+
+#define write_unlock_irqrestore(lock, flags)	do { write_unlock(lock); local_irq_restore(flags); } while (0)
+#define write_unlock_irq(lock)			do { write_unlock(lock); local_irq_enable();       } while (0)
+#define write_unlock_bh(lock)			do { write_unlock(lock); local_bh_enable();        } while (0)
+
 #ifndef __SMP__
 
 /*
@@ -23,11 +52,6 @@
 #define spin_trylock(lock)			((void) 0)
 #define spin_unlock_wait(lock)			((void) 0)
 #define spin_unlock(lock)			((void) 0)
-#define spin_lock_irq(lock)			cli()
-#define spin_unlock_irq(lock)			sti()
-
-#define spin_lock_irqsave(lock, flags)		save_and_cli(flags)
-#define spin_unlock_irqrestore(lock, flags)	restore_flags(flags)
 
 /*
  * Read-write spinlocks, allowing multiple readers
@@ -53,15 +77,6 @@
 #define read_unlock(lock)			((void) 0)
 #define write_lock(lock)			((void) 0)
 #define write_unlock(lock)			((void) 0)
-#define read_lock_irq(lock)			cli()
-#define read_unlock_irq(lock)			sti()
-#define write_lock_irq(lock)			cli()
-#define write_unlock_irq(lock)			sti()
-
-#define read_lock_irqsave(lock, flags)		save_and_cli(flags)
-#define read_unlock_irqrestore(lock, flags)	restore_flags(flags)
-#define write_lock_irqsave(lock, flags)		save_and_cli(flags)
-#define write_unlock_irqrestore(lock, flags)	restore_flags(flags)
 
 #else /* __SMP__ */
 
@@ -150,15 +165,6 @@
 #define spin_lock_own(LOCK, LOCATION)	((void)0)
 #endif /* DEBUG_SPINLOCK */
 
-#define spin_lock_irq(lock) \
-  (__cli(), spin_lock(lock))
-#define spin_unlock_irq(lock) \
-  (spin_unlock(lock), __sti())
-#define spin_lock_irqsave(lock, flags) \
-  (__save_and_cli(flags), spin_lock(lock))
-#define spin_unlock_irqrestore(lock, flags) \
-  (spin_unlock(lock), __restore_flags(flags))
-
 /***********************************************************/
 
 typedef struct { volatile int write_lock:1, read_counter:31; } rwlock_t;
@@ -232,20 +238,6 @@
 	: "=m" (__dummy_lock(lock)), "=&r" (regx)
 	: "m" (__dummy_lock(lock)));
 }
-
-#define read_lock_irq(lock)	(__cli(), read_lock(lock))
-#define read_unlock_irq(lock)	(read_unlock(lock), __sti())
-#define write_lock_irq(lock)	(__cli(), write_lock(lock))
-#define write_unlock_irq(lock)	(write_unlock(lock), __sti())
-
-#define read_lock_irqsave(lock, flags)	\
-	(__save_and_cli(flags), read_lock(lock))
-#define read_unlock_irqrestore(lock, flags) \
-	(read_unlock(lock), __restore_flags(flags))
-#define write_lock_irqsave(lock, flags)	\
-	(__save_and_cli(flags), write_lock(lock))
-#define write_unlock_irqrestore(lock, flags) \
-	(write_unlock(lock), __restore_flags(flags))
 
 #endif /* SMP */
 #endif /* _ALPHA_SPINLOCK_H */
diff -u --recursive --new-file v2.3.5/linux/include/asm-alpha/string.h linux/include/asm-alpha/string.h
--- v2.3.5/linux/include/asm-alpha/string.h	Sun Aug  9 12:09:06 1998
+++ linux/include/asm-alpha/string.h	Mon Jun  7 11:37:13 1999
@@ -11,6 +11,7 @@
  */
 
 #define __HAVE_ARCH_MEMCPY
+#define __HAVE_ARCH_MEMMOVE
 /* For backward compatibility with modules.  Unused otherwise.  */
 extern void * __memcpy(void *, const void *, size_t);
 
diff -u --recursive --new-file v2.3.5/linux/include/asm-alpha/system.h linux/include/asm-alpha/system.h
--- v2.3.5/linux/include/asm-alpha/system.h	Sat May 15 23:46:05 1999
+++ linux/include/asm-alpha/system.h	Thu Jun  3 14:28:25 1999
@@ -227,6 +227,11 @@
 #define __save_and_cli(flags)	((flags) = swpipl(7))
 #define __restore_flags(flags)	setipl(flags)
 
+#define local_irq_save(flags)		__save_and_cli(flags)
+#define local_irq_restore(flags)	__restore_flags(flags)
+#define local_irq_disable()		__cli()
+#define local_irq_enable()		__sti()
+
 #ifdef __SMP__
 
 extern int global_irq_holder;
diff -u --recursive --new-file v2.3.5/linux/include/asm-i386/bugs.h linux/include/asm-i386/bugs.h
--- v2.3.5/linux/include/asm-i386/bugs.h	Tue May 11 13:03:52 1999
+++ linux/include/asm-i386/bugs.h	Tue Jun  8 23:03:37 1999
@@ -357,10 +357,18 @@
 __initfunc(static void check_cyrix_coma(void))
 {
 	if (boot_cpu_data.coma_bug) {
-		unsigned char ccr1;
+		unsigned char ccr3, tmp;
 		cli();
-		ccr1 = getCx86 (CX86_CCR1);
-		setCx86 (CX86_CCR1, ccr1 | 0x10);
+		ccr3 = getCx86(CX86_CCR3);
+		setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN  */
+		tmp = getCx86(0x31);
+		setCx86(0x31, tmp | 0xf8);
+		tmp = getCx86(0x32);
+		setCx86(0x32, tmp | 0x7f);
+		setCx86(0x33, 0);
+		tmp = getCx86(0x3c);
+		setCx86(0x3c, tmp | 0x87);
+		setCx86(CX86_CCR3, ccr3);                 /* disable MAPEN */
 		sti();
 		printk("Cyrix processor with \"coma bug\" found, workaround enabled\n");
 	}
diff -u --recursive --new-file v2.3.5/linux/include/asm-i386/page.h linux/include/asm-i386/page.h
--- v2.3.5/linux/include/asm-i386/page.h	Tue Jan 26 13:04:02 1999
+++ linux/include/asm-i386/page.h	Tue Jun  8 23:03:39 1999
@@ -79,7 +79,10 @@
  *
  * which has the same constant encoded..
  */
-#define __PAGE_OFFSET		(0xC0000000)
+
+#include <asm/page_offset.h>
+
+#define __PAGE_OFFSET		(PAGE_OFFSET_RAW)
 
 #define PAGE_OFFSET		((unsigned long)__PAGE_OFFSET)
 #define __pa(x)			((unsigned long)(x)-PAGE_OFFSET)
diff -u --recursive --new-file v2.3.5/linux/include/asm-i386/page_offset.h linux/include/asm-i386/page_offset.h
--- v2.3.5/linux/include/asm-i386/page_offset.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/page_offset.h	Tue Jun  8 23:03:38 1999
@@ -0,0 +1,8 @@
+#include <linux/config.h>
+#ifdef CONFIG_1GB
+#define PAGE_OFFSET_RAW 0xC0000000
+#elif defined(CONFIG_2GB)
+#define PAGE_OFFSET_RAW 0x80000000
+#elif defined(CONFIG_3GB)
+#define PAGE_OFFSET_RAW 0x40000000
+#endif
diff -u --recursive --new-file v2.3.5/linux/include/asm-mips/namei.h linux/include/asm-mips/namei.h
--- v2.3.5/linux/include/asm-mips/namei.h	Mon Nov 16 10:38:09 1998
+++ linux/include/asm-mips/namei.h	Wed Jun  9 16:24:15 1999
@@ -23,7 +23,7 @@
 
 	base = lookup_dentry (IRIX32_EMUL,
 			dget (current->fs->root), 
-			(LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_SLASHOK));
+			(LOOKUP_FOLLOW | LOOKUP_DIRECTORY));
 			
 	if (IS_ERR (base)) return base;
 	
diff -u --recursive --new-file v2.3.5/linux/include/asm-ppc/system.h linux/include/asm-ppc/system.h
--- v2.3.5/linux/include/asm-ppc/system.h	Mon May 31 22:28:06 1999
+++ linux/include/asm-ppc/system.h	Mon Jun  7 12:11:51 1999
@@ -74,6 +74,9 @@
 extern void enable_kernel_fp(void);
 extern void cvt_fd(float *from, double *to, unsigned long *fpscr);
 extern void cvt_df(double *from, float *to, unsigned long *fpscr);
+extern int call_rtas(const char *, int, int, unsigned long *, ...);
+extern void chrp_progress(char *);
+void chrp_event_scan(void);
 
 struct device_node;
 extern void note_scsi_host(struct device_node *, void *);
@@ -87,6 +90,8 @@
 extern struct task_struct *_switch(struct thread_struct *prev,
 				     struct thread_struct *next,
 				     unsigned long context);
+
+extern unsigned int rtas_data;
 
 struct pt_regs;
 extern void dump_regs(struct pt_regs *);
diff -u --recursive --new-file v2.3.5/linux/include/asm-sparc/io.h linux/include/asm-sparc/io.h
--- v2.3.5/linux/include/asm-sparc/io.h	Mon May 17 09:55:23 1999
+++ linux/include/asm-sparc/io.h	Wed Jun  9 14:44:26 1999
@@ -1,4 +1,4 @@
-/* $Id: io.h,v 1.19 1999/05/14 07:26:09 davem Exp $ */
+/* $Id: io.h,v 1.20 1999/06/03 15:02:50 davem Exp $ */
 #ifndef __SPARC_IO_H
 #define __SPARC_IO_H
 
@@ -161,6 +161,15 @@
 
 #define virt_to_phys(x) __pa((unsigned long)(x))
 #define phys_to_virt(x) __va((unsigned long)(x))
+
+/*
+ * At the moment, we do not use CMOS_READ anywhere outside of rtc.c,
+ * so rtc_port is static in it. This should not change unless a new
+ * hardware pops up.
+ */
+
+#define RTC_PORT(x)   (rtc_port + (x))
+#define RTC_ALWAYS_BCD  0
 
 /* Nothing to do */
 
diff -u --recursive --new-file v2.3.5/linux/include/asm-sparc/namei.h linux/include/asm-sparc/namei.h
--- v2.3.5/linux/include/asm-sparc/namei.h	Thu Apr 22 19:24:52 1999
+++ linux/include/asm-sparc/namei.h	Wed Jun  9 16:24:15 1999
@@ -28,7 +28,7 @@
 
 	base = lookup_dentry (emul, 
 			      dget (current->fs->root),
-			      (LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_SLASHOK));
+			      (LOOKUP_FOLLOW | LOOKUP_DIRECTORY));
 			
 	if (IS_ERR (base)) return NULL;
 	
diff -u --recursive --new-file v2.3.5/linux/include/asm-sparc/pcic.h linux/include/asm-sparc/pcic.h
--- v2.3.5/linux/include/asm-sparc/pcic.h	Sun Oct  4 10:22:44 1998
+++ linux/include/asm-sparc/pcic.h	Wed Jun  9 14:44:26 1999
@@ -1,4 +1,4 @@
-/* $Id: pcic.h,v 1.1 1998/09/22 05:54:39 jj Exp $
+/* $Id: pcic.h,v 1.2 1999/06/03 15:02:51 davem Exp $
  * pcic.h: JavaEngine 1 specific PCI definitions.
  *
  * Copyright (C) 1998 V. Roganov and G. Raiko
@@ -7,6 +7,8 @@
 #ifndef __SPARC_PCIC_H
 #define __SPARC_PCIC_H
 
+#ifndef __ASSEMBLY__
+
 #include <linux/types.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
@@ -21,13 +23,17 @@
         unsigned long           pcic_config_space_addr;
         unsigned long           pcic_config_space_data;
         struct linux_pbm_info   pbm;
+	struct pcic_ca2irq	*pcic_imap;
+	int			pcic_imdim;
 };
 
 extern unsigned long pcic_alloc_io(unsigned long* addr);
 extern void pcic_probe(void);
 extern void sun4m_pci_init_IRQ(void);
 
-/* Size of PCI Space */
+#endif
+
+/* Size of PCI I/O space which we relocate. */
 #define PCI_SPACE_SIZE                  0x1000000       /* 16 MB */
 
 /* PCIC Register Set. */
@@ -50,10 +56,18 @@
 #define PCI_SOFTWARE_INT_CLEAR          0x6a    /* 16 bits */
 #define PCI_SOFTWARE_INT_SET            0x6e    /* 16 bits */
 #define PCI_SYS_INT_PENDING             0x70    /* 32 bits */
+#define  PCI_SYS_INT_PENDING_PIO		0x40000000
+#define  PCI_SYS_INT_PENDING_DMA		0x20000000
+#define  PCI_SYS_INT_PENDING_PCI		0x10000000
+#define  PCI_SYS_INT_PENDING_APSR		0x08000000
 #define PCI_SYS_INT_TARGET_MASK         0x74    /* 32 bits */
 #define PCI_SYS_INT_TARGET_MASK_CLEAR   0x78    /* 32 bits */
 #define PCI_SYS_INT_TARGET_MASK_SET     0x7c    /* 32 bits */
 #define PCI_SYS_INT_PENDING_CLEAR       0x83    /* 8  bits */
+#define  PCI_SYS_INT_PENDING_CLEAR_ALL		0x80
+#define  PCI_SYS_INT_PENDING_CLEAR_PIO		0x40
+#define  PCI_SYS_INT_PENDING_CLEAR_DMA		0x20
+#define  PCI_SYS_INT_PENDING_CLEAR_PCI		0x10
 #define PCI_IOTLB_CONTROL               0x84    /* 8  bits */
 #define PCI_INT_SELECT_LO               0x88    /* 16 bits */
 #define PCI_ARBITRATION_SELECT          0x8a    /* 16 bits */
diff -u --recursive --new-file v2.3.5/linux/include/asm-sparc64/namei.h linux/include/asm-sparc64/namei.h
--- v2.3.5/linux/include/asm-sparc64/namei.h	Thu Apr 22 19:24:52 1999
+++ linux/include/asm-sparc64/namei.h	Wed Jun  9 16:24:15 1999
@@ -28,7 +28,7 @@
 
 	base = lookup_dentry (emul, 
 			      dget (current->fs->root), 
-			      (LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_SLASHOK));
+			      (LOOKUP_FOLLOW | LOOKUP_DIRECTORY));
 			
 	if (IS_ERR (base)) return NULL;
 	
diff -u --recursive --new-file v2.3.5/linux/include/linux/cyclomx.h linux/include/linux/cyclomx.h
--- v2.3.5/linux/include/linux/cyclomx.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/cyclomx.h	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,91 @@
+/*
+* cyclomx.h	CYCLOM X Multiprotocol WAN Link Driver.
+*		User-level API definitions.
+*
+* Author:	Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+*
+* Copyright:	(c) 1998, 1999 Arnaldo Carvalho de Melo
+*
+* Based on wanpipe.h by Gene Kozin <genek@compuserve.com>
+*
+*		This program is free software; you can redistribute it and/or
+*		modify it under the terms of the GNU General Public License
+*		as published by the Free Software Foundation; either version
+*		2 of the License, or (at your option) any later version.
+* ============================================================================
+* 1999/05/19	acme		wait_queue_head_t wait_stats(support for 2.3.*)
+* 1999/01/03	acme		judicious use of data types
+* Dec 27, 1998	Arnaldo		cleanup: PACKED not needed
+* Aug 08, 1998	Arnaldo		Version 0.0.1
+*/
+#ifndef	_CYCLOMX_H
+#define	_CYCLOMX_H
+
+#include <linux/wanrouter.h>
+#include <asm/spinlock.h>
+
+#ifdef	__KERNEL__
+/* Kernel Interface */
+
+#include <linux/cycx_drv.h>	/* CYCLOM X support module API definitions */
+#include <linux/cycx_cfm.h>	/* CYCLOM X firmware module definitions */
+#ifdef CONFIG_CYCLOMX_X25
+#include <linux/cycx_x25.h>
+#endif
+
+#ifndef	min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif
+#ifndef	max
+#define max(a,b) (((a)>(b))?(a):(b))
+#endif
+
+#define	is_digit(ch) (((ch)>=(unsigned)'0'&&(ch)<=(unsigned)'9')?1:0)
+
+/* Adapter Data Space.
+ * This structure is needed because we handle multiple cards, otherwise
+ * static data would do it.
+ */
+typedef struct cycx {
+	char devname[WAN_DRVNAME_SZ+1];	/* card name */
+	cycxhw_t hw;			/* hardware configuration */
+	wan_device_t wandev;		/* WAN device data space */
+	u32 open_cnt;			/* number of open interfaces */
+	u32 state_tick;			/* link state timestamp */
+	spinlock_t lock;
+	char in_isr;			/* interrupt-in-service flag */
+	char buff_int_mode_unbusy;      /* flag for carrying out dev_tint */
+	u16 irq_dis_if_send_count;	/* Disabling irqs in if_send*/
+#if LINUX_VERSION_CODE >= 0x020300
+	wait_queue_head_t wait_stats;  /* to wait for the STATS indication */
+#else
+	struct wait_queue* wait_stats;  /* to wait for the STATS indication */
+#endif
+	u32 mbox;			/* -> mailbox */
+	void (*isr)(struct cycx* card);	/* interrupt service routine */
+	int (*exec)(struct cycx* card, void* u_cmd, void* u_data);
+	union {
+#ifdef CONFIG_CYCLOMX_X25
+		struct { /* X.25 specific data */
+			u32 lo_pvc;
+			u32 hi_pvc;
+			u32 lo_svc;
+			u32 hi_svc;
+			TX25Stats stats;
+			unsigned critical;	/* critical section flag */
+			u32 connection_keys;
+		} x;
+#endif
+	} u;
+} cycx_t;
+
+/* Public Functions */
+void cyclomx_open      (cycx_t* card);			/* cycx_main.c */
+void cyclomx_close     (cycx_t* card);			/* cycx_main.c */
+void cyclomx_set_state (cycx_t* card, int state);	/* cycx_main.c */
+
+#ifdef CONFIG_CYCLOMX_X25
+int cyx_init (cycx_t* card, wandev_conf_t* conf);	/* cycx_x25.c */
+#endif
+#endif	/* __KERNEL__ */
+#endif	/* _CYCLOMX_H */
diff -u --recursive --new-file v2.3.5/linux/include/linux/cycx_cfm.h linux/include/linux/cycx_cfm.h
--- v2.3.5/linux/include/linux/cycx_cfm.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/cycx_cfm.h	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,81 @@
+/*
+* cycx_cfm.h	CYCLOM X Multiprotocol WAN Link Driver.
+*		Definitions for the CYCLOM X Firmware Module (CFM).
+*
+* Author:	Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+* Copyright:	(c) 1998, 1999 Arnaldo Carvalho de Melo
+*
+* Based on sdlasfm.h by Gene Kozin <74604.152@compuserve.com>
+*
+*		This program is free software; you can redistribute it and/or
+*		modify it under the terms of the GNU General Public License
+*		as published by the Free Software Foundation; either version
+*		2 of the License, or (at your option) any later version.
+* ============================================================================
+* Aug 08, 1998	Arnaldo		Initial version.
+*/
+#ifndef	_CYCX_CFM_H
+#define	_CYCX_CFM_H
+
+/* Defines */
+
+#define	CFM_VERSION	2
+#define	CFM_SIGNATURE	"CFM - Cyclades CYCX Firmware Module"
+
+/* min/max */
+#define	CFM_IMAGE_SIZE	0x20000	/* max size of CYCX code image file */
+#define	CFM_DESCR_LEN	256	/* max length of description string */
+#define	CFM_MAX_CYCX	1	/* max number of compatible adapters */
+#define	CFM_LOAD_BUFSZ	0x400	/* buffer size for reset code (buffer_load) */
+
+/* Firmware Commands */
+#define GEN_POWER_ON	0x1280
+
+#define GEN_SET_SEG	0x1401	/* boot segment setting.        */
+#define GEN_BOOT_DAT	0x1402	/* boot data.                   */
+#define GEN_START	0x1403	/* board start.                 */
+#define GEN_DEFPAR	0x1404	/* buffer length for boot.      */
+
+/* Adapter types */
+#define CYCX_2X		2
+#define CYCX_8X		8
+#define CYCX_16X	16
+
+#define	CFID_X25_2X	5200
+
+
+/* Data Types */
+
+typedef struct	cfm_info		/* firmware module information */
+{
+	unsigned short	codeid;		/* firmware ID */
+	unsigned short	version;	/* firmaware version number */
+	unsigned short	adapter[CFM_MAX_CYCX]; /* compatible adapter types */
+	unsigned long	memsize;	/* minimum memory size */
+	unsigned short	reserved[2];	/* reserved */
+	unsigned short	startoffs;	/* entry point offset */
+	unsigned short	winoffs;	/* dual-port memory window offset */
+	unsigned short	codeoffs;	/* code load offset */
+	unsigned long	codesize;	/* code size */
+	unsigned short	dataoffs;	/* configuration data load offset */
+	unsigned long	datasize;	/* configuration data size */
+} cfm_info_t;
+
+typedef struct cfm			/* CYCX firmware file structire */
+{
+	char		signature[80];	/* CFM file signature */
+	unsigned short	version;	/* file format version */
+	unsigned short	checksum;	/* info + image */
+	unsigned short	reserved[6];	/* reserved */
+	char		descr[CFM_DESCR_LEN]; /* description string */
+	cfm_info_t	info;		/* firmware module info */
+	unsigned char	image[1];	/* code image (variable size) */
+} cfm_t;
+
+typedef struct cycx_header_s {
+	unsigned long  reset_size;
+	unsigned long  data_size;
+	unsigned long  code_size;
+} cycx_header_t;
+
+#endif	/* _CYCX_CFM_H */
diff -u --recursive --new-file v2.3.5/linux/include/linux/cycx_drv.h linux/include/linux/cycx_drv.h
--- v2.3.5/linux/include/linux/cycx_drv.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/cycx_drv.h	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,66 @@
+/*
+* cycx_drv.h	CYCX Support Module.  Kernel API Definitions.
+*
+* Author:	Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+* Copyright:	(c) 1998, 1999 Arnaldo Carvalho de Melo
+*
+* Based on sdladrv.h by Gene Kozin <genek@compuserve.com>
+*
+*		This program is free software; you can redistribute it and/or
+*		modify it under the terms of the GNU General Public License
+*		as published by the Free Software Foundation; either version
+*		2 of the License, or (at your option) any later version.
+* ============================================================================
+* 1999/01/03	acme		more judicious use of data types...
+*				uclong, ucchar, etc deleted, the u8, u16, u32
+*				types are the portable way to go.
+* 1999/01/03	acme		judicious use of data types... u16, u32, etc
+* Dec 26, 1998	Arnaldo 	FIXED_BUFFERS, CONF_OFFSET,
+*                               removal of cy_read{bwl}
+* Aug 08, 1998	Arnaldo 	Initial version.
+*/
+#ifndef	_CYCX_DRV_H
+#define	_CYCX_DRV_H
+
+#define	CYCX_WINDOWSIZE	0x4000	/* default dual-port memory window size */
+#define	GEN_CYCX_INTR	0x02
+#define	RST_ENABLE	0x04
+#define	START_CPU	0x06
+#define	RST_DISABLE	0x08
+#define	FIXED_BUFFERS	0x08
+#define	TEST_PATTERN	0xaa55
+#define	CMD_OFFSET	0x20
+#define CONF_OFFSET     0x0380
+#define	RESET_OFFSET	0x3c00	/* For reset file load */
+#define	DATA_OFFSET	0x0100	/* For code and data files load */
+#define	START_OFFSET	0x3ff0	/* 80186 starts here */
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+/* Data Structures */
+/* Adapter hardware configuration. Pointer to this structure is passed to all
+ * APIs.  */
+typedef struct cycxhw {
+	u32 type;		/* adapter type */
+	u32 fwid;		/* firmware ID */
+	int irq;		/* interrupt request level */
+	u32 dpmbase;		/* dual-port memory base */
+	u32 dpmsize;		/* dual-port memory size */
+	u32 pclk;		/* CPU clock rate, kHz */
+	u32 memory;		/* memory size */
+	u32 reserved[5];
+} cycxhw_t;
+
+/* Function Prototypes */
+extern int cycx_setup  (cycxhw_t* hw, void* sfm, u32 len);
+extern int cycx_down   (cycxhw_t* hw);
+extern int cycx_inten  (cycxhw_t* hw);
+extern int cycx_intde  (cycxhw_t* hw);
+extern int cycx_intack (cycxhw_t* hw);
+extern int cycx_intr   (cycxhw_t* hw);
+extern int cycx_peek   (cycxhw_t* hw, u32 addr, void* buf, u32 len);
+extern int cycx_poke   (cycxhw_t* hw, u32 addr, void* buf, u32 len);
+extern int cycx_exec   (u32 addr);
+#endif	/* _CYCX_DRV_H */
diff -u --recursive --new-file v2.3.5/linux/include/linux/cycx_x25.h linux/include/linux/cycx_x25.h
--- v2.3.5/linux/include/linux/cycx_x25.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/cycx_x25.h	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,103 @@
+/*
+* cycx_x25.h	Cyclom X.25 firmware API definitions.
+*
+* Author:	Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+*
+* Copyright:	(c) 1998, 1999 Arnaldo Carvalho de Melo
+*
+* Based on sdla_x25.h by Gene Kozin <74604.152@compuserve.com>
+*
+*		This program is free software; you can redistribute it and/or
+*		modify it under the terms of the GNU General Public License
+*		as published by the Free Software Foundation; either version
+*		2 of the License, or (at your option) any later version.
+* ============================================================================
+* 1999/01/03	acme		judicious use of data types
+*
+* 1999/01/02	acme		#define X25_ACK_N3	0x4411
+* Dec 28, 1998	Arnaldo		cleanup: lot'o'things removed
+*					 commands listed,
+*					 TX25Cmd & TX25Config structs
+*					 typedef'ed
+*/
+#ifndef	_CYCX_X25_H
+#define	_CYCX_X25_H
+
+#ifndef PACKED
+#define PACKED __attribute__((packed))
+#endif 
+
+/* X.25 shared memory layout. */
+#define	X25_MBOX_OFFS	0x300	/* general mailbox block */
+#define	X25_RXMBOX_OFFS	0x340	/* receive mailbox */
+
+/* DATA STRUCTURES */
+/* X.25 Command Block. */
+typedef struct X25Cmd
+{
+	u16 command PACKED;
+	u16 link    PACKED; /* values: 0 or 1 */
+	u16 len     PACKED; /* values: 0 thru 0x205 (517) */
+	u32 buf     PACKED;
+} TX25Cmd;
+
+/* Defines for the 'command' field. */
+#define X25_CONNECT_REQUEST             0x4401
+#define X25_CONNECT_RESPONSE            0x4402
+#define X25_DISCONNECT_REQUEST          0x4403
+#define X25_DISCONNECT_RESPONSE         0x4404
+#define X25_DATA_REQUEST                0x4405
+#define X25_ACK_TO_VC			0x4406
+#define X25_INTERRUPT_RESPONSE          0x4407
+#define X25_CONFIG                      0x4408
+#define X25_CONNECT_INDICATION          0x4409
+#define X25_CONNECT_CONFIRM             0x440A
+#define X25_DISCONNECT_INDICATION       0x440B
+#define X25_DISCONNECT_CONFIRM          0x440C
+#define X25_DATA_INDICATION             0x440E
+#define X25_INTERRUPT_INDICATION        0x440F
+#define X25_ACK_FROM_VC			0x4410
+#define X25_ACK_N3			0x4411
+#define X25_CONNECT_COLLISION           0x4413
+#define X25_N3WIN                       0x4414
+#define X25_LINE_ON                     0x4415
+#define X25_LINE_OFF                    0x4416
+#define X25_RESET_REQUEST               0x4417
+#define X25_LOG                         0x4500
+#define X25_STATISTIC                   0x4600
+#define X25_TRACE                       0x4700
+#define X25_N2TRACEXC                   0x4702
+#define X25_N3TRACEXC                   0x4703
+
+typedef struct X25Config {
+	u8  link	PACKED; /* link number */
+	u8  speed	PACKED; /* line speed */
+	u8  clock	PACKED; /* internal/external */
+	u8  n2		PACKED; /* # of level 2 retransm.(values: 1 thru FF) */
+	u8  n2win	PACKED; /* level 2 window (values: 1 thru 7) */
+	u8  n3win	PACKED; /* level 3 window (values: 1 thru 7) */
+	u8  nvc		PACKED; /* # of logical channels (values: 1 thru 64) */
+	u8  pktlen	PACKED; /* level 3 packet lenght - log base 2 of size */
+	u8  locaddr	PACKED; /* my address */
+	u8  remaddr	PACKED; /* remote address */
+	u16 t1		PACKED;	/* time, in seconds */
+	u16 t2		PACKED;	/* time, in seconds */
+	u8  t21		PACKED;	/* time, in seconds */
+	u8  npvc	PACKED;	/* # of permanent virt. circuits (1 thru nvc) */
+	u8  t23		PACKED;	/* time, in seconds */
+	u8  flags	PACKED;	/* see dosx25.doc, in portuguese, for details */
+} TX25Config;
+
+typedef struct X25Stats {
+	u16 rx_crc_errors	PACKED;
+	u16 rx_over_errors	PACKED;
+	u16 n2_tx_frames 	PACKED;
+	u16 n2_rx_frames 	PACKED;
+	u16 tx_timeouts 	PACKED;
+	u16 rx_timeouts 	PACKED;
+	u16 n3_tx_packets 	PACKED;
+	u16 n3_rx_packets 	PACKED;
+	u16 tx_aborts	 	PACKED;
+	u16 rx_aborts	 	PACKED;
+} TX25Stats;
+#endif	/* _CYCX_X25_H */
diff -u --recursive --new-file v2.3.5/linux/include/linux/dcache.h linux/include/linux/dcache.h
--- v2.3.5/linux/include/linux/dcache.h	Sat May  8 17:56:37 1999
+++ linux/include/linux/dcache.h	Tue Jun  8 10:47:58 1999
@@ -12,8 +12,6 @@
  * with heavy changes by Linus Torvalds
  */
 
-#define D_MAXLEN 1024
-
 #define IS_ROOT(x) ((x) == (x)->d_parent)
 
 /*
@@ -132,7 +130,7 @@
 extern void d_delete(struct dentry *);
 
 /* allocate/de-allocate */
-extern struct dentry * d_alloc(struct dentry * parent, const struct qstr *name);
+extern struct dentry * d_alloc(struct dentry *, const struct qstr *);
 extern void prune_dcache(int);
 extern void shrink_dcache_sb(struct super_block *);
 extern void shrink_dcache_parent(struct dentry *);
@@ -147,7 +145,7 @@
 extern void free_inode_memory(int);	/* defined in fs/inode.c */
 
 /* only used at mount-time */
-extern struct dentry * d_alloc_root(struct inode * root_inode, struct dentry * old_root);
+extern struct dentry * d_alloc_root(struct inode *);
 
 /* test whether root is busy without destroying dcache */
 extern int is_root_busy(struct dentry *);
@@ -155,7 +153,7 @@
 /*
  * This adds the entry to the hash queues.
  */
-extern void d_rehash(struct dentry * entry);
+extern void d_rehash(struct dentry *);
 /*
  * This adds the entry to the hash queues and initializes "d_inode".
  * The entry was actually filled in earlier during "d_alloc()"
@@ -167,17 +165,16 @@
 }
 
 /* used for rename() and baskets */
-extern void d_move(struct dentry * entry, struct dentry * newdentry);
+extern void d_move(struct dentry *, struct dentry *);
 
 /* appendix may either be NULL or be used for transname suffixes */
-extern struct dentry * d_lookup(struct dentry * dir, struct qstr * name);
+extern struct dentry * d_lookup(struct dentry *, struct qstr *);
 
 /* validate "insecure" dentry pointer */
-extern int d_validate(struct dentry *dentry, struct dentry *dparent,
-		      unsigned int hash, unsigned int len);
+extern int d_validate(struct dentry *, struct dentry *, unsigned int, unsigned int);
 
 /* write full pathname into buffer and return start of pathname */
-extern char * d_path(struct dentry * entry, char * buf, int buflen);
+extern char * d_path(struct dentry *, char *, int);
 
 /* Allocation counts.. */
 static __inline__ struct dentry * dget(struct dentry *dentry)
diff -u --recursive --new-file v2.3.5/linux/include/linux/file.h linux/include/linux/file.h
--- v2.3.5/linux/include/linux/file.h	Sat Jan  9 19:16:44 1999
+++ linux/include/linux/file.h	Tue Jun  8 10:47:58 1999
@@ -44,7 +44,7 @@
 /*
  * Install a file pointer in the fd array.
  */
-extern inline void fd_install(unsigned int fd, struct file *file)
+extern inline void fd_install(unsigned int fd, struct file * file)
 {
 	current->files->fd[fd] = file;
 }
@@ -65,7 +65,7 @@
  * I suspect there are many other similar "optimizations" across the
  * kernel...
  */
-extern void fput(struct file *file); 
-extern void put_filp(struct file *file);
+extern void fput(struct file *); 
+extern void put_filp(struct file *);
 
-#endif
+#endif /* __LINUX_FILE_H */
diff -u --recursive --new-file v2.3.5/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.3.5/linux/include/linux/fs.h	Wed Jun  2 14:44:39 1999
+++ linux/include/linux/fs.h	Tue Jun  8 23:03:41 1999
@@ -103,7 +103,8 @@
 /*
  * Flags that can be altered by MS_REMOUNT
  */
-#define MS_RMT_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS|MS_MANDLOCK|MS_NOATIME|MS_NODIRATIME)
+#define MS_RMT_MASK	(MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|\
+			MS_SYNCHRONOUS|MS_MANDLOCK|MS_NOATIME|MS_NODIRATIME)
 
 /*
  * Magic mount flag number. Has to be or-ed to the flag values.
@@ -112,7 +113,7 @@
 #define MS_MGC_MSK 0xffff0000	/* magic flag number mask */
 
 /*
- * Note that read-only etc flags are inode-specific: setting some file-system
+ * Note that nosuid etc flags are inode-specific: setting some file-system
  * flags just means all the inodes inherit those flags by default. It might be
  * possible to override it selectively if you really wanted to with some
  * ioctl() that is not currently implemented.
@@ -171,9 +172,8 @@
 
 #include <asm/semaphore.h>
 #include <asm/byteorder.h>
-#include <asm/bitops.h>
 
-extern void update_atime (struct inode *inode);
+extern void update_atime (struct inode *);
 #define UPDATE_ATIME(inode) update_atime (inode)
 
 extern void buffer_init(unsigned long);
@@ -233,8 +233,7 @@
 };
 
 typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate);
-void init_buffer(struct buffer_head *bh, kdev_t dev, int block,
-		 bh_end_io_t *handler, void *dev_id);
+void init_buffer(struct buffer_head *, kdev_t, int, bh_end_io_t *, void *);
 
 static inline int buffer_uptodate(struct buffer_head * bh)
 {
@@ -480,19 +479,17 @@
 
 #include <linux/fcntl.h>
 
-extern int fcntl_getlk(unsigned int fd, struct flock *l);
-extern int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l);
+extern int fcntl_getlk(unsigned int, struct flock *);
+extern int fcntl_setlk(unsigned int, unsigned int, struct flock *);
 
 /* fs/locks.c */
-extern void locks_remove_posix(struct file *, fl_owner_t id);
+extern void locks_remove_posix(struct file *, fl_owner_t);
 extern void locks_remove_flock(struct file *);
 extern struct file_lock *posix_test_lock(struct file *, struct file_lock *);
 extern int posix_lock_file(struct file *, struct file_lock *, unsigned int);
 extern void posix_block_lock(struct file_lock *, struct file_lock *);
 extern void posix_unblock_lock(struct file_lock *);
 
-#include <linux/stat.h>
-
 struct fasync_struct {
 	int    magic;
 	int    fa_fd;
@@ -667,10 +664,8 @@
 #define FLOCK_VERIFY_READ  1
 #define FLOCK_VERIFY_WRITE 2
 
-extern int locks_mandatory_locked(struct inode *inode);
-extern int locks_mandatory_area(int read_write, struct inode *inode,
-				struct file *filp, loff_t offset,
-				size_t count);
+extern int locks_mandatory_locked(struct inode *);
+extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t);
 
 extern inline int locks_verify_locked(struct inode *inode)
 {
@@ -709,35 +704,35 @@
 extern struct file *filp_open(const char *, int, int);
 extern int filp_close(struct file *, fl_owner_t id);
 
-extern char * getname(const char * filename);
+extern char * getname(const char *);
 #define __getname()	((char *) __get_free_page(GFP_KERNEL))
 #define putname(name)	free_page((unsigned long)(name))
 
-extern void kill_fasync(struct fasync_struct *fa, int sig);
+extern void kill_fasync(struct fasync_struct *, int);
 extern int register_blkdev(unsigned int, const char *, struct file_operations *);
-extern int unregister_blkdev(unsigned int major, const char * name);
-extern int blkdev_open(struct inode * inode, struct file * filp);
-extern int blkdev_release (struct inode * inode);
+extern int unregister_blkdev(unsigned int, const char *);
+extern int blkdev_open(struct inode *, struct file *);
+extern int blkdev_release (struct inode *);
 extern struct file_operations def_blk_fops;
 extern struct inode_operations blkdev_inode_operations;
 
 /* fs/devices.c */
 extern int register_chrdev(unsigned int, const char *, struct file_operations *);
-extern int unregister_chrdev(unsigned int major, const char * name);
-extern int chrdev_open(struct inode * inode, struct file * filp);
+extern int unregister_chrdev(unsigned int, const char *);
+extern int chrdev_open(struct inode *, struct file *);
 extern struct file_operations def_chr_fops;
 extern struct inode_operations chrdev_inode_operations;
-extern char * bdevname(kdev_t dev);
-extern char * cdevname(kdev_t dev);
-extern char * kdevname(kdev_t dev);
+extern char * bdevname(kdev_t);
+extern char * cdevname(kdev_t);
+extern char * kdevname(kdev_t);
 extern void init_special_inode(struct inode *, umode_t, int);
 
-extern void init_fifo(struct inode * inode);
+extern void init_fifo(struct inode *);
 extern struct inode_operations fifo_inode_operations;
 
 /* Invalid inode operations -- fs/bad_inode.c */
-extern void make_bad_inode(struct inode * inode);
-extern int is_bad_inode(struct inode * inode);
+extern void make_bad_inode(struct inode *);
+extern int is_bad_inode(struct inode *);
 
 extern struct file_operations connecting_fifo_fops;
 extern struct file_operations read_fifo_fops;
@@ -747,15 +742,15 @@
 extern struct file_operations write_pipe_fops;
 extern struct file_operations rdwr_pipe_fops;
 
-extern struct file_system_type *get_fs_type(const char *name);
+extern struct file_system_type *get_fs_type(const char *);
 
 extern int fs_may_remount_ro(struct super_block *);
-extern int fs_may_mount(kdev_t dev);
+extern int fs_may_mount(kdev_t);
 
 extern struct file *inuse_filps;
 
-extern void refile_buffer(struct buffer_head * buf);
-extern void set_writetime(struct buffer_head * buf, int flag);
+extern void refile_buffer(struct buffer_head *);
+extern void set_writetime(struct buffer_head *, int);
 extern int try_to_free_buffers(struct page *);
 
 extern int nr_buffers;
@@ -767,7 +762,7 @@
 #define BUF_DIRTY	2	/* Dirty buffers, not yet scheduled for write */
 #define NR_LIST		3
 
-void mark_buffer_uptodate(struct buffer_head * bh, int on);
+void mark_buffer_uptodate(struct buffer_head *, int);
 
 extern inline void mark_buffer_clean(struct buffer_head * bh)
 {
@@ -786,23 +781,23 @@
 	}
 }
 
-extern int check_disk_change(kdev_t dev);
-extern int invalidate_inodes(struct super_block * sb);
+extern int check_disk_change(kdev_t);
+extern int invalidate_inodes(struct super_block *);
 extern void invalidate_inode_pages(struct inode *);
-extern void invalidate_buffers(kdev_t dev);
-extern int floppy_is_wp(int minor);
-extern void sync_inodes(kdev_t dev);
-extern void write_inode_now(struct inode *inode);
-extern void sync_dev(kdev_t dev);
-extern int fsync_dev(kdev_t dev);
-extern void sync_supers(kdev_t dev);
-extern int bmap(struct inode * inode,int block);
+extern void invalidate_buffers(kdev_t);
+extern int floppy_is_wp(int);
+extern void sync_inodes(kdev_t);
+extern void write_inode_now(struct inode *);
+extern void sync_dev(kdev_t);
+extern int fsync_dev(kdev_t);
+extern void sync_supers(kdev_t);
+extern int bmap(struct inode *, int);
 extern int notify_change(struct dentry *, struct iattr *);
-extern int permission(struct inode * inode,int mask);
-extern int get_write_access(struct inode *inode);
-extern void put_write_access(struct inode *inode);
-extern struct dentry * open_namei(const char * pathname, int flag, int mode);
-extern struct dentry * do_mknod(const char * filename, int mode, dev_t dev);
+extern int permission(struct inode *, int);
+extern int get_write_access(struct inode *);
+extern void put_write_access(struct inode *);
+extern struct dentry * open_namei(const char *, int, int);
+extern struct dentry * do_mknod(const char *, int, dev_t);
 extern int do_pipe(int *);
 
 /* fs/dcache.c -- generic fs support functions */
@@ -840,7 +835,7 @@
 #define lnamei(pathname)	__namei(pathname, 0)
 
 extern void iput(struct inode *);
-extern struct inode * igrab(struct inode *inode);
+extern struct inode * igrab(struct inode *);
 extern ino_t iunique(struct super_block *, ino_t);
 extern struct inode * iget(struct super_block *, unsigned long);
 extern void clear_inode(struct inode *);
@@ -851,7 +846,7 @@
 extern struct file * get_empty_filp(void);
 extern struct buffer_head * get_hash_table(kdev_t, int, int);
 extern struct buffer_head * getblk(kdev_t, int, int);
-extern struct buffer_head * find_buffer(kdev_t dev, int block, int size);
+extern struct buffer_head * find_buffer(kdev_t, int, int);
 extern void ll_rw_block(int, int, struct buffer_head * bh[]);
 extern int is_read_only(kdev_t);
 extern void __brelse(struct buffer_head *);
@@ -860,17 +855,16 @@
 	if (buf)
 		__brelse(buf);
 }
-extern void __bforget(struct buffer_head *buf);
+extern void __bforget(struct buffer_head *);
 extern inline void bforget(struct buffer_head *buf)
 {
 	if (buf)
 		__bforget(buf);
 }
-extern void set_blocksize(kdev_t dev, int size);
-extern unsigned int get_hardblocksize(kdev_t dev);
-extern struct buffer_head * bread(kdev_t dev, int block, int size);
-extern struct buffer_head * breada(kdev_t dev,int block, int size, 
-				   unsigned int pos, unsigned int filesize);
+extern void set_blocksize(kdev_t, int);
+extern unsigned int get_hardblocksize(kdev_t);
+extern struct buffer_head * bread(kdev_t, int, int);
+extern struct buffer_head * breada(kdev_t, int, int, unsigned int, unsigned int);
 
 extern int brw_page(int, struct page *, kdev_t, int [], int, int);
 
@@ -879,12 +873,12 @@
 extern int generic_readpage(struct file *, struct page *);
 extern int generic_file_mmap(struct file *, struct vm_area_struct *);
 extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
-extern ssize_t generic_file_write(struct file *, const char*, size_t, loff_t *, writepage_t);
+extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *, writepage_t);
 
-extern struct super_block *get_super(kdev_t dev);
-extern void put_super(kdev_t dev);
-unsigned long generate_cluster(kdev_t dev, int b[], int size);
-unsigned long generate_cluster_swab32(kdev_t dev, int b[], int size);
+extern struct super_block *get_super(kdev_t);
+extern void put_super(kdev_t);
+unsigned long generate_cluster(kdev_t, int b[], int);
+unsigned long generate_cluster_swab32(kdev_t, int b[], int);
 extern kdev_t ROOT_DEV;
 
 extern void show_buffers(void);
@@ -892,7 +886,7 @@
 
 #ifdef CONFIG_BLK_DEV_INITRD
 extern kdev_t real_root_dev;
-extern int change_root(kdev_t new_root_dev,const char *put_old);
+extern int change_root(kdev_t, const char *);
 #endif
 
 extern ssize_t char_read(struct file *, char *, size_t, loff_t *);
@@ -902,8 +896,8 @@
 extern ssize_t char_write(struct file *, const char *, size_t, loff_t *);
 extern ssize_t block_write(struct file *, const char *, size_t, loff_t *);
 
-extern int block_fsync(struct file *, struct dentry *dir);
-extern int file_fsync(struct file *, struct dentry *dir);
+extern int block_fsync(struct file *, struct dentry *);
+extern int file_fsync(struct file *, struct dentry *);
 
 extern int inode_change_ok(struct inode *, struct iattr *);
 extern void inode_setattr(struct inode *, struct iattr *);
@@ -914,4 +908,4 @@
 
 #endif /* __KERNEL__ */
 
-#endif
+#endif /* _LINUX_FS_H */
diff -u --recursive --new-file v2.3.5/linux/include/linux/i2o.h linux/include/linux/i2o.h
--- v2.3.5/linux/include/linux/i2o.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/i2o.h	Wed Jun  2 14:40:22 1999
@@ -0,0 +1,588 @@
+#ifndef _I2O_H
+#define _I2O_H
+
+/*
+ *	Tunable parameters first
+ */
+
+/* How many different OSM's are we allowing */ 
+#define MAX_I2O_MODULES		64
+/* How many controllers are we allowing */
+#define MAX_I2O_CONTROLLERS	32
+
+
+#ifdef __KERNEL__   /* ioctl stuff only thing exported to users */
+
+/*
+ *	I2O Interface Objects
+ */
+
+#include <linux/notifier.h>
+#include <asm/atomic.h>
+
+/*
+ * message structures
+ */
+
+#define    TID_SZ                                  12
+#define    FUNCTION_SZ                             8
+
+struct i2o_message
+{
+	u32	version_size;
+	u32	function_addr;
+	u32	initiator_context;
+	/* List follows */
+};
+
+
+/*
+ *	Each I2O device entity has one or more of these. There is one
+ *	per device. *FIXME* how to handle multiple types on one unit.
+ */
+ 
+struct i2o_device
+{
+	int class;		/* Block, Net, SCSI etc (from spec) */
+	int subclass;		/* eth, fddi, tr etc (from spec)   */
+	int id;			/* I2O ID assigned by the controller */
+	int parent;		/* Parent device */
+	int flags;		/* Control flags */
+	int i2oversion;		/* I2O version supported. Actually there
+				 * should be high and low version */
+	struct proc_dir_entry* proc_entry;	/* /proc dir */
+	struct i2o_driver *owner;		/* Owning device */
+	struct i2o_controller *controller;	/* Controlling IOP */
+	struct i2o_device *next;	/* Chain */
+	char dev_name[8];		/* linux /dev name if available */
+};
+
+/*
+ *	Resource data for each PCI I2O controller
+ */	 	
+
+struct i2o_pci
+{
+	int irq;
+};
+
+/*
+ *	Each I2O controller has one of these objects
+ */
+ 
+struct i2o_controller
+{
+	char name[16];
+	int unit;
+	int status;				/* I2O status */
+	int i2oversion;
+	int type;
+#define I2O_TYPE_PCI		0x01		/* PCI I2O controller */	
+	struct notifier_block *event_notifer;	/* Events */
+	atomic_t users;
+	struct i2o_device *devices;		/* I2O device chain */
+	struct i2o_controller *next;		/* Controller chain */
+	volatile u32 *post_port;		/* Messaging ports */
+	volatile u32 *reply_port;
+	volatile u32 *irq_mask;			/* Interrupt port */
+	u32 mem_offset;				/* MFA offset */
+	u32 mem_phys;				/* MFA physical */
+	u32 priv_mem;
+	u32 priv_mem_size;
+	u32 priv_io;
+	u32 priv_io_size;
+
+	struct proc_dir_entry* proc_entry;	/* /proc dir */
+
+	union
+	{					/* Bus information */
+		struct i2o_pci pci;
+	} bus;
+	void (*destructor)(struct i2o_controller *);			/* Bus specific destructor */
+	int (*bind)(struct i2o_controller *, struct i2o_device *);	/* Bus specific attach/detach */
+	int (*unbind)(struct i2o_controller *, struct i2o_device *);
+	void *page_frame;		/* Message buffers */
+	int inbound_size;		/* Inbound queue size */
+};
+
+struct i2o_handler
+{
+	void (*reply)(struct i2o_handler *, struct i2o_controller *, struct i2o_message *);
+	char *name;
+	int context;		/* Low 8 bits of the transaction info */
+	/* User data follows */
+};
+
+/*
+ *	Messenger inlines
+ */
+
+extern inline u32 I2O_POST_READ32(struct i2o_controller *c)
+{
+	return *c->post_port;
+}
+
+extern inline void I2O_POST_WRITE32(struct i2o_controller *c, u32 Val)
+{
+	*c->post_port = Val;
+}
+
+
+extern inline u32 I2O_REPLY_READ32(struct i2o_controller *c)
+{
+	return *c->reply_port;
+}
+
+extern inline void I2O_REPLY_WRITE32(struct i2o_controller *c, u32 Val)
+{
+	*c->reply_port= Val;
+}
+ 
+
+extern inline u32 I2O_IRQ_READ32(struct i2o_controller *c)
+{
+	return *c->irq_mask;
+}
+
+extern inline void I2O_IRQ_WRITE32(struct i2o_controller *c, u32 Val)
+{
+	*c->irq_mask = Val;
+}
+
+
+extern inline void i2o_post_message(struct i2o_controller *c, u32 m)
+{
+	/* The second line isnt spurious - thats forcing PCI posting */
+	I2O_POST_WRITE32(c,m);
+	(void) I2O_IRQ_READ32(c);
+}
+
+extern inline void i2o_flush_reply(struct i2o_controller *c, u32 m)
+{
+	I2O_REPLY_WRITE32(c,m);
+}
+
+
+struct i2o_controller *i2o_controller_chain;
+
+extern int i2o_quiesce_controller(struct i2o_controller *);
+extern int i2o_clear_controller(struct i2o_controller *);
+extern int i2o_install_controller(struct i2o_controller *);
+extern int i2o_delete_controller(struct i2o_controller *);
+extern int i2o_activate_controller(struct i2o_controller *);
+extern void i2o_unlock_controller(struct i2o_controller *);
+extern struct i2o_controller *i2o_find_controller(int);
+extern int i2o_num_controllers;
+
+extern int i2o_install_handler(struct i2o_handler *);
+extern int i2o_remove_handler(struct i2o_handler *);
+
+extern int i2o_install_device(struct i2o_controller *, struct i2o_device *);
+extern int i2o_delete_device(struct i2o_device *);
+extern int i2o_claim_device(struct i2o_device *, struct i2o_driver *);
+extern int i2o_release_device(struct i2o_device *);
+
+extern int i2o_post_this(struct i2o_controller *, int, u32 *, int);
+extern int i2o_post_wait(struct i2o_controller *, int, u32 *, int, int *, int);
+extern int i2o_issue_claim(struct i2o_controller *, int, int, int, int *);
+extern int i2o_query_scalar(struct i2o_controller *, int, int, int, int, void *,
+			    int, int *);
+extern int i2o_params_set(struct i2o_controller *c, int, int, int, int, void *,
+			  int, int *);
+
+extern void i2o_run_queue(struct i2o_controller *);
+
+extern void i2o_report_status(const char *, const char *, u8, u8, u16);
+extern void report_common_status(u8);
+extern void report_lan_dsc(u16);
+
+extern u32 i2o_wait_message(struct i2o_controller *, char *);
+
+extern const char *i2o_get_class_name(int);
+
+
+/*
+ *	I2O classes / subclasses
+ */
+
+/*  Class ID and Code Assignments
+ *  (LCT.ClassID.Version field)
+ */
+#define    I2O_CLASS_VERSION_10                        0x00
+#define    I2O_CLASS_VERSION_11                        0x01
+
+/*  Class code names
+ *  (from v1.5 Table 6-1 Class Code Assignments.)
+ */
+ 
+#define    I2O_CLASS_EXECUTIVE                         0x000
+#define    I2O_CLASS_DDM                               0x001
+#define    I2O_CLASS_RANDOM_BLOCK_STORAGE              0x010
+#define    I2O_CLASS_SEQUENTIAL_STORAGE                0x011
+#define    I2O_CLASS_LAN                               0x020
+#define    I2O_CLASS_WAN                               0x030
+#define    I2O_CLASS_FIBRE_CHANNEL_PORT                0x040
+#define    I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL          0x041
+#define    I2O_CLASS_SCSI_PERIPHERAL                   0x051
+#define    I2O_CLASS_ATE_PORT                          0x060
+#define    I2O_CLASS_ATE_PERIPHERAL                    0x061
+#define    I2O_CLASS_FLOPPY_CONTROLLER                 0x070
+#define    I2O_CLASS_FLOPPY_DEVICE                     0x071
+#define    I2O_CLASS_BUS_ADAPTER_PORT                  0x080
+#define    I2O_CLASS_PEER_TRANSPORT_AGENT              0x090
+#define    I2O_CLASS_PEER_TRANSPORT                    0x091
+
+/*  Rest of 0x092 - 0x09f reserved for peer-to-peer classes
+ */
+ 
+#define    I2O_CLASS_MATCH_ANYCLASS                    0xffffffff
+
+/*  Subclasses
+ */
+
+#define    I2O_SUBCLASS_i960                           0x001
+#define    I2O_SUBCLASS_HDM                            0x020
+#define    I2O_SUBCLASS_ISM                            0x021
+ 
+/* Operation functions */
+
+#define I2O_PARAMS_FIELD_GET	0x0001
+#define I2O_PARAMS_LIST_GET	0x0002
+#define I2O_PARAMS_MORE_GET	0x0003
+#define I2O_PARAMS_SIZE_GET	0x0004
+#define I2O_PARAMS_TABLE_GET	0x0005
+#define I2O_PARAMS_FIELD_SET	0x0006
+#define I2O_PARAMS_LIST_SET	0x0007
+#define I2O_PARAMS_ROW_ADD	0x0008
+#define I2O_PARAMS_ROW_DELETE	0x0009
+#define I2O_PARAMS_TABLE_CLEAR	0x000A
+
+/*
+ *	I2O serial number conventions / formats
+ *	(circa v1.5)
+ */
+
+#define    I2O_SNFORMAT_UNKNOWN                        0
+#define    I2O_SNFORMAT_BINARY                         1
+#define    I2O_SNFORMAT_ASCII                          2
+#define    I2O_SNFORMAT_UNICODE                        3
+#define    I2O_SNFORMAT_LAN48_MAC                      4
+#define    I2O_SNFORMAT_WAN                            5
+
+/* Plus new in v2.0 (Yellowstone pdf doc)
+ */
+
+#define    I2O_SNFORMAT_LAN64_MAC                      6
+#define    I2O_SNFORMAT_DDM                            7
+#define    I2O_SNFORMAT_IEEE_REG64                     8
+#define    I2O_SNFORMAT_IEEE_REG128                    9
+#define    I2O_SNFORMAT_UNKNOWN2                       0xff
+
+
+/*
+ *	"Special" TID assignments
+ */
+#define    I2O_IOP_TID                                 0
+#define    I2O_HOST_TID                                1
+
+
+/* Transaction Reply Lists (TRL) Control Word structure */
+
+#define TRL_SINGLE_FIXED_LENGTH		0x00
+#define TRL_SINGLE_VARIABLE_LENGTH	0x40
+#define TRL_MULTIPLE_FIXED_LENGTH	0x80
+
+/* LAN Class specific functions */
+
+#define    LAN_PACKET_SEND			0x3B
+#define    LAN_SDU_SEND				0x3D
+#define    LAN_RECEIVE_POST			0x3E
+#define    LAN_RESET				0x35
+#define    LAN_SUSPEND				0x37
+
+/*
+ *	Messaging API values
+ */
+ 
+#define	I2O_CMD_ADAPTER_ASSIGN		0xB3
+#define	I2O_CMD_ADAPTER_READ		0xB2
+#define	I2O_CMD_ADAPTER_RELEASE		0xB5
+#define	I2O_CMD_BIOS_INFO_SET		0xA5
+#define	I2O_CMD_BOOT_DEVICE_SET		0xA7
+#define	I2O_CMD_CONFIG_VALIDATE		0xBB
+#define	I2O_CMD_CONN_SETUP		0xCA
+#define	I2O_CMD_DDM_DESTROY		0xB1
+#define	I2O_CMD_DDM_ENABLE		0xD5
+#define	I2O_CMD_DDM_QUIESCE		0xC7
+#define	I2O_CMD_DDM_RESET		0xD9
+#define	I2O_CMD_DDM_SUSPEND		0xAF
+#define	I2O_CMD_DEVICE_ASSIGN		0xB7
+#define	I2O_CMD_DEVICE_RELEASE		0xB9
+#define	I2O_CMD_HRT_GET			0xA8
+#define	I2O_CMD_ADAPTER_CLEAR		0xBE
+#define	I2O_CMD_ADAPTER_CONNECT		0xC9
+#define	I2O_CMD_ADAPTER_RESET		0xBD
+#define	I2O_CMD_LCT_NOTIFY		0xA2
+#define	I2O_CMD_OUTBOUND_INIT		0xA1
+#define	I2O_CMD_PATH_ENABLE		0xD3
+#define	I2O_CMD_PATH_QUIESCE		0xC5
+#define	I2O_CMD_PATH_RESET		0xD7
+#define	I2O_CMD_STATIC_MF_CREATE	0xDD
+#define	I2O_CMD_STATIC_MF_RELEASE	0xDF
+#define	I2O_CMD_STATUS_GET		0xA0
+#define	I2O_CMD_SW_DOWNLOAD		0xA9
+#define	I2O_CMD_SW_UPLOAD		0xAB
+#define	I2O_CMD_SW_REMOVE		0xAD
+#define	I2O_CMD_SYS_ENABLE		0xD1
+#define	I2O_CMD_SYS_MODIFY		0xC1
+#define	I2O_CMD_SYS_QUIESCE		0xC3
+#define	I2O_CMD_SYS_TAB_SET		0xA3
+
+#define I2O_CMD_UTIL_NOP		0x00
+#define I2O_CMD_UTIL_ABORT		0x01
+#define I2O_CMD_UTIL_CLAIM		0x09
+#define I2O_CMD_UTIL_RELEASE		0x0B
+#define I2O_CMD_UTIL_PARAMS_GET		0x06
+#define I2O_CMD_UTIL_PARAMS_SET		0x05
+#define I2O_CMD_UTIL_EVT_REGISTER	0x13
+#define I2O_CMD_UTIL_ACK		0x14
+#define I2O_CMD_UTIL_CONFIG_DIALOG	0x10
+#define I2O_CMD_UTIL_DEVICE_RESERVE	0x0D
+#define I2O_CMD_UTIL_DEVICE_RELEASE	0x0F
+#define I2O_CMD_UTIL_LOCK		0x17
+#define I2O_CMD_UTIL_LOCK_RELEASE	0x19
+#define I2O_CMD_UTIL_REPLY_FAULT_NOTIFY	0x15
+
+#define I2O_CMD_SCSI_EXEC		0x81
+#define I2O_CMD_SCSI_ABORT		0x83
+#define I2O_CMD_SCSI_BUSRESET		0x27
+
+#define I2O_CMD_BLOCK_READ		0x30
+#define I2O_CMD_BLOCK_WRITE		0x31
+#define I2O_CMD_BLOCK_CFLUSH		0x37
+#define I2O_CMD_BLOCK_MLOCK		0x49
+#define I2O_CMD_BLOCK_MUNLOCK		0x4B
+#define I2O_CMD_BLOCK_MMOUNT		0x41
+#define I2O_CMD_BLOCK_MEJECT		0x43
+
+#define I2O_PRIVATE_MSG			0xFF
+
+/*
+ *	Init Outbound Q status 
+ */
+ 
+#define I2O_CMD_OUTBOUND_INIT_IN_PROGRESS	0x01
+#define I2O_CMD_OUTBOUND_INIT_REJECTED		0x02
+#define I2O_CMD_OUTBOUND_INIT_FAILED		0x03
+#define I2O_CMD_OUTBOUND_INIT_COMPLETE		0x04
+
+/*
+ *	I2O Get Status State values 
+ */
+
+#define	ADAPTER_STATE_INITIALIZING		0x01
+#define	ADAPTER_STATE_RESET			0x02
+#define	ADAPTER_STATE_HOLD			0x04
+#define ADAPTER_STATE_READY			0x05
+#define	ADAPTER_STATE_OPERATIONAL		0x08
+#define	ADAPTER_STATE_FAILED			0x10
+#define	ADAPTER_STATE_FAULTED			0x11
+	
+/* I2O API function return values */
+
+#define I2O_RTN_NO_ERROR			0
+#define I2O_RTN_NOT_INIT			1
+#define I2O_RTN_FREE_Q_EMPTY			2
+#define I2O_RTN_TCB_ERROR			3
+#define I2O_RTN_TRANSACTION_ERROR		4
+#define I2O_RTN_ADAPTER_ALREADY_INIT		5
+#define I2O_RTN_MALLOC_ERROR			6
+#define I2O_RTN_ADPTR_NOT_REGISTERED		7
+#define I2O_RTN_MSG_REPLY_TIMEOUT		8
+#define I2O_RTN_NO_STATUS			9
+#define I2O_RTN_NO_FIRM_VER			10
+#define	I2O_RTN_NO_LINK_SPEED			11
+
+/* Reply message status defines for all messages */
+
+#define I2O_REPLY_STATUS_SUCCESS                    	0x00
+#define I2O_REPLY_STATUS_ABORT_DIRTY                	0x01
+#define I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER     	0x02
+#define	I2O_REPLY_STATUS_ABORT_PARTIAL_TRANSFER		0x03
+#define	I2O_REPLY_STATUS_ERROR_DIRTY			0x04
+#define	I2O_REPLY_STATUS_ERROR_NO_DATA_TRANSFER		0x05
+#define	I2O_REPLY_STATUS_ERROR_PARTIAL_TRANSFER		0x06
+#define	I2O_REPLY_STATUS_PROCESS_ABORT_DIRTY		0x08
+#define	I2O_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER	0x09
+#define	I2O_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER	0x0A
+#define	I2O_REPLY_STATUS_TRANSACTION_ERROR		0x0B
+#define	I2O_REPLY_STATUS_PROGRESS_REPORT		0x80
+
+/* Status codes and Error Information for Parameter functions */
+
+#define I2O_PARAMS_STATUS_SUCCESS		0x00
+#define I2O_PARAMS_STATUS_BAD_KEY_ABORT		0x01
+#define I2O_PARAMS_STATUS_BAD_KEY_CONTINUE   	0x02
+#define I2O_PARAMS_STATUS_BUFFER_FULL		0x03
+#define I2O_PARAMS_STATUS_BUFFER_TOO_SMALL	0x04
+#define I2O_PARAMS_STATUS_FIELD_UNREADABLE	0x05
+#define I2O_PARAMS_STATUS_FIELD_UNWRITEABLE	0x06
+#define I2O_PARAMS_STATUS_INSUFFICIENT_FIELDS	0x07
+#define I2O_PARAMS_STATUS_INVALID_GROUP_ID	0x08
+#define I2O_PARAMS_STATUS_INVALID_OPERATION	0x09
+#define I2O_PARAMS_STATUS_NO_KEY_FIELD		0x0A
+#define I2O_PARAMS_STATUS_NO_SUCH_FIELD		0x0B
+#define I2O_PARAMS_STATUS_NON_DYNAMIC_GROUP	0x0C
+#define I2O_PARAMS_STATUS_OPERATION_ERROR	0x0D
+#define I2O_PARAMS_STATUS_SCALAR_ERROR		0x0E
+#define I2O_PARAMS_STATUS_TABLE_ERROR		0x0F
+#define I2O_PARAMS_STATUS_WRONG_GROUP_TYPE	0x10
+
+/* DetailedStatusCode defines for Executive, DDM, Util and Transaction error
+ * messages: Table 3-2 Detailed Status Codes.*/
+
+#define I2O_DSC_SUCCESS                        0x0000
+#define I2O_DSC_BAD_KEY                        0x0002
+#define I2O_DSC_TCL_ERROR                      0x0003
+#define I2O_DSC_REPLY_BUFFER_FULL              0x0004
+#define I2O_DSC_NO_SUCH_PAGE                   0x0005
+#define I2O_DSC_INSUFFICIENT_RESOURCE_SOFT     0x0006
+#define I2O_DSC_INSUFFICIENT_RESOURCE_HARD     0x0007
+#define I2O_DSC_CHAIN_BUFFER_TOO_LARGE         0x0009
+#define I2O_DSC_UNSUPPORTED_FUNCTION           0x000A
+#define I2O_DSC_DEVICE_LOCKED                  0x000B
+#define I2O_DSC_DEVICE_RESET                   0x000C
+#define I2O_DSC_INAPPROPRIATE_FUNCTION         0x000D
+#define I2O_DSC_INVALID_INITIATOR_ADDRESS      0x000E
+#define I2O_DSC_INVALID_MESSAGE_FLAGS          0x000F
+#define I2O_DSC_INVALID_OFFSET                 0x0010
+#define I2O_DSC_INVALID_PARAMETER              0x0011
+#define I2O_DSC_INVALID_REQUEST                0x0012
+#define I2O_DSC_INVALID_TARGET_ADDRESS         0x0013
+#define I2O_DSC_MESSAGE_TOO_LARGE              0x0014
+#define I2O_DSC_MESSAGE_TOO_SMALL              0x0015
+#define I2O_DSC_MISSING_PARAMETER              0x0016
+#define I2O_DSC_TIMEOUT                        0x0017
+#define I2O_DSC_UNKNOWN_ERROR                  0x0018
+#define I2O_DSC_UNKNOWN_FUNCTION               0x0019
+#define I2O_DSC_UNSUPPORTED_VERSION            0x001A
+#define I2O_DSC_DEVICE_BUSY                    0x001B
+#define I2O_DSC_DEVICE_NOT_AVAILABLE           0x001C
+
+/* Message header defines for VersionOffset */
+#define I2OVER15	0x0001
+#define I2OVER20	0x0002
+/* Default is 1.5, FIXME: Need support for both 1.5 and 2.0 */
+#define I2OVERSION	I2OVER15
+#define SGL_OFFSET_0    I2OVERSION
+#define SGL_OFFSET_4    (0x0040 | I2OVERSION)
+#define SGL_OFFSET_5    (0x0050 | I2OVERSION)
+#define SGL_OFFSET_6    (0x0060 | I2OVERSION)
+#define SGL_OFFSET_8    (0x0080 | I2OVERSION)
+#define SGL_OFFSET_10   (0x00A0 | I2OVERSION)
+
+#define TRL_OFFSET_5    (0x0050 | I2OVERSION)
+#define TRL_OFFSET_6    (0x0060 | I2OVERSION)
+
+ /* msg header defines for MsgFlags */
+#define MSG_STATIC	0x0100
+#define MSG_64BIT_CNTXT	0x0200
+#define MSG_MULTI_TRANS	0x1000
+#define MSG_FAIL	0x2000
+#define MSG_LAST	0x4000
+#define MSG_REPLY	0x8000
+
+  /* normal LAN request message MsgFlags and VersionOffset (0x1041) */
+#define LAN_MSG_REQST	(MSG_MULTI_TRANS | SGL_OFFSET_4)
+
+ /* minimum size msg */
+#define THREE_WORD_MSG_SIZE	0x00030000
+#define FOUR_WORD_MSG_SIZE	0x00040000
+#define FIVE_WORD_MSG_SIZE	0x00050000
+#define SIX_WORD_MSG_SIZE	0x00060000
+#define SEVEN_WORD_MSG_SIZE	0x00070000
+#define EIGHT_WORD_MSG_SIZE	0x00080000
+#define NINE_WORD_MSG_SIZE	0x00090000
+#define TEN_WORD_MSG_SIZE	0x000A0000
+#define I2O_MESSAGE_SIZE(x)	((x)<<16)
+
+
+/* Special TID Assignments */
+
+#define ADAPTER_TID		0
+#define HOST_TID		1
+
+#define MSG_FRAME_SIZE		128
+#define NMBR_MSG_FRAMES		128
+
+#define MSG_POOL_SIZE		16384
+
+#define I2O_POST_WAIT_OK	1
+#define I2O_POST_WAIT_TIMEOUT	-1
+
+#endif /* __KERNEL__ */
+
+#include <asm/ioctl.h>
+
+/*
+ * I2O Control IOCTLs and structures
+ */
+#define I2O_MAGIC_NUMBER	'i'
+#define I2OGETIOPS		_IO(I2O_MAGIC_NUMBER,0)
+#define I2OHRTGET		_IO(I2O_MAGIC_NUMBER,1)
+#define I2OLCTGET		_IO(I2O_MAGIC_NUMBER,2)
+#define I2OPARMSET		_IO(I2O_MAGIC_NUMBER,3)
+#define I2OPARMGET		_IO(I2O_MAGIC_NUMBER,4)
+#define I2OSWDL			_IO(I2O_MAGIC_NUMBER,5)
+#define I2OSWUL			_IO(I2O_MAGIC_NUMBER,6)
+#define I2OSWDEL		_IO(I2O_MAGIC_NUMBER,7)
+#define I2OHTML			_IO(I2O_MAGIC_NUMBER,8)
+
+/* On hold until we figure this out
+#define I2OEVTREG		_IO(I2O_MAGIC_NUMBER,9)
+#define I2OEVTCLR		_IO(I2O_MAGIC_NUMBER,10)
+#define I2OEVTGET		_IO(I2O_MAGIC_NUMBER,11)
+ */
+
+struct i2o_cmd_hrtlct
+{
+	unsigned int iop;  /* IOP unit number */
+	void  *resbuf;  /* Buffer for result */
+	unsigned int *reslen;  /* Buffer length in bytes */
+};
+
+
+struct i2o_cmd_psetget
+{
+	unsigned int iop;      /* IOP unit number */
+	unsigned int tid;      /* Target device TID */
+	void  *opbuf;   /* Operation List buffer */
+	unsigned int   oplen;    /* Operation List buffer length in bytes */
+	void  *resbuf;  /* Result List buffer */
+	unsigned int  *reslen;  /* Result List buffer length in bytes */
+};
+
+struct i2o_sw_xfer
+{
+	unsigned int iop;	/* IOP unit number */
+	unsigned char dl_flags;	/* DownLoadFlags field */
+	unsigned char sw_type;	/* Software type */
+	unsigned int sw_id;     /* Software ID */
+	void  *buf;      	/* Pointer to software buffer */
+	unsigned int *swlen;    /* Length of software data */
+	unsigned int *maxfrag;  /* Maximum fragment count */
+        unsigned int *curfrag;  /* Current fragment count */
+};
+
+struct i2o_html
+{
+	unsigned int iop;      /* IOP unit number */
+	unsigned int tid;      /* Target device ID */
+	unsigned int page;     /* HTML page */
+	void  *resbuf;  /* Buffer for reply HTML page */
+	unsigned int *reslen;  /* Length in bytes of reply buffer */
+	void  *qbuf;    /* Pointer to HTTP query string */
+	unsigned int qlen;     /* Length in bytes of query string buffer */
+};
+
+#endif
diff -u --recursive --new-file v2.3.5/linux/include/linux/igmp.h linux/include/linux/igmp.h
--- v2.3.5/linux/include/linux/igmp.h	Mon Jan 12 15:28:18 1998
+++ linux/include/linux/igmp.h	Wed Jun  9 14:45:36 1999
@@ -101,19 +101,7 @@
 	char			loaded;
 };
 
-extern __inline__ int ip_check_mc(struct device *dev, u32 mc_addr)
-{
-	struct in_device *in_dev = dev->ip_ptr;
-	struct ip_mc_list *im;
-
-	if (in_dev) {
-		for (im=in_dev->mc_list; im; im=im->next)
-			if (im->multiaddr == mc_addr)
-				return 1;
-	}
-	return 0;
-}
-
+extern int ip_check_mc(struct device *dev, u32 mc_addr);
 extern int igmp_rcv(struct sk_buff *, unsigned short);
 extern int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr);
 extern int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr);
diff -u --recursive --new-file v2.3.5/linux/include/linux/inet.h linux/include/linux/inet.h
--- v2.3.5/linux/include/linux/inet.h	Thu May 15 16:48:05 1997
+++ linux/include/linux/inet.h	Wed Jun  9 14:45:36 1999
@@ -46,6 +46,7 @@
 
 extern void		inet_proto_init(struct net_proto *pro);
 extern char		*in_ntoa(__u32 in);
+extern char		*in_ntoa2(__u32 in, char *buf);
 extern __u32		in_aton(const char *str);
 
 #endif
diff -u --recursive --new-file v2.3.5/linux/include/linux/inetdevice.h linux/include/linux/inetdevice.h
--- v2.3.5/linux/include/linux/inetdevice.h	Mon Jan  4 15:31:35 1999
+++ linux/include/linux/inetdevice.h	Wed Jun  9 14:45:36 1999
@@ -74,7 +74,7 @@
 extern void		devinet_init(void);
 extern struct in_device *inetdev_init(struct device *dev);
 extern struct in_device	*inetdev_by_index(int);
-extern u32		inet_select_addr(struct device *dev, u32 dst, int scope);
+extern u32		inet_select_addr(const struct device *dev, u32 dst, int scope);
 extern struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, u32 prefix, u32 mask);
 extern void		inet_forward_change(void);
 
diff -u --recursive --new-file v2.3.5/linux/include/linux/mm.h linux/include/linux/mm.h
--- v2.3.5/linux/include/linux/mm.h	Fri May 14 18:55:29 1999
+++ linux/include/linux/mm.h	Tue Jun  8 23:03:47 1999
@@ -314,6 +314,7 @@
 extern unsigned long do_mmap(struct file *, unsigned long, unsigned long,
 	unsigned long, unsigned long, unsigned long);
 extern int do_munmap(unsigned long, size_t);
+extern unsigned long do_brk(unsigned long, unsigned long);
 
 /* filemap.c */
 extern void remove_inode_page(struct page *);
@@ -381,6 +382,8 @@
 		vma = NULL;
 	return vma;
 }
+
+extern struct vm_area_struct *find_extend_vma(struct task_struct *tsk, unsigned long addr);
 
 #define buffer_under_min()	((buffermem >> PAGE_SHIFT) * 100 < \
 				buffer_mem.min_percent * num_physpages)
diff -u --recursive --new-file v2.3.5/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.3.5/linux/include/linux/netdevice.h	Mon May 31 22:28:06 1999
+++ linux/include/linux/netdevice.h	Wed Jun  9 14:45:36 1999
@@ -152,6 +152,7 @@
 	struct hh_cache *hh_next;	/* Next entry			     */
 	atomic_t	hh_refcnt;	/* number of users                   */
 	unsigned short  hh_type;	/* protocol identifier, f.e ETH_P_IP */
+	int		hh_len;		/* length of header */
 	int		(*hh_output)(struct sk_buff *skb);
 	rwlock_t	hh_lock;
 	/* cached hardware header; allow for machine alignment needs.        */
@@ -260,6 +261,7 @@
 	void 			*atalk_ptr;	/* AppleTalk link 	*/
 	void			*ip_ptr;	/* IPv4 specific data	*/  
 	void                    *dn_ptr;        /* DECnet specific data */
+	void                    *ip6_ptr;       /* IPv6 specific data */
 
 	struct Qdisc		*qdisc;
 	struct Qdisc		*qdisc_sleeping;
@@ -268,6 +270,13 @@
 
 	/* hard_start_xmit synchronizer */
 	spinlock_t		xmit_lock;
+	/* cpu id of processor entered to hard_start_xmit or -1,
+	   if nobody entered there.
+	 */
+	int			xmit_lock_owner;
+	/* device queue lock */
+	spinlock_t		queue_lock;
+	atomic_t		refcnt;
 
 	/* Pointers to interface service routines.	*/
 	int			(*open)(struct device *dev);
diff -u --recursive --new-file v2.3.5/linux/include/linux/nfs.h linux/include/linux/nfs.h
--- v2.3.5/linux/include/linux/nfs.h	Sat Mar  6 14:21:13 1999
+++ linux/include/linux/nfs.h	Tue Jun  8 22:11:58 1999
@@ -158,6 +158,11 @@
 	const char *		name;
 };
 
+struct nfs_readlinkargs {
+	struct nfs_fh *		fh;
+	const void *		buffer;
+};
+
 struct nfs_readargs {
 	struct nfs_fh *		fh;
 	__u32			offset;
@@ -195,7 +200,7 @@
 	struct nfs_fh *		fh;
 	__u32			cookie;
 	void *			buffer;
-	unsigned int		bufsiz;
+	int			bufsiz;
 };
 
 struct nfs_diropok {
@@ -208,16 +213,10 @@
 	unsigned int		count;
 };
 
-struct nfs_readlinkres {
-	char **			string;
-	unsigned int *		lenp;
-	unsigned int		maxlen;
-	void *			buffer;
-};
-
 struct nfs_readdirres {
 	void *			buffer;
-	unsigned int		bufsiz;
+	int			bufsiz;
+	u32			cookie;
 };
 
 #endif /* NFS_NEED_XDR_TYPES */
diff -u --recursive --new-file v2.3.5/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- v2.3.5/linux/include/linux/nfs_fs.h	Wed Jun  2 14:44:39 1999
+++ linux/include/linux/nfs_fs.h	Tue Jun  8 23:03:44 1999
@@ -79,6 +79,8 @@
 #define NFS_FLAGS(inode)		((inode)->u.nfs_i.flags)
 #define NFS_REVALIDATING(inode)		(NFS_FLAGS(inode) & NFS_INO_REVALIDATE)
 #define NFS_WRITEBACK(inode)		((inode)->u.nfs_i.writeback)
+#define NFS_COOKIES(inode)		((inode)->u.nfs_i.cookies)
+#define NFS_DIREOF(inode)		((inode)->u.nfs_i.direof)
 
 /*
  * These are the default flags for swap requests
@@ -139,9 +141,6 @@
 extern int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir,
 			const char *name, struct nfs_fh *fhandle,
 			struct nfs_fattr *fattr);
-extern int nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
-			void **p0, char **string, unsigned int *len,
-			unsigned int maxlen);
 extern int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle,
 			int swap, unsigned long offset, unsigned int count,
 			void *buffer, struct nfs_fattr *fattr);
@@ -166,8 +165,6 @@
 			struct nfs_fh *fhandle, struct nfs_fattr *fattr);
 extern int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir,
 			const char *name);
-extern int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
-			u32 cookie, unsigned int size, __u32 *entry);
 extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
 			struct nfs_fsinfo *res);
 
@@ -195,9 +192,7 @@
  */
 extern struct inode_operations nfs_dir_inode_operations;
 extern struct dentry_operations nfs_dentry_operations;
-extern void nfs_free_dircache(void);
 extern void nfs_invalidate_dircache(struct inode *);
-extern void nfs_invalidate_dircache_sb(struct super_block *);
 
 /*
  * linux/fs/nfs/symlink.c
diff -u --recursive --new-file v2.3.5/linux/include/linux/nfs_fs_i.h linux/include/linux/nfs_fs_i.h
--- v2.3.5/linux/include/linux/nfs_fs_i.h	Sat Mar  6 15:04:35 1999
+++ linux/include/linux/nfs_fs_i.h	Tue Jun  8 22:42:23 1999
@@ -47,6 +47,10 @@
 	 * pages.
 	 */
 	struct nfs_wreq *	writeback;
+
+	/* Readdir caching information. */
+	void *cookies;
+	u32 direof;
 };
 
 /*
diff -u --recursive --new-file v2.3.5/linux/include/linux/pagemap.h linux/include/linux/pagemap.h
--- v2.3.5/linux/include/linux/pagemap.h	Mon May 31 22:28:06 1999
+++ linux/include/linux/pagemap.h	Tue Jun  8 23:03:47 1999
@@ -28,6 +28,7 @@
 #define PAGE_CACHE_SHIFT	PAGE_SHIFT
 #define PAGE_CACHE_SIZE		PAGE_SIZE
 #define PAGE_CACHE_MASK		PAGE_MASK
+#define PAGE_CACHE_ALIGN(addr)	(((addr)+PAGE_CACHE_SIZE-1)&PAGE_CACHE_MASK)
 
 #define page_cache_alloc()	__get_free_page(GFP_USER)
 #define page_cache_free(x)	free_page(x)
diff -u --recursive --new-file v2.3.5/linux/include/linux/parport.h linux/include/linux/parport.h
--- v2.3.5/linux/include/linux/parport.h	Mon May 31 22:28:06 1999
+++ linux/include/linux/parport.h	Tue Jun  8 23:04:38 1999
@@ -103,9 +103,6 @@
 
 	void (*change_mode)(struct parport *, int);
 
-	void (*release_resources)(struct parport *);
-	int (*claim_resources)(struct parport *);
-
 	void (*epp_write_data)(struct parport *, unsigned char);
 	unsigned char (*epp_read_data)(struct parport *);
 	void (*epp_write_addr)(struct parport *, unsigned char);
@@ -169,6 +166,7 @@
 	unsigned int waiting;
 	struct pardevice *waitprev;
 	struct pardevice *waitnext;
+        void * sysctl_table;
 };
 
 /* Directory information for the /proc interface */
@@ -210,6 +208,7 @@
 	spinlock_t pardevice_lock;
 	spinlock_t waitlist_lock;
 	rwlock_t cad_lock;
+        void * sysctl_table;
 };
 
 struct parport_driver {
@@ -240,10 +239,6 @@
 /* parport_in_use returns nonzero if there are devices attached to a port. */
 #define parport_in_use(x)  ((x)->devices != NULL)
 
-/* Put a parallel port to sleep; release its hardware resources.  Only possible
- * if no devices are registered.  */
-extern void parport_quiesce(struct parport *);
-
 /* parport_enumerate returns a pointer to the linked list of all the ports
  * in this machine.
  */
@@ -339,7 +334,7 @@
 #define PARPORT_DEV_LURK		(1<<0)	/* WARNING !! DEPRECATED !! */
 #define PARPORT_DEV_EXCL		(1<<1)	/* Need exclusive access. */
 
-#define PARPORT_FLAG_COMA		(1<<0)
+#define PARPORT_FLAG_COMA_		(1<<0)  /* No longer used. */
 #define PARPORT_FLAG_EXCL		(1<<1)	/* EXCL driver registered. */
 
 extern int parport_parse_irqs(int, const char *[], int irqval[]);
@@ -349,10 +344,12 @@
 				   char);
 
 /* Prototypes from parport_procfs */
-extern int parport_proc_init(void);
-extern void parport_proc_cleanup(void);
 extern int parport_proc_register(struct parport *pp);
 extern int parport_proc_unregister(struct parport *pp);
+extern int parport_device_proc_register(struct pardevice *device);
+extern int parport_device_proc_unregister(struct pardevice *device);
+extern int parport_default_proc_register(void);
+extern int parport_default_proc_unregister(void);
 
 extern void dec_parport_count(void);
 extern void inc_parport_count(void);
diff -u --recursive --new-file v2.3.5/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.3.5/linux/include/linux/pci.h	Fri May 14 18:55:29 1999
+++ linux/include/linux/pci.h	Wed Jun  9 16:59:16 1999
@@ -273,6 +273,8 @@
 #define PCI_CLASS_SERIAL_USB		0x0c03
 #define PCI_CLASS_SERIAL_FIBER		0x0c04
 
+#define PCI_CLASS_HOT_SWAP_CONTROLLER	0xff00
+
 #define PCI_CLASS_OTHERS		0xff
 
 /*
@@ -365,6 +367,7 @@
 #define PCI_DEVICE_ID_DEC_21150		0x0022
 #define PCI_DEVICE_ID_DEC_21152		0x0024
 #define PCI_DEVICE_ID_DEC_21153		0x0025
+#define PCI_DEVICE_ID_DEC_21154		0x0026
 
 #define PCI_VENDOR_ID_CIRRUS		0x1013
 #define PCI_DEVICE_ID_CIRRUS_7548	0x0038
@@ -510,9 +513,12 @@
 #define PCI_DEVICE_ID_WINBOND2_89C940	0x0940
 
 #define PCI_VENDOR_ID_MOTOROLA		0x1057
+#define PCI_VENDOR_ID_MOTOROLA_OOPS	0x1507
 #define PCI_DEVICE_ID_MOTOROLA_MPC105	0x0001
 #define PCI_DEVICE_ID_MOTOROLA_MPC106	0x0002
 #define PCI_DEVICE_ID_MOTOROLA_RAVEN	0x4801
+#define PCI_DEVICE_ID_MOTOROLA_FALCON	0x4802
+#define PCI_DEVICE_ID_MOTOROLA_CPX8216	0x4806
 
 #define PCI_VENDOR_ID_PROMISE		0x105a
 #define PCI_DEVICE_ID_PROMISE_20246	0x4d33
@@ -1062,9 +1068,11 @@
 
 #define PCI_VENDOR_ID_ADAPTEC		0x9004
 #define PCI_DEVICE_ID_ADAPTEC_7810	0x1078
+#define PCI_DEVICE_ID_ADAPTEC_7821	0x2178
 #define PCI_DEVICE_ID_ADAPTEC_7850	0x5078
 #define PCI_DEVICE_ID_ADAPTEC_7855	0x5578
 #define PCI_DEVICE_ID_ADAPTEC_5800	0x5800
+#define PCI_DEVICE_ID_ADAPTEC_3860	0x6038
 #define PCI_DEVICE_ID_ADAPTEC_1480A	0x6075
 #define PCI_DEVICE_ID_ADAPTEC_7860	0x6078
 #define PCI_DEVICE_ID_ADAPTEC_7861	0x6178
@@ -1079,15 +1087,28 @@
 #define PCI_DEVICE_ID_ADAPTEC_7882	0x8278
 #define PCI_DEVICE_ID_ADAPTEC_7883	0x8378
 #define PCI_DEVICE_ID_ADAPTEC_7884	0x8478
+#define PCI_DEVICE_ID_ADAPTEC_7885	0x8578
+#define PCI_DEVICE_ID_ADAPTEC_7886	0x8678
+#define PCI_DEVICE_ID_ADAPTEC_7887	0x8778
+#define PCI_DEVICE_ID_ADAPTEC_7888	0x8878
 #define PCI_DEVICE_ID_ADAPTEC_1030	0x8b78
 
 #define PCI_VENDOR_ID_ADAPTEC2		0x9005
 #define PCI_DEVICE_ID_ADAPTEC2_2940U2	0x0010
-#define PCI_DEVICE_ID_ADAPTEC2_78902	0x0013
+#define PCI_DEVICE_ID_ADAPTEC2_2930U2	0x0011
+#define PCI_DEVICE_ID_ADAPTEC2_7890B	0x0013
 #define PCI_DEVICE_ID_ADAPTEC2_7890	0x001f
 #define PCI_DEVICE_ID_ADAPTEC2_3940U2	0x0050
 #define PCI_DEVICE_ID_ADAPTEC2_3950U2D	0x0051
 #define PCI_DEVICE_ID_ADAPTEC2_7896	0x005f
+#define PCI_DEVICE_ID_ADAPTEC2_7892A	0x0080
+#define PCI_DEVICE_ID_ADAPTEC2_7892B	0x0081
+#define PCI_DEVICE_ID_ADAPTEC2_7892D	0x0083
+#define PCI_DEVICE_ID_ADAPTEC2_7892P	0x008f
+#define PCI_DEVICE_ID_ADAPTEC2_7899A	0x00c0
+#define PCI_DEVICE_ID_ADAPTEC2_7899B	0x00c1
+#define PCI_DEVICE_ID_ADAPTEC2_7899D	0x00c3
+#define PCI_DEVICE_ID_ADAPTEC2_7899P	0x00cf
 
 #define PCI_VENDOR_ID_ATRONICS		0x907f
 #define PCI_DEVICE_ID_ATRONICS_2015	0x2015
diff -u --recursive --new-file v2.3.5/linux/include/linux/pkt_sched.h linux/include/linux/pkt_sched.h
--- v2.3.5/linux/include/linux/pkt_sched.h	Tue Apr 28 11:10:10 1998
+++ linux/include/linux/pkt_sched.h	Wed Jun  9 14:45:36 1999
@@ -38,6 +38,9 @@
 	__u32	pps;			/* Current flow packet rate */
 	__u32	qlen;
 	__u32	backlog;
+#ifdef __KERNEL__
+	spinlock_t *lock;
+#endif
 };
 
 struct tc_estimator
diff -u --recursive --new-file v2.3.5/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.3.5/linux/include/linux/proc_fs.h	Mon May 31 22:28:06 1999
+++ linux/include/linux/proc_fs.h	Tue Jun  8 23:04:14 1999
@@ -52,7 +52,8 @@
 	PROC_STRAM,
 	PROC_SOUND,
 	PROC_MTRR, /* whether enabled or not */
-	PROC_FS
+	PROC_FS,
+	PROC_SYSVIPC
 };
 
 enum pid_directory_inos {
@@ -207,6 +208,7 @@
 	PROC_SCSI_INIA100,
 	PROC_SCSI_FCAL,
 	PROC_SCSI_I2O,
+	PROC_SCSI_USB_SCSI,
 	PROC_SCSI_SCSI_DEBUG,	
 	PROC_SCSI_NOT_PRESENT,
 	PROC_SCSI_FILE,                        /* I'm assuming here that we */
@@ -243,6 +245,12 @@
 	PROC_CODA_FS_LAST
 };
 
+enum sysvipc_directory_inos {
+	PROC_SYSVIPC_SHM = PROC_CODA_FS_LAST,
+	PROC_SYSVIPC_SEM,
+	PROC_SYSVIPC_MSG
+};
+
 /* Finally, the dynamically allocatable proc entries are reserved: */
 
 #define PROC_DYNAMIC_FIRST 4096
@@ -313,6 +321,7 @@
 extern struct proc_dir_entry proc_pid_fd;
 extern struct proc_dir_entry proc_mca;
 extern struct proc_dir_entry *proc_bus;
+extern struct proc_dir_entry *proc_sysvipc;
 
 extern struct inode_operations proc_scsi_inode_operations;
 
@@ -422,6 +431,7 @@
 #endif
 extern struct inode_operations proc_omirr_inode_operations;
 extern struct inode_operations proc_ppc_htab_inode_operations;
+extern struct inode_operations proc_sysvipc_inode_operations;
 
 /*
  * generic.c
diff -u --recursive --new-file v2.3.5/linux/include/linux/rtnetlink.h linux/include/linux/rtnetlink.h
--- v2.3.5/linux/include/linux/rtnetlink.h	Mon May 31 22:28:06 1999
+++ linux/include/linux/rtnetlink.h	Wed Jun  9 14:45:36 1999
@@ -515,9 +515,6 @@
 
 #ifdef __KERNEL__
 
-extern atomic_t rtnl_rlockct;
-extern wait_queue_head_t rtnl_wait;
-
 extern __inline__ int rtattr_strcmp(struct rtattr *rta, char *str)
 {
 	int len = strlen(str) + 1;
@@ -544,127 +541,31 @@
 #define RTA_PUT(skb, attrtype, attrlen, data) \
 ({ if (skb_tailroom(skb) < (int)RTA_SPACE(attrlen)) goto rtattr_failure; \
    __rta_fill(skb, attrtype, attrlen, data); })
-
-extern unsigned long rtnl_wlockct;
-
-/* NOTE: these locks are not interrupt safe, are not SMP safe,
- * they are even not atomic. 8)8)8) ... and it is not a bug.
- * Really, if these locks will be programmed correctly,
- * all the addressing/routing machine would become SMP safe,
- * but is absolutely useless at the moment, because all the kernel
- * is not reenterable in any case. --ANK
- *
- * Well, atomic_* and set_bit provide the only thing here:
- * gcc is confused not to overoptimize them, that's all.
- * I remember as gcc splitted ++ operation, but cannot reproduce
- * it with gcc-2.7.*. --ANK
- *
- * One more note: rwlock facility should be written and put
- * to a kernel wide location: f.e. current implementation of semaphores
- * (especially, for x86) looks like a wonder. It would be good
- * to have something similar for rwlock. Recursive lock could be also
- * useful thing. --ANK
- */
-
-extern __inline__ int rtnl_shlock_nowait(void)
-{
-	atomic_inc(&rtnl_rlockct);
-	if (test_bit(0, &rtnl_wlockct)) {
-		atomic_dec(&rtnl_rlockct);
-		return -EAGAIN;
-	}
-	return 0;
-}
-
-extern __inline__ void rtnl_shlock(void)
-{
-	while (rtnl_shlock_nowait())
-		sleep_on(&rtnl_wait);
-}
-
-/* Check for possibility to PROMOTE shared lock to exclusive.
-   Shared lock must be already grabbed with rtnl_shlock*().
- */
-
-extern __inline__ int rtnl_exlock_nowait(void)
-{
-	if (atomic_read(&rtnl_rlockct) > 1)
-		return -EAGAIN;
-	if (test_and_set_bit(0, &rtnl_wlockct))
-		return -EAGAIN;
-	return 0;
-}
-
-extern __inline__ void rtnl_exlock(void)
-{
-	while (rtnl_exlock_nowait())
-		sleep_on(&rtnl_wait);
-}
-
-#if 0
-extern __inline__ void rtnl_shunlock(void)
-{
-	atomic_dec(&rtnl_rlockct);
-	if (atomic_read(&rtnl_rlockct) <= 1) {
-		wake_up(&rtnl_wait);
-		if (rtnl && rtnl->receive_queue.qlen)
-			rtnl->data_ready(rtnl, 0);
-	}
-}
-#else
-
-/* The problem: inline requires to include <net/sock.h> and, hence,
-   almost all of net includes :-(
- */
-
-#define rtnl_shunlock() ({ \
-	atomic_dec(&rtnl_rlockct); \
-	if (atomic_read(&rtnl_rlockct) <= 1) { \
-		wake_up(&rtnl_wait); \
-		if (rtnl && rtnl->receive_queue.qlen) \
-			rtnl->data_ready(rtnl, 0); \
-	} \
-})
 #endif
 
-/* Release exclusive lock. Note, that we do not wake up rtnetlink socket,
- * it will be done later after releasing shared lock.
- */
-
-extern __inline__ void rtnl_exunlock(void)
-{
-	clear_bit(0, &rtnl_wlockct);
-	wake_up(&rtnl_wait);
-}
-
-#else
+extern struct semaphore rtnl_sem;
 
-extern __inline__ void rtnl_shlock(void)
-{
-	while (atomic_read(&rtnl_rlockct))
-		sleep_on(&rtnl_wait);
-	atomic_inc(&rtnl_rlockct);
-}
+#define rtnl_exlock()		do { } while(0)
+#define rtnl_exunlock()		do { } while(0)
+#define rtnl_exlock_nowait()	(0)
 
-extern __inline__ void rtnl_shunlock(void)
-{
-	if (atomic_dec_and_test(&rtnl_rlockct))
-		wake_up(&rtnl_wait);
-}
-
-extern __inline__ void rtnl_exlock(void)
-{
-}
-
-extern __inline__ void rtnl_exunlock(void)
-{
-}
+#define rtnl_shlock()		down(&rtnl_sem)
+#define rtnl_shlock_nowait()	down_trylock(&rtnl_sem)
 
+#ifndef CONFIG_RTNETLINK
+#define rtnl_shunlock()	up(&rtnl_sem)
+#else
+#define rtnl_shunlock()	do { up(&rtnl_sem); \
+		             if (rtnl && rtnl->receive_queue.qlen) \
+				     rtnl->data_ready(rtnl, 0); \
+		        } while(0)
 #endif
 
 extern void rtnl_lock(void);
 extern void rtnl_unlock(void);
 extern void rtnetlink_init(void);
+
+
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.3.5/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.3.5/linux/include/linux/sched.h	Mon May 31 22:28:06 1999
+++ linux/include/linux/sched.h	Tue Jun  8 23:03:44 1999
@@ -163,9 +163,9 @@
 #define AVL_MIN_MAP_COUNT	32
 
 struct mm_struct {
-	struct vm_area_struct *mmap;		/* list of VMAs */
-	struct vm_area_struct *mmap_avl;	/* tree of VMAs */
-	struct vm_area_struct *mmap_cache;	/* last find_vma result */
+	struct vm_area_struct * mmap;		/* list of VMAs */
+	struct vm_area_struct * mmap_avl;	/* tree of VMAs */
+	struct vm_area_struct * mmap_cache;	/* last find_vma result */
 	pgd_t * pgd;
 	atomic_t count;
 	int map_count;				/* number of VMAs */
@@ -453,8 +453,8 @@
 }
 
 /* per-UID process charging. */
-extern int alloc_uid(struct task_struct *p);
-void free_uid(struct task_struct *p);
+extern int alloc_uid(struct task_struct *);
+void free_uid(struct task_struct *);
 
 #include <asm/current.h>
 
@@ -482,26 +482,25 @@
 #define wake_up(x)			__wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE)
 #define wake_up_interruptible(x)	__wake_up((x),TASK_INTERRUPTIBLE)
 
-extern int in_group_p(gid_t grp);
+extern int in_group_p(gid_t);
 
 extern void flush_signals(struct task_struct *);
 extern void flush_signal_handlers(struct task_struct *);
-extern int dequeue_signal(sigset_t *block, siginfo_t *);
-extern int send_sig_info(int, struct siginfo *info, struct task_struct *);
-extern int force_sig_info(int, struct siginfo *info, struct task_struct *);
-extern int kill_pg_info(int, struct siginfo *info, pid_t);
-extern int kill_sl_info(int, struct siginfo *info, pid_t);
-extern int kill_proc_info(int, struct siginfo *info, pid_t);
-extern int kill_something_info(int, struct siginfo *info, int);
-extern void notify_parent(struct task_struct * tsk, int);
-extern void force_sig(int sig, struct task_struct * p);
-extern int send_sig(int sig, struct task_struct * p, int priv);
+extern int dequeue_signal(sigset_t *, siginfo_t *);
+extern int send_sig_info(int, struct siginfo *, struct task_struct *);
+extern int force_sig_info(int, struct siginfo *, struct task_struct *);
+extern int kill_pg_info(int, struct siginfo *, pid_t);
+extern int kill_sl_info(int, struct siginfo *, pid_t);
+extern int kill_proc_info(int, struct siginfo *, pid_t);
+extern int kill_something_info(int, struct siginfo *, int);
+extern void notify_parent(struct task_struct *, int);
+extern void force_sig(int, struct task_struct *);
+extern int send_sig(int, struct task_struct *, int);
 extern int kill_pg(pid_t, int, int);
 extern int kill_sl(pid_t, int, int);
 extern int kill_proc(pid_t, int, int);
-extern int do_sigaction(int sig, const struct k_sigaction *act,
-			struct k_sigaction *oact);
-extern int do_sigaltstack(const stack_t *ss, stack_t *oss, unsigned long sp);
+extern int do_sigaction(int, const struct k_sigaction *, struct k_sigaction *);
+extern int do_sigaltstack(const stack_t *, stack_t *, unsigned long);
 
 extern inline int signal_pending(struct task_struct *p)
 {
@@ -552,12 +551,10 @@
 		: on_sig_stack(sp) ? SS_ONSTACK : 0);
 }
 
-extern int request_irq(unsigned int irq,
+extern int request_irq(unsigned int,
 		       void (*handler)(int, void *, struct pt_regs *),
-		       unsigned long flags, 
-		       const char *device,
-		       void *dev_id);
-extern void free_irq(unsigned int irq, void *dev_id);
+		       unsigned long, const char *, void *);
+extern void free_irq(unsigned int, void *);
 
 /*
  * This has now become a routine instead of a macro, it sets a flag if
diff -u --recursive --new-file v2.3.5/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.3.5/linux/include/linux/sysctl.h	Mon May 31 22:28:06 1999
+++ linux/include/linux/sysctl.h	Thu Jun  3 16:21:47 1999
@@ -452,12 +452,42 @@
 /* CTL_DEV names: */
 enum {
 	DEV_CDROM=1,
-	DEV_HWMON=2
+	DEV_HWMON=2,
+	DEV_PARPORT=3
 };
 
 /* /proc/sys/dev/cdrom */
 enum {
 	DEV_CDROM_INFO=1
+};
+
+/* /proc/sys/dev/parport */
+enum {
+	DEV_PARPORT_DEFAULT=-3
+};
+
+/* /proc/sys/dev/parport/default */
+enum {
+	DEV_PARPORT_DEFAULT_TIMESLICE=1,
+	DEV_PARPORT_DEFAULT_SPINTIME=2
+};
+
+/* /proc/sys/dev/parport/parport n */
+enum {
+	DEV_PARPORT_SPINTIME=1,
+	DEV_PARPORT_HARDWARE=2,
+	DEV_PARPORT_DEVICES=3,
+	DEV_PARPORT_AUTOPROBE=16
+};
+
+/* /proc/sys/dev/parport/parport n/devices/ */
+enum {
+	DEV_PARPORT_DEVICES_ACTIVE=-3,
+};
+
+/* /proc/sys/dev/parport/parport n/devices/device n */
+enum {
+	DEV_PARPORT_DEVICE_TIMESLICE=1,
 };
 
 #ifdef __KERNEL__
diff -u --recursive --new-file v2.3.5/linux/include/linux/wanrouter.h linux/include/linux/wanrouter.h
--- v2.3.5/linux/include/linux/wanrouter.h	Tue May 11 13:06:00 1999
+++ linux/include/linux/wanrouter.h	Tue Jun  8 23:05:38 1999
@@ -5,6 +5,7 @@
 *
 * Author:	Gene Kozin	<genek@compuserve.com>
 *		Jaspreet Singh	<jaspreet@sangoma.com>
+* Additions:	Arnaldo Carvalho de Melo <acme@conectiva.com.br>
 *
 * Copyright:	(c) 1995-1997 Sangoma Technologies Inc.
 *
@@ -13,11 +14,14 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
+* May 23, 1999	Arnaldo Melo	Added local_addr to wanif_conf_t
+*				WAN_DISCONNECTING state added
 * Nov 06, 1997	Jaspreet Singh	Changed Router Driver version to 1.1 from 1.0
 * Oct 20, 1997	Jaspreet Singh	Added 'cir','bc','be' and 'mc' to 'wanif_conf_t'
 *				Added 'enable_IPX' and 'network_number' to 
 *				'wan_device_t'.  Also added defines for
-*				UDP PACKET TYPE, Interrupt test, critical values*				for RACE conditions.
+*				UDP PACKET TYPE, Interrupt test, critical values
+*				for RACE conditions.
 * Oct 05, 1997	Jaspreet Singh	Added 'dlci_num' and 'dlci[100]' to 
 *				'wan_fr_conf_t' to configure a list of dlci(s)
 *				for a NODE 
@@ -125,7 +129,7 @@
 	unsigned n392;		/* error threshold counter */
 	unsigned n393;		/* monitored events counter */
 	unsigned dlci_num;	/* number of DLCs (access node) */
-	unsigned  dlci[100];    /* List of all DLCIs */
+	unsigned dlci[100];	/* List of all DLCIs */
 } wan_fr_conf_t;
 
 /*----------------------------------------------------------------------------
@@ -274,6 +278,7 @@
 	WAN_DISCONNECTED,	/* link/channel is disconnected */
 	WAN_CONNECTING,		/* connection is in progress */
 	WAN_CONNECTED,		/* link/channel is operational */
+	WAN_DISCONNECTING,	/* disconnection is in progress */
 	WAN_LIMIT		/* for verification only */
 };
 
@@ -298,6 +303,8 @@
 	unsigned bc;			/* Committed Burst Size fwd, bwd */
 	unsigned be;			/* Excess Burst Size fwd, bwd */ 
 	char mc;			/* Multicast on or off */
+	char local_addr[WAN_ADDRESS_SZ+1];/* local media address, ASCIIZ */
+	unsigned char port;		/* board port */
 	int reserved[8];		/* reserved for future extensions */
 } wanif_conf_t;
 
diff -u --recursive --new-file v2.3.5/linux/include/net/addrconf.h linux/include/net/addrconf.h
--- v2.3.5/linux/include/net/addrconf.h	Sun Mar 21 07:22:00 1999
+++ linux/include/net/addrconf.h	Wed Jun  9 14:45:36 1999
@@ -56,7 +56,7 @@
 extern int			ipv6_get_saddr(struct dst_entry *dst, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern struct inet6_ifaddr *	ipv6_get_lladdr(struct device *dev);
+extern int			ipv6_get_lladdr(struct device *dev, struct in6_addr *);
 
 /*
  *	multicast prototypes (mcast.c)
@@ -68,6 +68,7 @@
 						  int ifindex, 
 						  struct in6_addr *addr);
 extern void			ipv6_sock_mc_close(struct sock *sk);
+extern int			inet6_mc_check(struct sock *sk, struct in6_addr *addr);
 
 extern int			ipv6_dev_mc_inc(struct device *dev,
 						struct in6_addr *addr);
diff -u --recursive --new-file v2.3.5/linux/include/net/dst.h linux/include/net/dst.h
--- v2.3.5/linux/include/net/dst.h	Mon May 31 22:28:06 1999
+++ linux/include/net/dst.h	Wed Jun  9 14:45:36 1999
@@ -170,6 +170,9 @@
 	if (dst->expires == 0 || (long)(dst->expires - expires) > 0)
 		dst->expires = expires;
 }
+
+extern void		dst_init(void);
+
 #endif
 
 #endif /* _NET_DST_H */
diff -u --recursive --new-file v2.3.5/linux/include/net/if_inet6.h linux/include/net/if_inet6.h
--- v2.3.5/linux/include/net/if_inet6.h	Sun Mar  1 14:40:40 1998
+++ linux/include/net/if_inet6.h	Wed Jun  9 14:45:36 1999
@@ -44,6 +44,7 @@
 	__u32			valid_lft;
 	__u32			prefered_lft;
 	unsigned long		tstamp;
+	atomic_t		refcnt;
 
 	__u8			probes;
 	__u8			flags;
@@ -108,6 +109,7 @@
 
 	struct inet6_ifaddr	*addr_list;
 	struct ifmcaddr6	*mc_list;
+	rwlock_t		lock;
 	__u32			if_flags;
 
 	struct neigh_parms	*nd_parms;
diff -u --recursive --new-file v2.3.5/linux/include/net/ip.h linux/include/net/ip.h
--- v2.3.5/linux/include/net/ip.h	Mon May 31 22:28:06 1999
+++ linux/include/net/ip.h	Wed Jun  9 14:45:36 1999
@@ -146,10 +146,10 @@
 	skb->protocol = __constant_htons(ETH_P_IP);
 
 	if (hh) {
-		read_lock_irq(&hh->hh_lock);
+		read_lock_bh(&hh->hh_lock);
 		memcpy(skb->data - 16, hh->hh_data, 16);
-		read_unlock_irq(&hh->hh_lock);
-	        skb_push(skb, dev->hard_header_len);
+		read_unlock_bh(&hh->hh_lock);
+	        skb_push(skb, hh->hh_len);
 		return hh->hh_output(skb);
 	} else if (dst->neighbour)
 		return dst->neighbour->output(skb);
diff -u --recursive --new-file v2.3.5/linux/include/net/irda/dongle.h linux/include/net/irda/dongle.h
--- v2.3.5/linux/include/net/irda/dongle.h	Mon May 31 22:28:07 1999
+++ linux/include/net/irda/dongle.h	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Wed Oct 21 22:47:12 1998
- * Modified at:   Mon May 10 14:51:06 1999
+ * Modified at:   Sun May 16 13:40:03 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
@@ -35,15 +35,15 @@
 	ACTISYS_PLUS_DONGLE,
 	GIRBIL_DONGLE,
 	LITELINK_DONGLE,
-} DONGLE_T;
+} IRDA_DONGLE;
 
 struct irda_device;
 
 struct dongle {
-	DONGLE_T type;
+	IRDA_DONGLE type;
 	void (*open)(struct irda_device *, int type);
 	void (*close)(struct irda_device *);
-	void (*reset)( struct irda_device *, int unused);
+	void (*reset)( struct irda_device *);
 	void (*change_speed)( struct irda_device *, int baudrate);
 	void (*qos_init)( struct irda_device *, struct qos_info *);
 };
diff -u --recursive --new-file v2.3.5/linux/include/net/irda/ircomm_common.h linux/include/net/irda/ircomm_common.h
--- v2.3.5/linux/include/net/irda/ircomm_common.h	Mon May 31 22:28:07 1999
+++ linux/include/net/irda/ircomm_common.h	Mon Jun  7 16:18:58 1999
@@ -77,9 +77,9 @@
 
 #define IRCOMM_MAGIC            0x434f4d4d
 #define COMM_INIT_CTRL_PARAM    3          /* length of initial control parameters */
-#define COMM_HEADER             1          /* length of clen field */
-#define COMM_HEADER_SIZE        (TTP_MAX_HEADER+COMM_HEADER)
-#define COMM_DEFAULT_DATA_SIZE  64
+#define COMM_HEADER_SIZE        1          /* length of clen field */
+#define COMM_MAX_HEADER_SIZE    (TTP_MAX_HEADER+COMM_HEADER_SIZE)
+#define COMM_DEFAULT_SDU_SIZE   (64 - COMM_HEADER_SIZE)
 #define IRCOMM_MAX_CONNECTION   1          /* Don't change for now */
 
 
@@ -177,8 +177,8 @@
 	int null_modem_mode;     /* switch for null modem emulation */
 	int ttp_stop;
 
-	int max_txbuff_size;          
-	__u32 max_sdu_size;
+	__u32 tx_max_sdu_size;          
+	__u32 rx_max_sdu_size;
 	__u8 max_header_size;
 
  	__u32 daddr;        /* Device address of the peer device */ 
diff -u --recursive --new-file v2.3.5/linux/include/net/irda/irda_device.h linux/include/net/irda/irda_device.h
--- v2.3.5/linux/include/net/irda/irda_device.h	Mon May 31 22:28:07 1999
+++ linux/include/net/irda/irda_device.h	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Apr 14 12:41:42 1998
- * Modified at:   Mon May 10 15:46:02 1999
+ * Modified at:   Wed May 19 08:44:48 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
@@ -124,14 +124,14 @@
 
 	struct dongle *dongle; /* Dongle driver */
 
-	/* spinlock_t lock; */ /* For serializing operations */
+	spinlock_t lock;       /* For serializing operations */
 	
 	/* Media busy stuff */
 	int media_busy;
 	struct timer_list media_busy_timer;
 
 	/* Callbacks for driver specific implementation */
-        void (*change_speed)(struct irda_device *driver, int baud);
+        void (*change_speed)(struct irda_device *idev, int baud);
  	int  (*is_receiving)(struct irda_device *);    /* receiving? */
 	void (*set_dtr_rts)(struct irda_device *idev, int dtr, int rts);
 	int  (*raw_write)(struct irda_device *idev, __u8 *buf, int len);
diff -u --recursive --new-file v2.3.5/linux/include/net/irda/irlan_common.h linux/include/net/irda/irlan_common.h
--- v2.3.5/linux/include/net/irda/irlan_common.h	Mon May 31 22:28:07 1999
+++ linux/include/net/irda/irlan_common.h	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Sun May  9 11:45:33 1999
+ * Modified at:   Mon May 31 13:54:20 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
@@ -124,6 +124,9 @@
 	int unicast_open;
 	int broadcast_open;
 
+	int tx_busy;
+	struct sk_buff_head txq; /* Transmit control queue */
+
 	struct timer_list kick_timer;
 };
 
@@ -163,7 +166,7 @@
 	struct device dev;        /* Ethernet device structure*/
 	struct enet_statistics stats;
 
-	__u32 saddr;              /* Source devcie address */
+	__u32 saddr;              /* Source device address */
 	__u32 daddr;              /* Destination device address */
 	int   netdev_registered;
 	int   notify_irmanager;
@@ -200,6 +203,8 @@
 void irlan_start_watchdog_timer(struct irlan_cb *self, int timeout);
 
 void irlan_open_data_tsap(struct irlan_cb *self);
+
+int irlan_run_ctrl_tx_queue(struct irlan_cb *self);
 
 void irlan_get_provider_info(struct irlan_cb *self);
 void irlan_get_unicast_addr(struct irlan_cb *self);
diff -u --recursive --new-file v2.3.5/linux/include/net/irda/irlan_eth.h linux/include/net/irda/irlan_eth.h
--- v2.3.5/linux/include/net/irda/irlan_eth.h	Sat Apr 24 17:50:05 1999
+++ linux/include/net/irda/irlan_eth.h	Mon Jun  7 16:18:58 1999
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Thu Oct 15 08:36:58 1998
- * Modified at:   Thu Apr 22 14:09:37 1999
+ * Modified at:   Fri May 14 23:29:00 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -32,6 +32,7 @@
 int  irlan_eth_xmit(struct sk_buff *skb, struct device *dev);
 
 void irlan_eth_flow_indication( void *instance, void *sap, LOCAL_FLOW flow);
+void irlan_eth_send_gratuitous_arp(struct device *dev);
 
 void irlan_eth_set_multicast_list( struct device *dev);
 struct enet_statistics *irlan_eth_get_stats(struct device *dev);
diff -u --recursive --new-file v2.3.5/linux/include/net/irda/irport.h linux/include/net/irda/irport.h
--- v2.3.5/linux/include/net/irda/irport.h	Mon May 31 22:28:07 1999
+++ linux/include/net/irda/irport.h	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug  3 13:49:59 1997
- * Modified at:   Mon May 10 22:12:56 1999
+ * Modified at:   Wed May 19 15:31:16 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1997, 1998-1999 Dag Brattli <dagb@cs.uit.no>
@@ -49,13 +49,14 @@
 
 #define FRAME_MAX_SIZE 2048
 
-void irport_start(int iobase);
-void irport_stop(int iobase);
+void irport_start(struct irda_device *idev, int iobase);
+void irport_stop(struct irda_device *idev, int iobase);
 int  irport_probe(int iobase);
 
 void irport_change_speed(struct irda_device *idev, int speed);
 void irport_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 
 int  irport_hard_xmit(struct sk_buff *skb, struct device *dev);
+void irport_wait_until_sent(struct irda_device *idev);
 
 #endif
diff -u --recursive --new-file v2.3.5/linux/include/net/irda/irqueue.h linux/include/net/irda/irqueue.h
--- v2.3.5/linux/include/net/irda/irqueue.h	Tue May 11 13:04:00 1999
+++ linux/include/net/irda/irqueue.h	Tue Jun  8 23:03:42 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Jun  9 13:26:50 1998
- * Modified at:   Thu Feb 25 20:34:21 1999
+ * Modified at:   Tue May 25 07:54:41 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (C) 1998, Aage Kvalnes <aage@cs.uit.no>
@@ -29,8 +29,6 @@
 
 #include <linux/types.h>
 #include <asm/spinlock.h>
-
-/* #include <net/irda/irda.h> */
 
 #ifndef QUEUE_H
 #define QUEUE_H
diff -u --recursive --new-file v2.3.5/linux/include/net/irda/irvtd.h linux/include/net/irda/irvtd.h
--- v2.3.5/linux/include/net/irda/irvtd.h	Fri May 14 18:55:30 1999
+++ linux/include/net/irda/irvtd.h	Mon Jun  7 16:18:58 1999
@@ -53,6 +53,8 @@
 	struct sk_buff_head rxbuff; 
 	struct ircomm_cb *comm;     /* ircomm instance */
 
+	__u32 tx_max_sdu_size;
+	__u32 max_header_size;
 	/* 
 	 * These members are used for compatibility with usual serial device.
 	 * See linux/serial.h
@@ -69,7 +71,8 @@
 	wait_queue_head_t       delta_msr_wait;
 	wait_queue_head_t       tx_wait;
 
-	struct timer_list       timer;
+	struct timer_list       tx_timer;
+	struct timer_list       rx_timer;
 
 	long pgrp;
 	long session;  
diff -u --recursive --new-file v2.3.5/linux/include/net/irda/smc-ircc.h linux/include/net/irda/smc-ircc.h
--- v2.3.5/linux/include/net/irda/smc-ircc.h	Wed Dec 31 16:00:00 1969
+++ linux/include/net/irda/smc-ircc.h	Mon Jun  7 16:18:58 1999
@@ -0,0 +1,160 @@
+/*********************************************************************
+ *                
+ * Filename:      smc.h
+ * Version:       
+ * Description:   
+ * Status:        Experimental.
+ * Author:        Thomas Davis (tadavis@jps.net)
+ *
+ *     Copyright (c) 1998, 1999 Thomas Davis (tadavis@jps.net>
+ *     All Rights Reserved
+ *      
+ *     This program is free software; you can redistribute it and/or 
+ *     modify it under the terms of the GNU General Public License as 
+ *     published by the Free Software Foundation; either version 2 of 
+ *     the License, or (at your option) any later version.
+ *  
+ *     I, Thomas Davis, admit no liability nor provide warranty for any
+ *     of this software. This material is provided "AS-IS" and at no charge.
+ *     
+ * Definitions for the SMC IrCC controller.
+ *
+ ********************************************************************/
+
+#ifndef SMC_IRCC_H
+#define SMC_IRCC_H
+
+#define UART_MASTER			0x07
+#define UART_MASTER_POWERDOWN	1<<7
+#define UART_MASTER_RESET		1<<6
+#define UART_MASTER_INT_EN		1<<5
+#define UART_MASTER_ERROR_RESET	1<<4
+
+/* Register block 0 */
+
+#define UART_IIR	0x01
+#define UART_IER	0x02
+#define UART_LSR	0x03
+#define UART_LCR_A	0x04
+#define UART_LCR_B	0x05
+#define UART_BSR	0x06
+
+#define UART_IIR_ACTIVE_FRAME	1<<7
+#define UART_IIR_EOM 		1<<6
+#define UART_IIR_RAW_MODE		1<<5
+#define UART_IIR_FIFO		1<<4
+
+#define UART_IER_ACTIVE_FRAME	1<<7
+#define UART_IER_EOM 		1<<6
+#define UART_IER_RAW_MODE		1<<5
+#define UART_IER_FIFO		1<<4
+
+#define UART_LSR_UNDERRUN		1<<7
+#define UART_LSR_OVERRUN		1<<6
+#define UART_LSR_FRAME_ERROR 	1<<5
+#define UART_LSR_SIZE_ERROR		1<<4
+#define UART_LSR_CRC_ERROR		1<<3
+#define UART_LSR_FRAME_ABORT 	1<<2
+
+#define UART_LCR_A_FIFO_RESET        1<<7
+#define UART_LCR_A_FAST              1<<6
+#define UART_LCR_A_GP_DATA           1<<5
+#define UART_LCR_A_RAW_TX            1<<4
+#define UART_LCR_A_RAW_RX            1<<3
+#define UART_LCR_A_ABORT             1<<2
+#define UART_LCR_A_DATA_DONE         1<<1
+
+#define UART_LCR_B_SCE_DISABLED 	0x00<<6
+#define UART_LCR_B_SCE_TRANSMIT 	0x01<<6
+#define UART_LCR_B_SCE_RECEIVE		0x02<<6
+#define UART_LCR_B_SCE_UNDEFINED	0x03<<6
+#define UART_LCR_B_SIP_ENABLE		1<<5
+#define UART_LCR_B_BRICK_WALL		1<<4
+
+#define UART_BSR_NOT_EMPTY	1<<7
+#define UART_BSR_FIFO_FULL	1<<6
+#define UART_BSR_TIMEOUT	1<<5
+
+/* Register block 1 */
+
+#define UART_SCE_CFGA	0x00
+#define UART_SCE_CFGB	0x01
+#define UART_FIFO_THRESHOLD	0x02
+
+#define UART_CFGA_AUX_IR		0x01<<7
+#define UART_CFGA_HALF_DUPLEX	0x01<<2
+#define UART_CFGA_TX_POLARITY	0x01<<1
+#define UART_CFGA_RX_POLARITY	0x01
+
+#define UART_CFGA_COM		0x00<<3
+#define UART_CFGA_IRDA_SIR_A	0x01<<3
+#define UART_CFGA_ASK_SIR		0x02<<3
+#define UART_CFGA_IRDA_SIR_B	0x03<<3
+#define UART_CFGA_IRDA_HDLC 	0x04<<3
+#define UART_CFGA_IRDA_4PPM 	0x05<<3
+#define UART_CFGA_CONSUMER		0x06<<3
+#define UART_CFGA_RAW_IR		0x07<<3
+#define UART_CFGA_OTHER		0x08<<3
+
+#define UART_IR_HDLC			0x04
+#define UART_IR_4PPM			0x01
+#define UART_IR_CONSUMER		0x02
+
+#define UART_CFGB_LOOPBACK		0x01<<5
+#define UART_CFGB_LPBCK_TX_CRC	0x01<<4
+#define UART_CFGB_NOWAIT		0x01<<3
+#define UART_CFGB_STRING_MOVE	0x01<<2
+#define UART_CFGB_DMA_BURST 	0x01<<1
+#define UART_CFGB_DMA_ENABLE	0x01
+
+#define UART_CFGB_COM		0x00<<6
+#define UART_CFGB_IR		0x01<<6
+#define UART_CFGB_AUX		0x02<<6
+#define UART_CFGB_INACTIVE		0x03<<6
+
+/* Register block 2 - Consumer IR - not used */
+
+/* Register block 3 - Identification Registers! */
+
+#define UART_ID_HIGH	0x00   /* 0x10 */
+#define UART_ID_LOW	0x01   /* 0xB8 */
+#define UART_CHIP_ID 	0x02   /* 0xF1 */
+#define UART_VERSION	0x03   /* 0x01 */
+#define UART_INTERFACE	0x04   /* low 4 = DMA, high 4 = IRQ */
+
+/* Register block 4 - IrDA */
+#define UART_CONTROL        0x00
+#define UART_BOF_COUNT_LO      0x01
+#define UART_BRICKWALL_CNT_LO 0x02
+#define UART_BRICKWALL_TX_CNT_HI   0x03
+#define UART_TX_SIZE_LO   0x04
+#define UART_RX_SIZE_HI   0x05
+#define UART_RX_SIZE_LO   0x06
+
+#define UART_1152     0x01<<7
+#define UART_CRC      0x01<<6
+
+/* For storing entries in the status FIFO */
+struct st_fifo_entry {
+	int status;
+	int len;
+};
+
+struct st_fifo {
+	struct st_fifo_entry entries[10];
+	int head;
+	int tail;
+	int len;
+};
+
+/* Private data for each instance */
+struct ircc_cb {
+	struct st_fifo st_fifo;
+
+	int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */
+	int tx_len;          /* Number of frames in tx_buff */
+
+	struct irda_device idev;
+};
+
+#endif
diff -u --recursive --new-file v2.3.5/linux/include/net/irda/smc_ircc.h linux/include/net/irda/smc_ircc.h
--- v2.3.5/linux/include/net/irda/smc_ircc.h	Thu Dec 17 09:01:03 1998
+++ linux/include/net/irda/smc_ircc.h	Wed Dec 31 16:00:00 1969
@@ -1,123 +0,0 @@
-#if 0
-static char *rcsid = "$Id: smc_ircc.h,v 1.5 1998/07/27 01:25:29 ratbert Exp $";
-#endif
-
-#ifndef SMC_IRCC_H
-#define SMC_IRCC_H
-
-#define FIR_XMIT	1
-#define FIR_RECEIVE	2
-#define SIR_XMIT	3
-#define SIR_RECEIVE	4
-
-#define MASTER			0x07
-#define MASTER_POWERDOWN	1<<7
-#define MASTER_RESET		1<<6
-#define MASTER_INT_EN		1<<5
-#define MASTER_ERROR_RESET	1<<4
-
-/* Register block 0 */
-
-#define IIR	0x01
-#define IER	0x02
-#define LSR	0x03
-#define LCR_A	0x04
-#define LCR_B	0x05
-#define BSR	0x06
-
-#define IIR_ACTIVE_FRAME	1<<7
-#define IIR_EOM 		1<<6
-#define IIR_RAW_MODE		1<<5
-#define IIR_FIFO		1<<4
-
-#define IER_ACTIVE_FRAME	1<<7
-#define IER_EOM 		1<<6
-#define IER_RAW_MODE		1<<5
-#define IER_FIFO		1<<4
-
-#define LSR_UNDER_RUN		1<<7
-#define LSR_OVER_RUN		1<<6
-#define LSR_FRAME_ERROR 	1<<5
-#define LSR_SIZE_ERROR		1<<4
-#define LSR_CRC_ERROR		1<<3
-#define LSR_FRAME_ABORT 	1<<2
-
-#define LCR_A_FIFO_RESET        1<<7
-#define LCR_A_FAST              1<<6
-#define LCR_A_GP_DATA           1<<5
-#define LCR_A_RAW_TX            1<<4
-#define LCR_A_RAW_RX            1<<3
-#define LCR_A_ABORT             1<<2
-#define LCR_A_DATA_DONE         1<<1
-
-#define LCR_B_SCE_MODE_DISABLED 	0x00<<6
-#define LCR_B_SCE_MODE_TRANSMIT 	0x01<<6
-#define LCR_B_SCE_MODE_RECEIVE		0x02<<6
-#define LCR_B_SCE_MODE_UNDEFINED	0x03<<6
-#define LCR_B_SIP_ENABLE		1<<5
-#define LCR_B_BRICK_WALL		1<<4
-
-#define BSR_NOT_EMPTY	1<<7
-#define BSR_FIFO_FULL	1<<6
-#define BSR_TIMEOUT	1<<5
-
-/* Register block 1 */
-
-#define SCE_CFG_A	0x00
-#define SCE_CFG_B	0x01
-#define FIFO_THRESHOLD	0x02
-
-#define CFG_A_AUX_IR		0x01<<7
-#define CFG_A_HALF_DUPLEX	0x01<<2
-#define CFG_A_TX_POLARITY	0x01<<1
-#define CFG_A_RX_POLARITY	0x01
-
-#define CFG_A_COM		0x00<<3
-#define CFG_A_IRDA_SIR_A	0x01<<3
-#define CFG_A_ASK_SIR		0x02<<3
-#define CFG_A_IRDA_SIR_B	0x03<<3
-#define CFG_A_IRDA_HDLC 	0x04<<3
-#define CFG_A_IRDA_4PPM 	0x05<<3
-#define CFG_A_CONSUMER		0x06<<3
-#define CFG_A_RAW_IR		0x07<<3
-#define CFG_A_OTHER		0x08<<3
-
-#define IR_HDLC			0x04
-#define IR_4PPM			0x01
-#define IR_CONSUMER		0x02
-
-#define CFG_B_LOOPBACK		0x01<<5
-#define CFG_B_LPBCK_TX_CRC	0x01<<4
-#define CFG_B_NOWAIT		0x01<<3
-#define CFB_B_STRING_MOVE	0x01<<2
-#define CFG_B_DMA_BURST 	0x01<<1
-#define CFG_B_DMA_ENABLE	0x01
-
-#define CFG_B_MUX_COM		0x00<<6
-#define CFG_B_MUX_IR		0x01<<6
-#define CFG_B_MUX_AUX		0x02<<6
-#define CFG_B_INACTIVE		0x03<<6
-
-/* Register block 2 - Consumer IR - not used */
-
-/* Register block 3 - Identification Registers! */
-
-#define SMSC_ID_HIGH	0x00   /* 0x10 */
-#define SMSC_ID_LOW	0x01   /* 0xB8 */
-#define CHIP_ID 	0x02   /* 0xF1 */
-#define VERSION_NUMBER	0x03   /* 0x01 */
-#define HOST_INTERFACE	0x04   /* low 4 = DMA, high 4 = IRQ */
-
-/* Register block 4 - IrDA */
-#define IR_CONTROL        0x00
-#define BOF_COUNT_LO      0x01
-#define BRICK_WALL_CNT_LO 0x02
-#define BRICK_TX_CNT_HI   0x03
-#define TX_DATA_SIZE_LO   0x04
-#define RX_DATA_SIZE_HI   0x05
-#define RX_DATA_SIZE_LO   0x06
-
-#define SELECT_1152     0x01<<7
-#define CRC_SELECT      0x01<<6
-
-#endif
diff -u --recursive --new-file v2.3.5/linux/include/net/neighbour.h linux/include/net/neighbour.h
--- v2.3.5/linux/include/net/neighbour.h	Tue May 11 13:05:00 1999
+++ linux/include/net/neighbour.h	Wed Jun  9 14:45:36 1999
@@ -96,7 +96,8 @@
 	__u8			flags;
 	__u8			nud_state;
 	__u8			type;
-	__u8			probes;
+	atomic_t		probes;
+	rwlock_t		lock;
 	unsigned char		ha[MAX_ADDR_LEN];
 	struct hh_cache		*hh;
 	atomic_t		refcnt;
@@ -155,7 +156,7 @@
 	struct timer_list 	proxy_timer;
 	struct sk_buff_head	proxy_queue;
 	int			entries;
-	atomic_t		lock;
+	rwlock_t		lock;
 	unsigned long		last_rand;
 	struct neigh_parms	*parms_list;
 	struct neigh_statistics	stats;
@@ -165,9 +166,12 @@
 
 extern void			neigh_table_init(struct neigh_table *tbl);
 extern int			neigh_table_clear(struct neigh_table *tbl);
-extern struct neighbour		*__neigh_lookup(struct neigh_table *tbl,
-					       const void *pkey, struct device *dev,
-					       int creat);
+extern struct neighbour *	neigh_lookup(struct neigh_table *tbl,
+					     const void *pkey,
+					     struct device *dev);
+extern struct neighbour *	neigh_create(struct neigh_table *tbl,
+					     const void *pkey,
+					     struct device *dev);
 extern void			neigh_destroy(struct neighbour *neigh);
 extern int			__neigh_event_send(struct neighbour *neigh, struct sk_buff *skb);
 extern int			neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int arp);
@@ -226,16 +230,6 @@
 		neigh->confirmed = jiffies;
 }
 
-extern __inline__ struct neighbour *
-neigh_lookup(struct neigh_table *tbl, const void *pkey, struct device *dev)
-{
-	struct neighbour *neigh;
-	start_bh_atomic();
-	neigh = __neigh_lookup(tbl, pkey, dev, 0);
-	end_bh_atomic();
-	return neigh;
-}
-
 extern __inline__ int neigh_is_connected(struct neighbour *neigh)
 {
 	return neigh->nud_state&NUD_CONNECTED;
@@ -254,17 +248,16 @@
 	return 0;
 }
 
-extern __inline__ void neigh_table_lock(struct neigh_table *tbl)
+extern __inline__ struct neighbour *
+__neigh_lookup(struct neigh_table *tbl, const void *pkey, struct device *dev, int creat)
 {
-	atomic_inc(&tbl->lock);
-	synchronize_bh();
-}
+	struct neighbour *n = neigh_lookup(tbl, pkey, dev);
 
-extern __inline__ void neigh_table_unlock(struct neigh_table *tbl)
-{
-	atomic_dec(&tbl->lock);
-}
+	if (n || !creat)
+		return n;
 
+	return neigh_create(tbl, pkey, dev);
+}
 
 #endif
 #endif
diff -u --recursive --new-file v2.3.5/linux/include/net/pkt_cls.h linux/include/net/pkt_cls.h
--- v2.3.5/linux/include/net/pkt_cls.h	Thu Mar 25 09:23:34 1999
+++ linux/include/net/pkt_cls.h	Wed Jun  9 14:45:36 1999
@@ -77,14 +77,11 @@
 	return -1;
 }
 
-extern __inline__ unsigned long cls_set_class(unsigned long *clp, unsigned long cl)
-{
-	cl = xchg(clp, cl);
-	synchronize_bh();
-	return cl;
-}
+
 
 extern int register_tcf_proto_ops(struct tcf_proto_ops *ops);
 extern int unregister_tcf_proto_ops(struct tcf_proto_ops *ops);
+
+
 
 #endif
diff -u --recursive --new-file v2.3.5/linux/include/net/pkt_sched.h linux/include/net/pkt_sched.h
--- v2.3.5/linux/include/net/pkt_sched.h	Thu Apr 22 19:45:19 1999
+++ linux/include/net/pkt_sched.h	Wed Jun  9 14:45:36 1999
@@ -66,9 +66,12 @@
 struct Qdisc_head
 {
 	struct Qdisc_head *forw;
+	struct Qdisc_head *back;
 };
 
 extern struct Qdisc_head qdisc_head;
+extern spinlock_t qdisc_runqueue_lock;
+extern rwlock_t qdisc_tree_lock;
 
 struct Qdisc
 {
@@ -106,6 +109,46 @@
 	int		refcnt;
 };
 
+extern __inline__ void sch_tree_lock(struct Qdisc *q)
+{
+	write_lock(&qdisc_tree_lock);
+	spin_lock_bh(&q->dev->queue_lock);
+}
+
+extern __inline__ void sch_tree_unlock(struct Qdisc *q)
+{
+	spin_unlock_bh(&q->dev->queue_lock);
+	write_unlock(&qdisc_tree_lock);
+}
+
+extern __inline__ void tcf_tree_lock(struct tcf_proto *tp)
+{
+	write_lock(&qdisc_tree_lock);
+	spin_lock_bh(&tp->q->dev->queue_lock);
+}
+
+extern __inline__ void tcf_tree_unlock(struct tcf_proto *tp)
+{
+	spin_unlock_bh(&tp->q->dev->queue_lock);
+	write_unlock(&qdisc_tree_lock);
+}
+
+
+extern __inline__ unsigned long
+cls_set_class(struct tcf_proto *tp, unsigned long *clp, unsigned long cl)
+{
+	tcf_tree_lock(tp);
+	cl = xchg(clp, cl);
+	tcf_tree_unlock(tp);
+	return cl;
+}
+
+extern __inline__ unsigned long
+__cls_set_class(unsigned long *clp, unsigned long cl)
+{
+	return xchg(clp, cl);
+}
+
 
 /* 
    Timer resolution MUST BE < 10% of min_schedulable_packet_size/bandwidth
@@ -343,12 +386,14 @@
 	u32		toks;
 	u32		ptoks;
 	psched_time_t	t_c;
+	spinlock_t	lock;
 	struct qdisc_rate_table *R_tab;
 	struct qdisc_rate_table *P_tab;
 
 	struct tc_stats	stats;
 };
 
+extern int qdisc_copy_stats(struct sk_buff *skb, struct tc_stats *st);
 extern void tcf_police_destroy(struct tcf_police *p);
 extern struct tcf_police * tcf_police_locate(struct rtattr *rta, struct rtattr *est);
 extern int tcf_police_dump(struct sk_buff *skb, struct tcf_police *p);
@@ -384,20 +429,56 @@
 int tc_filter_init(void);
 int pktsched_init(void);
 
-void qdisc_run_queues(void);
-int qdisc_restart(struct device *dev);
+extern void qdisc_run_queues(void);
+extern int qdisc_restart(struct device *dev);
+
+extern spinlock_t qdisc_runqueue_lock;
+
+/* Is it on run list? Reliable only under qdisc_runqueue_lock. */
+
+extern __inline__ int qdisc_on_runqueue(struct Qdisc *q)
+{
+	return q->h.forw != NULL;
+}
+
+/* Is run list not empty? Reliable only under qdisc_runqueue_lock. */
+
+extern __inline__ int qdisc_pending(void)
+{
+	return qdisc_head.forw != &qdisc_head;
+}
+
+/* Add qdisc to tail of run list. Called with BH, disabled on this CPU */
+
+extern __inline__ void qdisc_run(struct Qdisc *q)
+{
+	spin_lock(&qdisc_runqueue_lock);
+	if (!qdisc_on_runqueue(q)) {
+		q->h.forw = &qdisc_head;
+		q->h.back = qdisc_head.back;
+		qdisc_head.back->forw = &q->h;
+		qdisc_head.back = &q->h;
+	}
+	spin_unlock(&qdisc_runqueue_lock);
+}
+
+/* If the device is not throttled, restart it and add to run list.
+ * BH must be disabled on this CPU.
+ */
 
 extern __inline__ void qdisc_wakeup(struct device *dev)
 {
 	if (!dev->tbusy) {
-		struct Qdisc *q = dev->qdisc;
-		if (qdisc_restart(dev) && q->h.forw == NULL) {
-			q->h.forw = qdisc_head.forw;
-			qdisc_head.forw = &q->h;
-		}
+		spin_lock(&dev->queue_lock);
+		if (qdisc_restart(dev))
+			qdisc_run(dev->qdisc);
+		spin_unlock(&dev->queue_lock);
 	}
 }
 
+/* Calculate maximal size of packet seen by hard_start_xmit
+   routine of this device.
+ */
 extern __inline__ unsigned psched_mtu(struct device *dev)
 {
 	unsigned mtu = dev->mtu;
diff -u --recursive --new-file v2.3.5/linux/include/net/route.h linux/include/net/route.h
--- v2.3.5/linux/include/net/route.h	Mon May 31 22:28:07 1999
+++ linux/include/net/route.h	Wed Jun  9 14:45:36 1999
@@ -35,13 +35,6 @@
 
 #define RT_HASH_DIVISOR	    	256
 
-/*
- * Prevents LRU trashing, entries considered equivalent,
- * if the difference between last use times is less then this number.
- */
-#define RT_CACHE_BUBBLE_THRESHOLD	(5*HZ)
-
-
 #define RTO_ONLINK	0x01
 #define RTO_TPROXY	0x80000000
 
@@ -103,6 +96,7 @@
 };
 
 extern struct ip_rt_acct ip_rt_acct[256];
+extern rwlock_t ip_rt_acct_lock;
 
 extern void		ip_rt_init(void);
 extern void		ip_rt_redirect(u32 old_gw, u32 dst, u32 new_gw,
diff -u --recursive --new-file v2.3.5/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.3.5/linux/include/net/tcp.h	Mon May 31 22:28:07 1999
+++ linux/include/net/tcp.h	Tue Jun  8 23:04:46 1999
@@ -289,7 +289,7 @@
 #define TCP_PROBEWAIT_LEN (1*HZ)/* time to wait between probes when
 				 * I've got something to write and
 				 * there is no window			*/
-#define TCP_KEEPALIVE_TIME (180*60*HZ)		/* two hours */
+#define TCP_KEEPALIVE_TIME (120*60*HZ)		/* two hours */
 #define TCP_KEEPALIVE_PROBES	9		/* Max of 9 keepalive probes	*/
 #define TCP_KEEPALIVE_PERIOD ((75*HZ)>>2)	/* period of keepalive check	*/
 
diff -u --recursive --new-file v2.3.5/linux/include/net/udp.h linux/include/net/udp.h
--- v2.3.5/linux/include/net/udp.h	Sun Nov 30 14:00:39 1997
+++ linux/include/net/udp.h	Tue Jun  8 17:58:03 1999
@@ -34,8 +34,14 @@
 
 extern unsigned short udp_good_socknum(void);
 
-#define UDP_NO_CHECK	0
+/* Note: this must match 'valbool' in sock_setsockopt */
+#define UDP_CSUM_NOXMIT		1
 
+/* Used by SunRPC/xprt layer. */
+#define UDP_CSUM_NORCV		2
+
+/* Default, as per the RFC, is to always do csums. */
+#define UDP_CSUM_DEFAULT	0
 
 extern struct proto udp_prot;
 
diff -u --recursive --new-file v2.3.5/linux/include/scsi/scsicam.h linux/include/scsi/scsicam.h
--- v2.3.5/linux/include/scsi/scsicam.h	Thu Jun 12 15:29:27 1997
+++ linux/include/scsi/scsicam.h	Wed Jun  9 16:59:16 1999
@@ -14,4 +14,6 @@
 #define SCSICAM_H
 #include <linux/kdev_t.h>
 extern int scsicam_bios_param (Disk *disk, kdev_t dev, int *ip);
+extern int scsi_partsize(struct buffer_head *bh, unsigned long capacity,
+           unsigned int  *cyls, unsigned int *hds, unsigned int *secs);
 #endif /* def SCSICAM_H */
diff -u --recursive --new-file v2.3.5/linux/include/scsi/sg.h linux/include/scsi/sg.h
--- v2.3.5/linux/include/scsi/sg.h	Fri May  7 11:05:30 1999
+++ linux/include/scsi/sg.h	Mon Jun  7 16:20:36 1999
@@ -12,10 +12,16 @@
 *       Copyright (C) 1998, 1999 Douglas Gilbert
 
 
-    Version: 2.1.32 (990501)
-    This version for later 2.1.x series and 2.2.x kernels
+    Version: 2.1.34 (990603)
+    This version for later 2.1.x and 2.2.x series kernels
     D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au)
 
+    Changes since 2.1.33 (990521)
+        - implement SG_SET_RESERVED_SIZE and associated memory re-org.
+        - add SG_NEXT_CMD_LEN to override SCSI command lengths
+        - add SG_GET_VERSION_NUM to get version expressed as an integer
+    Changes since 2.1.32 (990501)
+        - fix race condition in sg_read() and sg_open()
     Changes since 2.1.31 (990327)
         - add ioctls SG_GET_UNDERRUN_FLAG and _SET_. Change the default
           to _not_ flag underruns (affects aic7xxx driver)
@@ -25,24 +31,6 @@
     Changes since 2.1.30 (990320)
         - memory tweaks: change flags on kmalloc (GFP_KERNEL to GFP_ATOMIC)
         -                increase max allowable mid-level pool usage
-    Changes since 2.1.21 (990315)
-        - skipped to 2.1.30 indicating interface change (revert to 2.1.9)
-        - remove attempt to accomodate cdrecord 1.8, will fix app
-        - keep SG_?ET_RESERVED_SIZE naming for clarity
-    Changes since 2.1.20 (990313)
-        - ommission: left out logic for SG_?ET_ALT_INTERFACE, now added
-    Changes since 2.1.9 (990309)
-        - skipped to version 2.1.20 to indicate some interface changes
-        - incorporate sg changes to make cdrecord 1.8 work (had its
-          own patches that were different from the original)
-        - change SG_?ET_BUFF_SIZE to SG_?ET_RESERVED_SIZE for clarity
-    Changes since 2.1.8 (990303)
-        - debug ">9" option dumps debug for _all_ active sg devices
-        - increase allowable dma pool usage + increase minimum threshhold
-        - pad out sg_scsi_id structure
-    Changes since 2.1.7 (990227)
-        - command queuing now "non-default" [back. compat. with cdparanoia]
-        - Tighten access on some ioctls
 
 
     New features and changes:
@@ -52,24 +40,32 @@
         - the SCSI target, host and driver status are returned
           in unused fields of sg_header (maintaining its original size).
         - asynchronous notification support added (SIGPOLL, SIGIO) for
-          read()s ( write()s should never block).
-        - pack_id logic added so read() can be made to wait for a specific
-          pack_id. 
+          read()s (write()s should never block).
+        - pack_id logic added so read() can wait for a specific pack_id. 
         - uses memory > ISA_DMA_THRESHOLD if adapter allows it (e.g. a
           pci scsi adapter).
         - this driver no longer uses a single SG_BIG_BUFF sized buffer
-          obtained at driver/module init time. Rather it obtains a 
-          SG_SCATTER_SZ buffer when a fd is open()ed and frees it at
-          the corresponding release() (ie pr fd). Hence open() can return
-          ENOMEM! If write() request > SG_SCATTER_SZ bytes for data then
-          it can fail with ENOMEM as well (if so, scale back).
+          obtained at driver/module init time. Rather it tries to obtain a 
+          SG_DEF_RESERVED_SIZE buffer when a fd is open()ed and frees it
+          at the corresponding release() (ie per fd). Actually the "buffer"
+          may be a collection of buffers if scatter-gather is being used.
+        - add SG_SET_RESERVED_SIZE ioctl allowing the user to request a
+          large buffer for duration of current file descriptor's lifetime.
+        - SG_GET_RESERVED_SIZE ioctl can be used to find out how much
+          actually has been reserved.
+        - add SG_NEXT_CMD_LEN ioctl to override SCSI command length on
+          the next write() to this file descriptor.
+        - SG_GET_RESERVED_SIZE's presence as a symbol can be used for
+          compile time identification of the version 2 sg driver.
+          However, it is recommended that run time identification based on
+          calling the ioctl of the same name is a more flexible and
+          safer approach.
         - adds several ioctl calls, see ioctl section below.
-        - SG_SCATTER_SZ's presence indicates this version of "sg" driver.
  
  Good documentation on the original "sg" device interface and usage can be
- found in the Linux HOWTO document: "SCSI Programming HOWTO" by Heiko
- Eissfeldt; last updated 7 May 1996. I will add more info on using the
- extensions in this driver as required. A quick summary:
+ found in the Linux HOWTO document: "SCSI Programming HOWTO" (version 0.5)
+ by Heiko Eissfeldt; last updated 7 May 1996. Here is a quick summary of
+ sg basics:
  An SG device is accessed by writing SCSI commands plus any associated 
  outgoing data to it; the resulting status codes and any incoming data
  are then obtained by a read call. The device can be opened O_NONBLOCK
@@ -88,38 +84,37 @@
  The given SCSI command has its LUN field overwritten internally by the
  value associated with the device that has been opened.
 
- Memory (RAM) is used within this driver for direct memory access (DMA)
- in transferring data to and from the SCSI device. The dreaded ENOMEM
- seems to be more prevalent under early 2.2.x kernels than under the
- 2.0.x kernel series. For a given (large) transfer the memory obtained by
- this driver must be contiguous or scatter-gather must be used (if
- supported by the adapter). [Furthermore, ISA SCSI adapters can only use
- memory below the 16MB level on a i386.]
- This driver tries hard to find some suitable memory before admitting
- defeat and returning ENOMEM. All is not lost if application writers
- then back off the amount they are requesting. The value returned by
- the SG_GET_RESERVED_SIZE ioctl is guaranteed to be available (one
- per fd). This driver does the following:
-   -  attempts to reserve a SG_SCATTER_SZ sized buffer on open(). The
-      actual amount reserved is given by the SG_GET_RESERVED_SIZE ioctl().
-   -  each write() needs to reserve a DMA buffer of the size of the
-      data buffer indicated (excluding sg_header and command overhead).
-      This buffer, depending on its size, adapter type (ISA or not) and
-      the amount of memory available will be obtained from the kernel
-      directly (get_free_pages or kmalloc) or the from the scsi mid-level
-      dma pool (taking care not to exhaust it).
-      If the buffer requested is > SG_SCATTER_SZ or memory is tight then
-      scatter-gather will be used if supported by the adapter.
-  -   write() will also attempt to use the buffer reserved on open()
-      if it is large enough.
- The above strategy ensures that a write() can always depend on a buffer 
- of the size indicated by the SG_GET_RESERVED_SIZE ioctl() (which could be
- 0, but at least the app knows things are tight in advance).
- Hence application writers can adopt quite aggressive strategies (e.g. 
- requesting 512KB) and scale them back in the face of ENOMEM errors.
- N.B. Queuing up commands also ties up kernel memory.
+ This device currently uses "indirect IO" in the sense that data is
+ DMAed into kernel buffers from the hardware and afterwards is
+ transferred into the user space (or vice versa if you are writing).
+ Transfer speeds or up to 20 to 30MBytes/sec have been measured using
+ indirect IO. For faster throughputs "direct IO" which cuts out the
+ double handling of data is required. This will also need a new interface.
+
+ Grabbing memory for those kernel buffers used in this driver for DMA may
+ cause the dreaded ENOMEM error. This error seems to be more prevalent 
+ under early 2.2.x kernels than under the 2.0.x kernel series. For a given 
+ (large) transfer the memory obtained by this driver must be contiguous or
+ scatter-gather must be used (if supported by the adapter). [Furthermore, 
+ ISA SCSI adapters can only use memory below the 16MB level on a i386.]
+
+ When a "sg" device is open()ed O_RDWR then this driver will attempt to
+ reserve a buffer of SG_DEF_RESERVED_SIZE that will be used by subsequent
+ write()s on this file descriptor as long as:
+    -  it is not already in use (eg when command queuing is in use)
+    -  the write() does not call for a buffer size larger than the
+       reserved size.
+ In these cases the write() will attempt to find the memory it needs for
+ DMA buffers dynamically and in the worst case will fail with ENOMEM.
+ The amount of memory actually reserved depends on various dynamic factors
+ and can be checked with the SG_GET_RESERVED_SIZE ioctl(). [In a very
+ tight memory situation it may yield 0!] The size of the reserved buffer
+ can be changed with the SG_SET_RESERVED_SIZE ioctl(). It should be
+ followed with a call to the SG_GET_RESERVED_SIZE ioctl() to find out how
+ much was actually reserved.
 
- More documentation can be found at www.torque.net/sg
+ More documentation plus test and utility programs can be found at 
+ http://www.torque.net/sg
 */
 
 #define SG_MAX_SENSE 16   /* too little, unlikely to change in 2.2.x */
@@ -129,7 +124,7 @@
     int pack_len;    /* [o] reply_len (ie useless), ignored as input */
     int reply_len;   /* [i] max length of expected reply (inc. sg_header) */
     int pack_id;     /* [io] id number of packet (use ints >= 0) */
-    int result;      /* [o] 0==ok, else (+ve) Unix errno code (e.g. EIO) */
+    int result;      /* [o] 0==ok, else (+ve) Unix errno (best ignored) */
     unsigned int twelve_byte:1; 
         /* [i] Force 12 byte command length for group 6 & 7 commands  */
     unsigned int target_status:5;   /* [o] scsi status from target */
@@ -154,9 +149,9 @@
     int unused3;  
 } Sg_scsi_id;
 
-/* ioctls  ( _GET_s yield result via 'int *' 3rd argument unless 
-            otherwise indicated */
-#define SG_SET_TIMEOUT 0x2201  /* unit: jiffies, 10ms on i386 */
+/* IOCTLs: ( _GET_s yield result via 'int *' 3rd argument unless 
+             otherwise indicated) */
+#define SG_SET_TIMEOUT 0x2201  /* unit: jiffies (10ms on i386) */
 #define SG_GET_TIMEOUT 0x2202  /* yield timeout as _return_ value */
 
 #define SG_EMULATED_HOST 0x2203 /* true for emulated host adapter (ATAPI) */
@@ -165,23 +160,21 @@
 #define SG_SET_TRANSFORM 0x2204
 #define SG_GET_TRANSFORM 0x2205
 
-#define SG_SET_RESERVED_SIZE 0x2275  /* currently ignored, future addition */
-/* Following yields buffer reserved by open(): 0 <= x <= SG_SCATTER_SZ */
-#define SG_GET_RESERVED_SIZE 0x2272
+#define SG_SET_RESERVED_SIZE 0x2275  /* request a new reserved buffer size */
+#define SG_GET_RESERVED_SIZE 0x2272  /* actual size of reserved buffer */
 
 /* The following ioctl takes a 'Sg_scsi_id *' object as its 3rd argument. */
-#define SG_GET_SCSI_ID 0x2276   /* Yields fd's bus,chan,dev,lun+type */
+#define SG_GET_SCSI_ID 0x2276   /* Yields fd's bus, chan, dev, lun + type */
 /* SCSI id information can also be obtained from SCSI_IOCTL_GET_IDLUN */
 
-/* Override adapter setting and always DMA using low memory ( <16MB on i386).
-   Default is 0 (off - use adapter setting) */
+/* Override host setting and always DMA using low memory ( <16MB on i386) */
 #define SG_SET_FORCE_LOW_DMA 0x2279  /* 0-> use adapter setting, 1-> force */
 #define SG_GET_LOW_DMA 0x227a   /* 0-> use all ram for dma; 1-> low dma ram */
 
 /* When SG_SET_FORCE_PACK_ID set to 1, pack_id is input to read() which
    will attempt to read that pack_id or block (or return EAGAIN). If 
    pack_id is -1 then read oldest waiting. When ...FORCE_PACK_ID set to 0
-   (default) then pack_id ignored by read() and oldest readable fetched. */ 
+   then pack_id ignored by read() and oldest readable fetched. */ 
 #define SG_SET_FORCE_PACK_ID 0x227b
 #define SG_GET_PACK_ID 0x227c /* Yields oldest readable pack_id (or -1) */
 
@@ -194,43 +187,47 @@
 /* Yields max scatter gather tablesize allowed by current host adapter */
 #define SG_GET_SG_TABLESIZE 0x227F  /* 0 implies can't do scatter gather */
 
-/* Control whether sequencing per file descriptor (default) or per device */
-#define SG_GET_MERGE_FD 0x2274   /* 0-> per fd (default), 1-> per device */
+/* Control whether sequencing per file descriptor or per device */
+#define SG_GET_MERGE_FD 0x2274   /* 0-> per fd, 1-> per device */
 #define SG_SET_MERGE_FD 0x2273   /* Attempt to change sequencing state,
-  if more than 1 fd open on device, will fail with EBUSY */
+  if more than current fd open on device, will fail with EBUSY */
 
 /* Get/set command queuing state per fd (default is SG_DEF_COMMAND_Q) */
 #define SG_GET_COMMAND_Q 0x2270   /* Yields 0 (queuing off) or 1 (on) */
 #define SG_SET_COMMAND_Q 0x2271   /* Change queuing state with 0 or 1 */
 
-/* Get/set whether DMA underrun will cause an error (DID_ERROR) [this only
-   currently applies to the [much-used] aic7xxx driver) */
+/* Get/set whether DMA underrun will cause an error (DID_ERROR). This only
+   currently applies to the [much-used] aic7xxx driver. */
 #define SG_GET_UNDERRUN_FLAG 0x2280 /* Yields 0 (don't flag) or 1 (flag) */
 #define SG_SET_UNDERRUN_FLAG 0x2281 /* Change flag underrun state */
 
+#define SG_GET_VERSION_NUM 0x2282 /* Example: version 2.1.34 yields 20134 */
+#define SG_NEXT_CMD_LEN 0x2283  /* override SCSI command length with given
+                   number on the next write() on this file descriptor */
+
+
+#define SG_SCATTER_SZ (8 * 4096)  /* PAGE_SIZE not available to user */
+/* Largest size (in bytes) a single scatter-gather list element can have.
+   The value must be a power of 2 and <= (PAGE_SIZE * 32) [131072 bytes on 
+   i386]. The minimum value is PAGE_SIZE. If scatter-gather not supported
+   by adapter then this value is the largest data block that can be
+   read/written by a single scsi command. The user can find the value of
+   PAGE_SIZE by calling getpagesize() defined in unistd.h . */
 
 #define SG_DEFAULT_TIMEOUT (60*HZ) /* HZ == 'jiffies in 1 second' */
 #define SG_DEFAULT_RETRIES 1
 
-/* Default modes, commented if they differ from original sg driver */
+/* Defaults, commented if they differ from original sg driver */
 #define SG_DEF_COMMAND_Q 0
 #define SG_DEF_MERGE_FD 0       /* was 1 -> per device sequencing */
 #define SG_DEF_FORCE_LOW_DMA 0  /* was 1 -> memory below 16MB on i386 */
 #define SG_DEF_FORCE_PACK_ID 0
 #define SG_DEF_UNDERRUN_FLAG 0
+#define SG_DEF_RESERVED_SIZE SG_SCATTER_SZ
 
 /* maximum outstanding requests, write() yields EDOM if exceeded */
 #define SG_MAX_QUEUE 16
 
-#define SG_SCATTER_SZ (8 * 4096)  /* PAGE_SIZE not available to user */
-/* Largest size (in bytes) a single scatter-gather list element can have.
-   The value must be a power of 2 and <= (PAGE_SIZE * 32) [131072 bytes on 
-   i386]. The minimum value is PAGE_SIZE. If scatter-gather not supported
-   by adapter then this value is the largest data block that can be
-   read/written by a single scsi command. Max number of scatter-gather
-   list elements seems to be limited to 255. */
-
-#define SG_BIG_BUFF SG_SCATTER_SZ       /* for backward compatibility */
-/* #define SG_BIG_BUFF (SG_SCATTER_SZ * 8) */ /* =256KB, if you want */
+#define SG_BIG_BUFF SG_DEF_RESERVED_SIZE    /* for backward compatibility */
 
 #endif
diff -u --recursive --new-file v2.3.5/linux/ipc/msg.c linux/ipc/msg.c
--- v2.3.5/linux/ipc/msg.c	Fri May 14 18:55:30 1999
+++ linux/ipc/msg.c	Mon Jun  7 12:20:50 1999
@@ -8,6 +8,7 @@
  * Fixed up the unchecked user space derefs
  * Copyright (C) 1998 Alan Cox & Andi Kleen
  *
+ * /proc/sysvipc/msg support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
  */
 
 #include <linux/malloc.h>
@@ -15,6 +16,7 @@
 #include <linux/interrupt.h>
 #include <linux/smp_lock.h>
 #include <linux/init.h>
+#include <linux/proc_fs.h>
 
 #include <asm/uaccess.h>
 
@@ -23,6 +25,9 @@
 static void freeque (int id);
 static int newque (key_t key, int msgflg);
 static int findkey (key_t key);
+#ifdef CONFIG_PROC_FS
+static int sysvipc_msg_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);
+#endif
 
 static struct msqid_ds *msgque[MSGMNI];
 static int msgbytes = 0;
@@ -35,11 +40,18 @@
 void __init msg_init (void)
 {
 	int id;
-	
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *ent;
+#endif
+
 	for (id = 0; id < MSGMNI; id++) 
 		msgque[id] = (struct msqid_ds *) IPC_UNUSED;
 	msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0;
 	init_waitqueue_head(&msg_lock);
+#ifdef CONFIG_PROC_FS
+	ent = create_proc_entry("sysvipc/msg", 0, 0);
+	ent->read_proc = sysvipc_msg_read_proc;
+#endif
 	return;
 }
 
@@ -492,3 +504,49 @@
 	return err;
 }
 
+#ifdef CONFIG_PROC_FS
+static int sysvipc_msg_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+{
+	off_t pos = 0;
+	off_t begin = 0;
+	int i, len = 0;
+
+	len += sprintf(buffer, "       key      msqid perms cbytes  qnum lspid lrpid   uid   gid  cuid  cgid      stime      rtime      ctime\n");
+
+	for(i = 0; i < MSGMNI; i++)
+		if(msgque[i] != IPC_UNUSED) {
+			len += sprintf(buffer + len, "%10d %10d  %4o  %5u %5u %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu\n",
+				msgque[i]->msg_perm.key,
+				msgque[i]->msg_perm.seq * MSGMNI + i,
+				msgque[i]->msg_perm.mode,
+				msgque[i]->msg_cbytes,
+				msgque[i]->msg_qnum,
+				msgque[i]->msg_lspid,
+				msgque[i]->msg_lrpid,
+				msgque[i]->msg_perm.uid,
+				msgque[i]->msg_perm.gid,
+				msgque[i]->msg_perm.cuid,
+				msgque[i]->msg_perm.cgid,
+				msgque[i]->msg_stime,
+				msgque[i]->msg_rtime,
+				msgque[i]->msg_ctime);
+
+			pos += len;
+			if(pos < offset) {
+				len = 0;
+				begin = pos;
+			}
+			if(pos > offset + length)
+				goto done;
+		}
+	*eof = 1;
+done:
+	*start = buffer + (offset - begin);
+	len -= (offset - begin);
+	if(len > length)
+		len = length;
+	if(len < 0)
+		len = 0;
+	return len;
+}
+#endif
diff -u --recursive --new-file v2.3.5/linux/ipc/sem.c linux/ipc/sem.c
--- v2.3.5/linux/ipc/sem.c	Fri May 14 18:55:30 1999
+++ linux/ipc/sem.c	Mon Jun  7 12:20:50 1999
@@ -48,11 +48,14 @@
  *      better but only get the semops right which only wait for zero or
  *      increase. If there are decrement operations in the operations
  *      array we do the same as before.
+ *
+ * /proc/sysvipc/sem support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
  */
 
 #include <linux/malloc.h>
 #include <linux/smp_lock.h>
 #include <linux/init.h>
+#include <linux/proc_fs.h>
 
 #include <asm/uaccess.h>
 
@@ -60,6 +63,9 @@
 static int newary (key_t, int, int);
 static int findkey (key_t key);
 static void freeary (int id);
+#ifdef CONFIG_PROC_FS
+static int sysvipc_sem_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);
+#endif
 
 static struct semid_ds *semary[SEMMNI];
 static int used_sems = 0, used_semids = 0;
@@ -71,11 +77,18 @@
 void __init sem_init (void)
 {
 	int i;
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *ent;
+#endif
 
 	init_waitqueue_head(&sem_lock);
 	used_sems = used_semids = max_semid = sem_seq = 0;
 	for (i = 0; i < SEMMNI; i++)
 		semary[i] = (struct semid_ds *) IPC_UNUSED;
+#ifdef CONFIG_PROC_FS
+	ent = create_proc_entry("sysvipc/sem", 0, 0);
+	ent->read_proc = sysvipc_sem_read_proc;
+#endif
 	return;
 }
 
@@ -785,3 +798,46 @@
 	}
 	current->semundo = NULL;
 }
+
+#ifdef CONFIG_PROC_FS
+static int sysvipc_sem_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+{
+	off_t pos = 0;
+	off_t begin = 0;
+	int i, len = 0;
+
+	len += sprintf(buffer, "       key      semid perms nsems   uid   gid  cuid  cgid      otime      ctime\n");
+
+	for(i = 0; i < SEMMNI; i++)
+		if(semary[i] != IPC_UNUSED) {
+			len += sprintf(buffer + len, "%10d %10d  %4o %5u %5u %5u %5u %5u %10lu %10lu\n",
+				semary[i]->sem_perm.key,
+				semary[i]->sem_perm.seq * SEMMNI + i,
+				semary[i]->sem_perm.mode,
+				semary[i]->sem_nsems,
+				semary[i]->sem_perm.uid,
+				semary[i]->sem_perm.gid,
+				semary[i]->sem_perm.cuid,
+				semary[i]->sem_perm.cgid,
+				semary[i]->sem_otime,
+				semary[i]->sem_ctime);
+
+			pos += len;
+			if(pos < offset) {
+				len = 0;
+	    		begin = pos;
+			}
+			if(pos > offset + length)
+				goto done;
+		}
+	*eof = 1;
+done:
+	*start = buffer + (offset - begin);
+	len -= (offset - begin);
+	if(len > length)
+		len = length;
+	if(len < 0)
+		len = 0;
+	return len;
+}
+#endif
diff -u --recursive --new-file v2.3.5/linux/ipc/shm.c linux/ipc/shm.c
--- v2.3.5/linux/ipc/shm.c	Fri May 14 18:55:30 1999
+++ linux/ipc/shm.c	Mon Jun  7 12:20:50 1999
@@ -4,6 +4,8 @@
  *         Many improvements/fixes by Bruno Haible.
  * Replaced `struct shm_desc' by `struct vm_area_struct', July 1994.
  * Fixed the shm swap deallocation (shm_unuse()), August 1998 Andrea Arcangeli.
+ *
+ * /proc/sysvipc/shm support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
  */
 
 #include <linux/malloc.h>
@@ -12,6 +14,7 @@
 #include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -26,6 +29,9 @@
 static void shm_close (struct vm_area_struct *shmd);
 static unsigned long shm_nopage(struct vm_area_struct *, unsigned long, int);
 static int shm_swapout(struct vm_area_struct *, struct page *);
+#ifdef CONFIG_PROC_FS
+static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);
+#endif
 
 static int shm_tot = 0; /* total number of shared memory pages */
 static int shm_rss = 0; /* number of shared memory pages that are in memory */
@@ -44,11 +50,18 @@
 void __init shm_init (void)
 {
 	int id;
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *ent;
+#endif
 
 	for (id = 0; id < SHMMNI; id++)
 		shm_segs[id] = (struct shmid_kernel *) IPC_UNUSED;
 	shm_tot = shm_rss = shm_seq = max_shmid = used_segs = 0;
 	init_waitqueue_head(&shm_lock);
+#ifdef CONFIG_PROC_FS
+	ent = create_proc_entry("sysvipc/shm", 0, 0);
+	ent->read_proc = sysvipc_shm_read_proc;
+#endif
 	return;
 }
 
@@ -762,3 +775,50 @@
 					return;
 				}
 }
+
+#ifdef CONFIG_PROC_FS
+static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+{
+	off_t pos = 0;
+	off_t begin = 0;
+	int i, len = 0;
+
+    	len += sprintf(buffer, "       key      shmid perms       size  cpid  lpid nattch   uid   gid  cuid  cgid      atime      dtime      ctime\n");
+
+    	for(i = 0; i < SHMMNI; i++)
+		if(shm_segs[i] != IPC_UNUSED) {
+	    		len += sprintf(buffer + len, "%10d %10d  %4o %10d %5u %5u  %5d %5u %5u %5u %5u %10lu %10lu %10lu\n",
+			shm_segs[i]->u.shm_perm.key,
+			shm_segs[i]->u.shm_perm.seq * SHMMNI + i,
+			shm_segs[i]->u.shm_perm.mode,
+			shm_segs[i]->u.shm_segsz,
+			shm_segs[i]->u.shm_cpid,
+			shm_segs[i]->u.shm_lpid,
+			shm_segs[i]->u.shm_nattch,
+			shm_segs[i]->u.shm_perm.uid,
+			shm_segs[i]->u.shm_perm.gid,
+			shm_segs[i]->u.shm_perm.cuid,
+			shm_segs[i]->u.shm_perm.cgid,
+			shm_segs[i]->u.shm_atime,
+			shm_segs[i]->u.shm_dtime,
+			shm_segs[i]->u.shm_ctime);
+
+			pos += len;
+			if(pos < offset) {
+				len = 0;
+				begin = pos;
+			}
+			if(pos > offset + length)
+				goto done;
+		}
+	*eof = 1;
+done:
+	*start = buffer + (offset - begin);
+	len -= (offset - begin);
+	if(len > length)
+		len = length;
+	if(len < 0)
+		len = 0;
+	return len;
+}
+#endif
diff -u --recursive --new-file v2.3.5/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.3.5/linux/kernel/ksyms.c	Mon May 17 09:55:23 1999
+++ linux/kernel/ksyms.c	Tue Jun  8 10:47:58 1999
@@ -82,6 +82,7 @@
 /* process memory management */
 EXPORT_SYMBOL(do_mmap);
 EXPORT_SYMBOL(do_munmap);
+EXPORT_SYMBOL(do_brk);
 EXPORT_SYMBOL(exit_mm);
 EXPORT_SYMBOL(exit_files);
 EXPORT_SYMBOL(exit_fs);
@@ -345,7 +346,7 @@
 
 /* Program loader interfaces */
 EXPORT_SYMBOL(setup_arg_pages);
-EXPORT_SYMBOL(copy_strings);
+EXPORT_SYMBOL(copy_strings_kernel);
 EXPORT_SYMBOL(do_execve);
 EXPORT_SYMBOL(flush_old_exec);
 EXPORT_SYMBOL(open_dentry);
diff -u --recursive --new-file v2.3.5/linux/kernel/signal.c linux/kernel/signal.c
--- v2.3.5/linux/kernel/signal.c	Mon May 31 22:28:07 1999
+++ linux/kernel/signal.c	Mon Jun  7 16:14:06 1999
@@ -11,6 +11,7 @@
 #include <linux/unistd.h>
 #include <linux/smp_lock.h>
 #include <linux/init.h>
+#include <linux/sched.h>
 
 #include <asm/uaccess.h>
 
@@ -324,7 +325,7 @@
 
 		if (nr_queued_signals < max_queued_signals) {
 			q = (struct signal_queue *)
-			    kmem_cache_alloc(signal_queue_cachep, GFP_KERNEL);
+			    kmem_cache_alloc(signal_queue_cachep, GFP_ATOMIC);
 		}
 		
 		if (q) {
@@ -417,6 +418,7 @@
 	if (t->sig->action[sig-1].sa.sa_handler == SIG_IGN)
 		t->sig->action[sig-1].sa.sa_handler = SIG_DFL;
 	sigdelset(&t->blocked, sig);
+	recalc_sigpending(t);
 	spin_unlock_irqrestore(&t->sigmask_lock, flags);
 
 	return send_sig_info(sig, info, t);
diff -u --recursive --new-file v2.3.5/linux/kernel/sys.c linux/kernel/sys.c
--- v2.3.5/linux/kernel/sys.c	Fri May 14 18:55:30 1999
+++ linux/kernel/sys.c	Mon Jun  7 16:17:59 1999
@@ -901,6 +901,8 @@
 		return -EINVAL;
 	if(copy_from_user(&new_rlim, rlim, sizeof(*rlim)))
 		return -EFAULT;
+	if (new_rlim.rlim_cur < 0 || new_rlim.rlim_max < 0)
+		return -EINVAL;
 	old_rlim = current->rlim + resource;
 	if (((new_rlim.rlim_cur > old_rlim->rlim_max) ||
 	     (new_rlim.rlim_max > old_rlim->rlim_max)) &&
diff -u --recursive --new-file v2.3.5/linux/mm/memory.c linux/mm/memory.c
--- v2.3.5/linux/mm/memory.c	Mon May 31 22:28:07 1999
+++ linux/mm/memory.c	Tue Jun  8 14:09:57 1999
@@ -543,19 +543,6 @@
 }
 
 /*
- * sanity-check function..
- */
-static void put_page(pte_t * page_table, pte_t pte)
-{
-	if (!pte_none(*page_table)) {
-		free_page_and_swap_cache(pte_page(pte));
-		return;
-	}
-/* no need for flush_tlb */
-	set_pte(page_table, pte);
-}
-
-/*
  * This routine is used to map in a page into an address space: needed by
  * execve() for the initial stack and environment pages.
  */
@@ -612,21 +599,15 @@
  * and potentially makes it more efficient.
  */
 static int do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma,
-	unsigned long address, pte_t *page_table)
+	unsigned long address, pte_t *page_table, pte_t pte)
 {
-	pte_t pte;
 	unsigned long old_page, new_page;
 	struct page * page_map;
 	
-	pte = *page_table;
 	new_page = __get_free_page(GFP_USER);
-	/* Did someone else copy this page for us while we slept? */
+	/* Did swap_out() unmapped the protected page while we slept? */
 	if (pte_val(*page_table) != pte_val(pte))
 		goto end_wp_page;
-	if (!pte_present(pte))
-		goto end_wp_page;
-	if (pte_write(pte))
-		goto end_wp_page;
 	old_page = pte_page(pte);
 	if (MAP_NR(old_page) >= max_mapnr)
 		goto bad_wp_page;
@@ -650,36 +631,42 @@
 		delete_from_swap_cache(page_map);
 		/* FallThrough */
 	case 1:
-		/* We can release the kernel lock now.. */
-		unlock_kernel();
-
 		flush_cache_page(vma, address);
 		set_pte(page_table, pte_mkdirty(pte_mkwrite(pte)));
 		flush_tlb_page(vma, address);
 end_wp_page:
+		/*
+		 * We can release the kernel lock now.. Now swap_out will see
+		 * a dirty page and so won't get confused and flush_tlb_page
+		 * won't SMP race. -Andrea
+		 */
+		unlock_kernel();
+
 		if (new_page)
 			free_page(new_page);
 		return 1;
 	}
 		
-	unlock_kernel();
 	if (!new_page)
-		return 0;
+		goto no_new_page;
 
-	if (PageReserved(mem_map + MAP_NR(old_page)))
+	if (PageReserved(page_map))
 		++vma->vm_mm->rss;
 	copy_cow_page(old_page,new_page);
 	flush_page_to_ram(old_page);
 	flush_page_to_ram(new_page);
 	flush_cache_page(vma, address);
 	set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
-	free_page(old_page);
 	flush_tlb_page(vma, address);
+	unlock_kernel();
+	__free_page(page_map);
 	return 1;
 
 bad_wp_page:
 	printk("do_wp_page: bogus page at address %08lx (%08lx)\n",address,old_page);
 	send_sig(SIGKILL, tsk, 1);
+no_new_page:
+	unlock_kernel();
 	if (new_page)
 		free_page(new_page);
 	return 0;
@@ -816,7 +803,7 @@
 		tsk->min_flt++;
 		flush_page_to_ram(page);
 	}
-	put_page(page_table, entry);
+	set_pte(page_table, entry);
 	return 1;
 }
 
@@ -874,7 +861,7 @@
 	} else if (atomic_read(&mem_map[MAP_NR(page)].count) > 1 &&
 		   !(vma->vm_flags & VM_SHARED))
 		entry = pte_wrprotect(entry);
-	put_page(page_table, entry);
+	set_pte(page_table, entry);
 	/* no need to invalidate: a not-present page shouldn't be cached */
 	return 1;
 }
@@ -908,7 +895,7 @@
 	flush_tlb_page(vma, address);
 	if (write_access) {
 		if (!pte_write(entry))
-			return do_wp_page(tsk, vma, address, pte);
+			return do_wp_page(tsk, vma, address, pte, entry);
 
 		entry = pte_mkdirty(entry);
 		set_pte(pte, entry);
diff -u --recursive --new-file v2.3.5/linux/mm/mmap.c linux/mm/mmap.c
--- v2.3.5/linux/mm/mmap.c	Mon May 31 22:28:07 1999
+++ linux/mm/mmap.c	Mon Jun  7 11:15:33 1999
@@ -84,6 +84,13 @@
 	}
 }
 
+/*
+ *  sys_brk() for the most part doesn't need the global kernel
+ *  lock, except when an application is doing something nasty
+ *  like trying to un-brk an area that has already been mapped
+ *  to a regular file.  in this case, the unmapping will need
+ *  to invoke file system routines that need the global lock.
+ */
 asmlinkage unsigned long sys_brk(unsigned long brk)
 {
 	unsigned long rlim, retval;
@@ -92,20 +99,6 @@
 
 	down(&mm->mmap_sem);
 
-	/*
-	 * This lock-kernel is one of the main contention points for
-	 * certain normal loads.  And it really should not be here: almost
-	 * everything in brk()/mmap()/munmap() is protected sufficiently by
-	 * the mmap semaphore that we got above.
-	 *
-	 * We should move this into the few things that really want the
-	 * lock, namely anything that actually touches a file descriptor
-	 * etc.  We can do all the normal anonymous mapping cases without
-	 * ever getting the lock at all - the actual memory management
-	 * code is already completely thread-safe.
-	 */
-	lock_kernel();
-
 	if (brk < mm->end_code)
 		goto out;
 	newbrk = PAGE_ALIGN(brk);
@@ -134,15 +127,12 @@
 		goto out;
 
 	/* Ok, looks good - let it rip. */
-	if (do_mmap(NULL, oldbrk, newbrk-oldbrk,
-		   PROT_READ|PROT_WRITE|PROT_EXEC,
-		   MAP_FIXED|MAP_PRIVATE, 0) != oldbrk)
+	if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
 		goto out;
 set_brk:
 	mm->brk = brk;
 out:
 	retval = mm->brk;
-	unlock_kernel();
 	up(&mm->mmap_sem);
 	return retval;
 }
@@ -470,6 +460,28 @@
 	return NULL;
 }
 
+struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr)
+{
+	struct vm_area_struct * vma;
+	unsigned long start;
+
+	addr &= PAGE_MASK;
+	vma = find_vma(tsk->mm,addr);
+	if (!vma)
+		return NULL;
+	if (vma->vm_start <= addr)
+		return vma;
+	if (!(vma->vm_flags & VM_GROWSDOWN))
+		return NULL;
+	start = vma->vm_start;
+	if (expand_stack(vma, addr))
+		return NULL;
+	if (vma->vm_flags & VM_LOCKED) {
+		make_pages_present(addr, start);
+	}
+	return vma;
+}
+
 /* Normal function to fix up a mapping
  * This function is the default for when an area has no specific
  * function.  This may be used as part of a more specific routine.
@@ -665,6 +677,8 @@
 		end = end > mpnt->vm_end ? mpnt->vm_end : end;
 		size = end - st;
 
+		lock_kernel();
+
 		if (mpnt->vm_ops && mpnt->vm_ops->unmap)
 			mpnt->vm_ops->unmap(mpnt, st, size);
 
@@ -679,6 +693,8 @@
 		 * Fix the mapping, and free the old area if it wasn't reused.
 		 */
 		extra = unmap_fixup(mpnt, st, size, extra);
+
+		unlock_kernel();
 	}
 
 	/* Release the extra vma struct if it wasn't used */
@@ -696,11 +712,85 @@
 	int ret;
 
 	down(&current->mm->mmap_sem);
-	lock_kernel();
 	ret = do_munmap(addr, len);
-	unlock_kernel();
 	up(&current->mm->mmap_sem);
 	return ret;
+}
+
+/*
+ *  this is really a simplified "do_mmap".  it only handles
+ *  anonymous maps.  eventually we may be able to do some
+ *  brk-specific accounting here.
+ */
+unsigned long do_brk(unsigned long addr, unsigned long len)
+{
+	struct mm_struct * mm = current->mm;
+	struct vm_area_struct * vma;
+	unsigned long flags, retval;
+
+	/*
+	 * mlock MCL_FUTURE?
+	 */
+	if (mm->def_flags & VM_LOCKED) {
+		unsigned long locked = mm->locked_vm << PAGE_SHIFT;
+		locked += len;
+		if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)
+			return -EAGAIN;
+	}
+
+	/*
+	 * Clear old maps.  this also does some error checking for us
+	 */
+	retval = do_munmap(addr, len);
+	if (retval != 0)
+		return retval;
+
+	/* Check against address space limits *after* clearing old maps... */
+	if ((mm->total_vm << PAGE_SHIFT) + len
+	    > current->rlim[RLIMIT_AS].rlim_cur)
+		return -ENOMEM;
+
+	if (mm->map_count > MAX_MAP_COUNT)
+		return -ENOMEM;
+
+	if (!vm_enough_memory(len >> PAGE_SHIFT))
+		return -ENOMEM;
+
+	/*
+	 * create a vma struct for an anonymous mapping
+	 */
+	vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+	if (!vma)
+		return -ENOMEM;
+
+	vma->vm_mm = mm;
+	vma->vm_start = addr;
+	vma->vm_end = addr + len;
+	vma->vm_flags = vm_flags(PROT_READ|PROT_WRITE|PROT_EXEC,
+				MAP_FIXED|MAP_PRIVATE) | mm->def_flags;
+
+	vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
+	vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f];
+	vma->vm_ops = NULL;
+	vma->vm_offset = 0;
+	vma->vm_file = NULL;
+	vma->vm_pte = 0;
+
+	/*
+	 * merge_segments may merge our vma, so we can't refer to it
+	 * after the call.  Save the values we need now ...
+	 */
+	flags = vma->vm_flags;
+	addr = vma->vm_start;
+	insert_vm_struct(mm, vma);
+	merge_segments(mm, vma->vm_start, vma->vm_end);
+	
+	mm->total_vm += len >> PAGE_SHIFT;
+	if (flags & VM_LOCKED) {
+		mm->locked_vm += len >> PAGE_SHIFT;
+		make_pages_present(addr, addr + len);
+	}
+	return addr;
 }
 
 /* Build the AVL tree corresponding to the VMA list. */
diff -u --recursive --new-file v2.3.5/linux/mm/mremap.c linux/mm/mremap.c
--- v2.3.5/linux/mm/mremap.c	Sun Nov 22 09:38:19 1998
+++ linux/mm/mremap.c	Thu Jun  3 23:15:29 1999
@@ -134,12 +134,14 @@
 			new_vma->vm_start = new_addr;
 			new_vma->vm_end = new_addr+new_len;
 			new_vma->vm_offset = vma->vm_offset + (addr - vma->vm_start);
+			lock_kernel();
 			if (new_vma->vm_file)
 				new_vma->vm_file->f_count++;
 			if (new_vma->vm_ops && new_vma->vm_ops->open)
 				new_vma->vm_ops->open(new_vma);
 			insert_vm_struct(current->mm, new_vma);
 			merge_segments(current->mm, new_vma->vm_start, new_vma->vm_end);
+			unlock_kernel();
 			do_munmap(addr, old_len);
 			current->mm->total_vm += new_len >> PAGE_SHIFT;
 			if (new_vma->vm_flags & VM_LOCKED) {
@@ -166,7 +168,6 @@
 	unsigned long ret = -EINVAL;
 
 	down(&current->mm->mmap_sem);
-	lock_kernel();
 	if (addr & ~PAGE_MASK)
 		goto out;
 	old_len = PAGE_ALIGN(old_len);
@@ -239,7 +240,6 @@
 	else
 		ret = -ENOMEM;
 out:
-	unlock_kernel();
 	up(&current->mm->mmap_sem);
 	return ret;
 }
diff -u --recursive --new-file v2.3.5/linux/mm/swapfile.c linux/mm/swapfile.c
--- v2.3.5/linux/mm/swapfile.c	Mon May 31 22:28:07 1999
+++ linux/mm/swapfile.c	Mon Jun  7 16:20:36 1999
@@ -5,6 +5,7 @@
  *  Swap reorganised 29.12.95, Stephen Tweedie
  */
 
+#include <linux/config.h>
 #include <linux/malloc.h>
 #include <linux/smp_lock.h>
 #include <linux/kernel_stat.h>
@@ -573,7 +574,7 @@
 	} else if (S_ISREG(swap_dentry->d_inode->i_mode)) {
 		error = -EBUSY;
 		for (i = 0 ; i < nr_swapfiles ; i++) {
-			if (i == type)
+			if (i == type || !swap_info[i].swap_file)
 				continue;
 			if (swap_dentry->d_inode == swap_info[i].swap_file->d_inode)
 				goto bad_swap;
diff -u --recursive --new-file v2.3.5/linux/net/core/dev.c linux/net/core/dev.c
--- v2.3.5/linux/net/core/dev.c	Wed Jun  2 14:44:39 1999
+++ linux/net/core/dev.c	Wed Jun  9 14:45:36 1999
@@ -263,13 +263,13 @@
 {
 	struct device *dev;
 
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if (strcmp(dev->name, name) == 0)
 			goto out;
 	}
 out:
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 	return dev;
 }
 
@@ -277,13 +277,13 @@
 {
 	struct device *dev;
 
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if (dev->ifindex == ifindex)
 			goto out;
 	}
 out:
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 	return dev;
 }
 
@@ -291,14 +291,14 @@
 {
 	struct device *dev;
 
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if (dev->type == type &&
 		    memcmp(dev->dev_addr, ha, dev->addr_len) == 0)
 			goto out;
 	}
 out:
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 	return dev;
 }
 
@@ -321,7 +321,7 @@
 	}
 	return -ENFILE;	/* Over 100 of the things .. bail out! */
 }
- 
+
 struct device *dev_alloc(const char *name, int *err)
 {
 	struct device *dev=kmalloc(sizeof(struct device)+16, GFP_KERNEL);
@@ -387,9 +387,6 @@
 	if (dev->flags&IFF_UP)
 		return 0;
 
-	/* Setup the lock before we open the faucet. */
-	spin_lock_init(&dev->xmit_lock);
-
 	/*
 	 *	Call device private open method
 	 */
@@ -452,10 +449,10 @@
 	if (dev) {
 		dev_do_clear_fastroute(dev);
 	} else {
-		read_lock_bh(&dev_base_lock);
+		read_lock(&dev_base_lock);
 		for (dev = dev_base; dev; dev = dev->next)
 			dev_do_clear_fastroute(dev);
-		read_unlock_bh(&dev_base_lock);
+		read_unlock(&dev_base_lock);
 	}
 }
 #endif
@@ -596,59 +593,61 @@
 	struct device *dev = skb->dev;
 	struct Qdisc  *q;
 
-#ifdef CONFIG_NET_PROFILE
-	start_bh_atomic();
-	NET_PROFILE_ENTER(dev_queue_xmit);
-#endif
-
-	spin_lock_bh(&dev->xmit_lock);
+	/* Grab device queue */
+	spin_lock_bh(&dev->queue_lock);
 	q = dev->qdisc;
 	if (q->enqueue) {
 		q->enqueue(skb, q);
-		qdisc_wakeup(dev);
-		spin_unlock_bh(&dev->xmit_lock);
 
-#ifdef CONFIG_NET_PROFILE
-	        NET_PROFILE_LEAVE(dev_queue_xmit);
-		end_bh_atomic();
-#endif
+		/* If the device is not busy, kick it.
+		 * Otherwise or if queue is not empty after kick,
+		 * add it to run list.
+		 */
+		if (dev->tbusy || qdisc_restart(dev))
+			qdisc_run(dev->qdisc);
 
+		spin_unlock_bh(&dev->queue_lock);
 		return 0;
 	}
+	spin_unlock_bh(&dev->queue_lock);
 
 	/* The device has no queue. Common case for software devices:
 	   loopback, all the sorts of tunnels...
 
-	   Really, it is unlikely that bh protection is necessary here:
-	   virtual devices do not generate EOI events.
-	   However, it is possible, that they rely on bh protection
+	   Really, it is unlikely that xmit_lock protection is necessary here.
+	   (f.e. loopback and IP tunnels are clean ignoring statistics counters.)
+	   However, it is possible, that they rely on protection
 	   made by us here.
+
+	   Check this and shot the lock. It is not prone from deadlocks.
+	   Either shot noqueue qdisc, it is even simpler 8)
 	 */
 	if (dev->flags&IFF_UP) {
 		if (netdev_nit) 
 			dev_queue_xmit_nit(skb,dev);
-		if (dev->hard_start_xmit(skb, dev) == 0) {
-			spin_unlock_bh(&dev->xmit_lock);
-
-#ifdef CONFIG_NET_PROFILE
-			NET_PROFILE_LEAVE(dev_queue_xmit);
-			end_bh_atomic();
-#endif
 
-			return 0;
+		local_bh_disable();
+		if (dev->xmit_lock_owner != smp_processor_id()) {
+			spin_lock(&dev->xmit_lock);
+			dev->xmit_lock_owner = smp_processor_id();
+			if (dev->hard_start_xmit(skb, dev) == 0) {
+				dev->xmit_lock_owner = -1;
+				spin_unlock_bh(&dev->xmit_lock);
+				return 0;
+			}
+			dev->xmit_lock_owner = -1;
+			spin_unlock_bh(&dev->xmit_lock);
+			if (net_ratelimit())
+				printk(KERN_DEBUG "Virtual device %s asks to queue packet!\n", dev->name);
+		} else {
+			/* Recursion is detected! It is possible, unfortunately */
+			local_bh_enable();
+			if (net_ratelimit())
+				printk(KERN_DEBUG "Dead loop on virtual device %s, fix it urgently!\n", dev->name);
 		}
-		if (net_ratelimit())
-			printk(KERN_DEBUG "Virtual device %s asks to queue packet!\n", dev->name);
 	}
-	spin_unlock_bh(&dev->xmit_lock);
 
 	kfree_skb(skb);
-
-#ifdef CONFIG_NET_PROFILE
-	NET_PROFILE_LEAVE(dev_queue_xmit);
-	end_bh_atomic();
-#endif
-
 	return 0;
 }
 
@@ -660,9 +659,6 @@
 int netdev_dropping = 0;
 int netdev_max_backlog = 300;
 atomic_t netdev_rx_dropped;
-#ifdef CONFIG_CPU_IS_SLOW
-int net_cpu_congestion;
-#endif
 
 #ifdef CONFIG_NET_HW_FLOWCONTROL
 int netdev_throttle_events;
@@ -852,14 +848,6 @@
 	struct packet_type *pt_prev;
 	unsigned short type;
 	unsigned long start_time = jiffies;
-#ifdef CONFIG_CPU_IS_SLOW
-	static unsigned long start_busy = 0;
-	static unsigned long ave_busy = 0;
-
-	if (start_busy == 0)
-		start_busy = start_time;
-	net_cpu_congestion = ave_busy>>8;
-#endif
 
 	NET_PROFILE_ENTER(net_bh);
 	/*
@@ -869,9 +857,9 @@
 	 *	latency on a transmit interrupt bh.
 	 */
 
-	if (qdisc_head.forw != &qdisc_head)
+	if (qdisc_pending())
 		qdisc_run_queues();
-  
+
 	/*
 	 *	Any data left to process. This may occur because a
 	 *	mark_bh() is done after we empty the queue including
@@ -899,19 +887,6 @@
 		 */
 		skb = skb_dequeue(&backlog);
 
-#ifdef CONFIG_CPU_IS_SLOW
-		if (ave_busy > 128*16) {
-			kfree_skb(skb);
-			while ((skb = skb_dequeue(&backlog)) != NULL)
-				kfree_skb(skb);
-			break;
-		}
-#endif
-
-
-#if 0
-		NET_PROFILE_SKB_PASSED(skb, net_bh_skb);
-#endif
 #ifdef CONFIG_NET_FASTROUTE
 		if (skb->pkt_type == PACKET_FASTROUTE) {
 			dev_queue_xmit(skb);
@@ -1022,16 +997,9 @@
 	 *	One last output flush.
 	 */
 
-	if (qdisc_head.forw != &qdisc_head)
+	if (qdisc_pending())
 		qdisc_run_queues();
 
-#ifdef  CONFIG_CPU_IS_SLOW
-        if (1) {
-		unsigned long start_idle = jiffies;
-		ave_busy += ((start_idle - start_busy)<<3) - (ave_busy>>4);
-		start_busy = 0;
-	}
-#endif
 #ifdef CONFIG_NET_HW_FLOWCONTROL
 	if (netdev_dropping)
 		netdev_wakeup();
@@ -1065,14 +1033,6 @@
  */
 
 /*
- *	This call is useful, but I'd remove it too.
- *
- *	The reason is purely aestetical, it is the only call
- *	from SIOC* family using struct ifreq in reversed manner.
- *	Besides that, it is pretty silly to put "drawing" facility
- *	to kernel, it is useful only to print ifindices
- *	in readable form, is not it? --ANK
- *
  *	We need this ioctl for efficient implementation of the
  *	if_indextoname() function required by the IPv6 API.  Without
  *	it, we would have to search all the interfaces to find a
@@ -1138,7 +1098,7 @@
 	 */
 
 	total = 0;
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		for (i=0; i<NPROTO; i++) {
 			if (gifconf_list[i]) {
@@ -1152,7 +1112,7 @@
 			}
 		}
   	}
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 
 	if(pos != NULL) {
 		int err = copy_to_user(ifc.ifc_buf, pos, total);
@@ -1232,7 +1192,7 @@
 	len+=size;
 	
 
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		size = sprintf_stats(buffer+len, dev);
 		len+=size;
@@ -1245,7 +1205,7 @@
 		if(pos>offset+length)
 			break;
 	}
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 	
 	*start=buffer+(offset-begin);	/* Start of wanted data */
 	len-=(offset-begin);		/* Start slop */
@@ -1347,7 +1307,7 @@
 	pos+=size;
 	len+=size;
 
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for(dev = dev_base; dev != NULL; dev = dev->next) {
 		size = sprintf_wireless_stats(buffer+len, dev);
 		len+=size;
@@ -1360,7 +1320,7 @@
 		if(pos > offset + length)
 			break;
 	}
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 
 	*start = buffer + (offset - begin);	/* Start of wanted data */
 	len -= (offset - begin);		/* Start slop */
@@ -1736,11 +1696,10 @@
 				if (IW_IS_SET(cmd)) {
 					if (!suser())
 						return -EPERM;
-					rtnl_lock();
 				}
+				rtnl_lock();
 				ret = dev_ifsioc(&ifr, cmd);
-				if (IW_IS_SET(cmd))
-					rtnl_unlock();
+				rtnl_unlock();
 				if (!ret && IW_IS_GET(cmd) &&
 				    copy_to_user(arg, &ifr, sizeof(struct ifreq)))
 					return -EFAULT;
@@ -1769,6 +1728,10 @@
 {
 	struct device *d, **dp;
 
+	spin_lock_init(&dev->queue_lock);
+	spin_lock_init(&dev->xmit_lock);
+	dev->xmit_lock_owner = -1;
+
 	if (dev_boot_phase) {
 		/* This is NOT bug, but I am not sure, that all the
 		   devices, initialized before netdev module is started
@@ -1784,14 +1747,13 @@
 		printk(KERN_INFO "early initialization of device %s is deferred\n", dev->name);
 
 		/* Check for existence, and append to tail of chain */
-		write_lock_bh(&dev_base_lock);
 		for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {
 			if (d == dev || strcmp(d->name, dev->name) == 0) {
-				write_unlock_bh(&dev_base_lock);
 				return -EEXIST;
 			}
 		}
 		dev->next = NULL;
+		write_lock_bh(&dev_base_lock);
 		*dp = dev;
 		write_unlock_bh(&dev_base_lock);
 		return 0;
@@ -1803,24 +1765,22 @@
 	if (dev->init && dev->init(dev) != 0)
 		return -EIO;
 
+	dev->ifindex = dev_new_index();
+	if (dev->iflink == -1)
+		dev->iflink = dev->ifindex;
+
 	/* Check for existence, and append to tail of chain */
-	write_lock_bh(&dev_base_lock);
 	for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {
 		if (d == dev || strcmp(d->name, dev->name) == 0) {
-			write_unlock_bh(&dev_base_lock);
 			return -EEXIST;
 		}
 	}
 	dev->next = NULL;
 	dev_init_scheduler(dev);
+	write_lock_bh(&dev_base_lock);
 	*dp = dev;
 	write_unlock_bh(&dev_base_lock);
 
-	dev->ifindex = -1;
-	dev->ifindex = dev_new_index();
-	if (dev->iflink == -1)
-		dev->iflink = dev->ifindex;
-
 	/* Notify protocols, that a new device appeared. */
 	notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
 
@@ -1831,15 +1791,35 @@
 {
 	struct device *d, **dp;
 
-	if (dev_boot_phase == 0) {
-		/* If device is running, close it.
-		   It is very bad idea, really we should
-		   complain loudly here, but random hackery
-		   in linux/drivers/net likes it.
-		 */
-		if (dev->flags & IFF_UP)
-			dev_close(dev);
+	/* If device is running, close it first. */
+	if (dev->flags & IFF_UP)
+		dev_close(dev);
 
+	/* And unlink it from device chain. */
+	for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) {
+		if (d == dev) {
+			write_lock_bh(&dev_base_lock);
+			*dp = d->next;
+			write_unlock_bh(&dev_base_lock);
+
+			/* Sorry. It is known "feature". The race is clear.
+			   Keep it after device reference counting will
+			   be complete.
+			 */
+			synchronize_bh();
+			break;
+		}
+	}
+	if (d == NULL)
+		return -ENODEV;
+
+	/* It is "synchronize_bh" to those of guys, who overslept
+	   in skb_alloc/page fault etc. that device is off-line.
+	   Again, it can be removed only if devices are refcounted.
+	 */
+	dev_lock_wait();
+
+	if (dev_boot_phase == 0) {
 #ifdef CONFIG_NET_FASTROUTE
 		dev_clear_fastroute(dev);
 #endif
@@ -1856,27 +1836,11 @@
 		 *	Flush the multicast chain
 		 */
 		dev_mc_discard(dev);
-
-		/* To avoid pointers looking to nowhere,
-		   we wait for end of critical section */
-		dev_lock_wait();
 	}
 
-	/* And unlink it from device chain. */
-	write_lock_bh(&dev_base_lock);
-	for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) {
-		if (d == dev) {
-			*dp = d->next;
-			d->next = NULL;
-			write_unlock_bh(&dev_base_lock);
-
-			if (dev->destructor)
-				dev->destructor(dev);
-			return 0;
-		}
-	}
-	write_unlock_bh(&dev_base_lock);
-	return -ENODEV;
+	if (dev->destructor)
+		dev->destructor(dev);
+	return 0;
 }
 
 
@@ -2018,16 +1982,24 @@
 	 *	If the call to dev->init fails, the dev is removed
 	 *	from the chain disconnecting the device until the
 	 *	next reboot.
+	 *
+	 *	NB At boot phase networking is dead. No locking is required.
+	 *	But we still preserve dev_base_lock for sanity.
 	 */
 
 	dp = &dev_base;
 	while ((dev = *dp) != NULL) {
+		spin_lock_init(&dev->queue_lock);
+		spin_lock_init(&dev->xmit_lock);
+		dev->xmit_lock_owner = -1;
 		dev->iflink = -1;
 		if (dev->init && dev->init(dev)) {
 			/*
 			 *	It failed to come up. Unhook it.
 			 */
+			write_lock_bh(&dev_base_lock);
 			*dp = dev->next;
+			write_unlock_bh(&dev_base_lock);
 		} else {
 			dp = &dev->next;
 			dev->ifindex = dev_new_index();
@@ -2055,6 +2027,7 @@
 
 	dev_boot_phase = 0;
 
+	dst_init();
 	dev_mcast_init();
 
 #ifdef CONFIG_IP_PNP
diff -u --recursive --new-file v2.3.5/linux/net/core/dev_mcast.c linux/net/core/dev_mcast.c
--- v2.3.5/linux/net/core/dev_mcast.c	Mon May 31 22:28:07 1999
+++ linux/net/core/dev_mcast.c	Wed Jun  9 14:45:36 1999
@@ -58,7 +58,11 @@
  *
  *	Device mc lists are changed by bh at least if IPv6 is enabled,
  *	so that it must be bh protected.
+ *
+ *	We protect all mc lists with global rw lock
+ *	and block accesses to device mc filters with dev->xmit_lock.
  */
+static rwlock_t dev_mc_lock = RW_LOCK_UNLOCKED;
 
 /*
  *	Update the multicast list into the physical NIC controller.
@@ -69,7 +73,7 @@
 	/* Don't do anything till we up the interface
 	   [dev_open will call this function so the list will
 	    stay sane] */
-	    
+
 	if(!(dev->flags&IFF_UP))
 		return;
 
@@ -80,11 +84,15 @@
 	if(dev->set_multicast_list==NULL)
 		return;
 
-	start_bh_atomic();
+	read_lock_bh(&dev_mc_lock);
+	spin_lock(&dev->xmit_lock);
+	dev->xmit_lock_owner = smp_processor_id();
 	dev->set_multicast_list(dev);
-	end_bh_atomic();
+	dev->xmit_lock_owner = -1;
+	spin_unlock(&dev->xmit_lock);
+	read_unlock_bh(&dev_mc_lock);
 }
-  
+
 /*
  *	Delete a device level multicast
  */
@@ -94,7 +102,7 @@
 	int err = 0;
 	struct dev_mc_list *dmi, **dmip;
 
-	start_bh_atomic();
+	write_lock_bh(&dev_mc_lock);
 	for (dmip=&dev->mc_list; (dmi=*dmip)!=NULL; dmip=&dmi->next) {
 		/*
 		 *	Find the entry we want to delete. The device could
@@ -120,14 +128,15 @@
 			 *	We have altered the list, so the card
 			 *	loaded filter is now wrong. Fix it
 			 */
-			end_bh_atomic();
+			write_unlock_bh(&dev_mc_lock);
+
 			dev_mc_upload(dev);
 			return 0;
 		}
 	}
 	err = -ENOENT;
 done:
-	end_bh_atomic();
+	write_unlock_bh(&dev_mc_lock);
 	return err;
 }
 
@@ -140,9 +149,12 @@
 	int err = 0;
 	struct dev_mc_list *dmi, *dmi1;
 
+	/* RED-PEN: does gfp_any() work now? It requires
+	   true local_bh_disable rather than global.
+	 */
 	dmi1 = (struct dev_mc_list *)kmalloc(sizeof(*dmi), gfp_any());
 
-	start_bh_atomic();
+	write_lock_bh(&dev_mc_lock);
 	for(dmi=dev->mc_list; dmi!=NULL; dmi=dmi->next) {
 		if (memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen) {
 			if (glbl) {
@@ -156,8 +168,10 @@
 		}
 	}
 
-	if ((dmi=dmi1)==NULL)
+	if ((dmi=dmi1)==NULL) {
+		write_unlock_bh(&dev_mc_lock);
 		return -ENOMEM;
+	}
 	memcpy(dmi->dmi_addr, addr, alen);
 	dmi->dmi_addrlen=alen;
 	dmi->next=dev->mc_list;
@@ -165,12 +179,12 @@
 	dmi->dmi_gusers=glbl ? 1 : 0;
 	dev->mc_list=dmi;
 	dev->mc_count++;
-	end_bh_atomic();
+	write_unlock_bh(&dev_mc_lock);
 	dev_mc_upload(dev);
 	return 0;
 
 done:
-	end_bh_atomic();
+	write_unlock_bh(&dev_mc_lock);
 	if (dmi1)
 		kfree(dmi1);
 	return err;
@@ -182,7 +196,7 @@
 
 void dev_mc_discard(struct device *dev)
 {
-	start_bh_atomic();
+	write_lock_bh(&dev_mc_lock);
 	while (dev->mc_list!=NULL) {
 		struct dev_mc_list *tmp=dev->mc_list;
 		dev->mc_list=tmp->next;
@@ -191,7 +205,7 @@
 		kfree_s(tmp,sizeof(*tmp));
 	}
 	dev->mc_count=0;
-	end_bh_atomic();
+	write_unlock_bh(&dev_mc_lock);
 }
 
 #ifdef CONFIG_PROC_FS
@@ -203,8 +217,9 @@
 	int len=0;
 	struct device *dev;
 
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev; dev = dev->next) {
+		read_lock_bh(&dev_mc_lock);
 		for (m = dev->mc_list; m; m = m->next) {
 			int i;
 
@@ -221,14 +236,17 @@
 				len=0;
 				begin=pos;
 			}
-			if (pos > offset+length)
+			if (pos > offset+length) {
+				read_unlock_bh(&dev_mc_lock);
 				goto done;
+			}
 		}
+		read_unlock_bh(&dev_mc_lock);
 	}
 	*eof = 1;
 
 done:
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 	*start=buffer+(offset-begin);
 	len-=(offset-begin);
 	if(len>length)
diff -u --recursive --new-file v2.3.5/linux/net/core/dst.c linux/net/core/dst.c
--- v2.3.5/linux/net/core/dst.c	Mon May 31 22:28:07 1999
+++ linux/net/core/dst.c	Wed Jun  9 14:45:36 1999
@@ -16,6 +16,7 @@
 #include <linux/errno.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
+#include <linux/init.h>
 
 #include <net/dst.h>
 
@@ -39,16 +40,16 @@
 static struct timer_list dst_gc_timer =
 	{ NULL, NULL, DST_GC_MIN, 0L, dst_run_gc };
 
-#if RT_CACHE_DEBUG >= 2
-atomic_t hh_count;
-#endif
 
 static void dst_run_gc(unsigned long dummy)
 {
 	int    delayed = 0;
 	struct dst_entry * dst, **dstp;
 
-	spin_lock(&dst_lock);
+	if (!spin_trylock(&dst_lock)) {
+		mod_timer(&dst_gc_timer, jiffies + HZ/10);
+		return;
+	}
 
 	del_timer(&dst_gc_timer);
 	dstp = &dst_garbage_list;
@@ -159,4 +160,37 @@
 		dst->ops->destroy(dst);
 	atomic_dec(&dst_total);
 	kfree(dst);
+}
+
+static int dst_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+	struct device *dev = ptr;
+	struct dst_entry *dst;
+
+	switch (event) {
+	case NETDEV_UNREGISTER:
+	case NETDEV_DOWN:
+		spin_lock_bh(&dst_lock);
+		for (dst = dst_garbage_list; dst; dst = dst->next) {
+			if (dst->dev == dev) {
+				dst->input = dst_discard;
+				dst->output = dst_blackhole;
+				dst->dev = &loopback_dev;
+			}
+		}
+		spin_unlock_bh(&dst_lock);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+struct notifier_block dst_dev_notifier = {
+	dst_dev_event,
+	NULL,
+	0
+};
+
+__initfunc(void dst_init(void))
+{
+	register_netdevice_notifier(&dst_dev_notifier);
 }
diff -u --recursive --new-file v2.3.5/linux/net/core/neighbour.c linux/net/core/neighbour.c
--- v2.3.5/linux/net/core/neighbour.c	Wed Jun  2 14:44:39 1999
+++ linux/net/core/neighbour.c	Wed Jun  9 14:45:36 1999
@@ -12,12 +12,10 @@
  *
  *	Fixes:
  *	Vitaly E. Lavrov	releasing NULL neighbor in neigh_add.
- *      Horst von Brand        Add #include <linux/string.h>
  */
 
 #include <linux/config.h>
 #include <linux/types.h>
-#include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/socket.h>
 #include <linux/sched.h>
@@ -30,33 +28,6 @@
 #include <net/sock.h>
 #include <linux/rtnetlink.h>
 
-/*
-   NOTE. The most unpleasent question is serialization of
-   accesses to resolved addresses. The problem is that addresses
-   are modified by bh, but they are referenced from normal
-   kernel thread. Before today no locking was made.
-   My reasoning was that corrupted address token will be copied
-   to packet with cosmologically small probability
-   (it is even difficult to estimate such small number)
-   and it is very silly to waste cycles in fast path to lock them.
-
-   But now I changed my mind, but not because previous statement
-   is wrong. Actually, neigh->ha MAY BE not opaque byte array,
-   but reference to some private data. In this case even neglibible
-   corruption probability becomes bug.
-
-   - hh cache is protected by rwlock. It assumes that
-     hh cache update procedure is short and fast, and that
-     read_lock is cheaper than start_bh_atomic().
-   - ha tokens, saved in neighbour entries, are protected
-     by bh_atomic().
-   - no protection is made in /proc reading. It is OK, because
-     /proc is broken by design in any case, and
-     corrupted output is normal behaviour there.
-
-     --ANK (981025)
- */
-
 #define NEIGH_DEBUG 1
 
 #define NEIGH_PRINTK(x...) printk(x)
@@ -83,6 +54,46 @@
 static int neigh_glbl_allocs;
 static struct neigh_table *neigh_tables;
 
+#if defined(__i386__) && defined(__SMP__)
+#define ASSERT_WL(n) if ((int)((n)->lock.lock) >= 0) { printk("WL assertion failed at " __FILE__ "(%d):" __FUNCTION__ "\n", __LINE__); }
+#else
+#define ASSERT_WL(n) do { } while(0)
+#endif
+
+/*
+   Neighbour hash table buckets are protected with rwlock tbl->lock.
+
+   - All the scans/updates to hash buckets MUST be made under this lock.
+   - NOTHING clever should be made under this lock: no callbacks
+     to protocol backends, no attempts to send something to network.
+     It will result in deadlocks, if backend/driver wants to use neighbour
+     cache.
+   - If the entry requires some non-trivial actions, increase
+     its reference count and release table lock.
+ 
+   Neighbour entries are protected:
+   - with reference count.
+   - with rwlock neigh->lock
+
+   Reference count prevents destruction.
+
+   neigh->lock mainly serializes ll address data and its validity state.
+   However, the same lock is used to protect another entry fields:
+    - timer
+    - resolution queue
+
+   Again, nothing clever shall be made under neigh->lock,
+   the most complicated procedure, which we allow is dev->hard_header.
+   It is supposed, that dev->hard_header is simplistic and does
+   not make callbacks to neighbour tables.
+
+   The last lock is neigh_tbl_lock. It is pure SMP lock, protecting
+   list of neighbour tables. This list is used only in process context,
+   so that this lock is useless with big kernel lock.
+ */
+
+static rwlock_t neigh_tbl_lock = RW_LOCK_UNLOCKED;
+
 static int neigh_blackhole(struct sk_buff *skb)
 {
 	kfree_skb(skb);
@@ -106,13 +117,11 @@
 	int shrunk = 0;
 	int i;
 
-	if (atomic_read(&tbl->lock))
-		return 0;
-
 	for (i=0; i<=NEIGH_HASHMASK; i++) {
 		struct neighbour *n, **np;
 
 		np = &tbl->hash_buckets[i];
+		write_lock_bh(&tbl->lock);
 		while ((n = *np) != NULL) {
 			/* Neighbour record may be discarded if:
 			   - nobody refers to it.
@@ -124,6 +133,7 @@
 			     It is not clear, what is better table overflow
 			     or flooding.
 			 */
+			write_lock(&n->lock);
 			if (atomic_read(&n->refcnt) == 0 &&
 			    !(n->nud_state&NUD_PERMANENT) &&
 			    (n->nud_state != NUD_INCOMPLETE ||
@@ -132,11 +142,14 @@
 				n->tbl = NULL;
 				tbl->entries--;
 				shrunk = 1;
+				write_unlock(&n->lock);
 				neigh_destroy(n);
 				continue;
 			}
+			write_unlock(&n->lock);
 			np = &n->next;
 		}
+		write_unlock_bh(&tbl->lock);
 	}
 	
 	tbl->last_flush = jiffies;
@@ -147,12 +160,8 @@
 {
 	int i;
 
-	if (atomic_read(&tbl->lock)) {
-		NEIGH_PRINTK1("neigh_ifdown: impossible event 1763\n");
-		return -EBUSY;
-	}
+	write_lock_bh(&tbl->lock);
 
-	start_bh_atomic();
 	for (i=0; i<=NEIGH_HASHMASK; i++) {
 		struct neighbour *n, **np;
 
@@ -163,6 +172,7 @@
 				continue;
 			}
 			*np = n->next;
+			write_lock(&n->lock);
 			n->tbl = NULL;
 			tbl->entries--;
 			if (atomic_read(&n->refcnt)) {
@@ -185,33 +195,32 @@
 				else
 					n->nud_state = NUD_NONE;
 				NEIGH_PRINTK2("neigh %p is stray.\n", n);
-			} else
+				write_unlock(&n->lock);
+			} else {
+				write_unlock(&n->lock);
 				neigh_destroy(n);
+			}
 		}
 	}
 
 	del_timer(&tbl->proxy_timer);
 	skb_queue_purge(&tbl->proxy_queue);
 	pneigh_ifdown(tbl, dev);
-	end_bh_atomic();
+	write_unlock_bh(&tbl->lock);
 	return 0;
 }
 
-static struct neighbour *neigh_alloc(struct neigh_table *tbl, int creat)
+static struct neighbour *neigh_alloc(struct neigh_table *tbl)
 {
 	struct neighbour *n;
 	unsigned long now = jiffies;
 
-	if (tbl->entries > tbl->gc_thresh1) {
-		if (creat < 0)
+	if (tbl->entries > tbl->gc_thresh3 ||
+	    (tbl->entries > tbl->gc_thresh2 &&
+	     now - tbl->last_flush > 5*HZ)) {
+		if (neigh_forced_gc(tbl) == 0 &&
+		    tbl->entries > tbl->gc_thresh3)
 			return NULL;
-		if (tbl->entries > tbl->gc_thresh3 ||
-		    (tbl->entries > tbl->gc_thresh2 &&
-		     now - tbl->last_flush > 5*HZ)) {
-			if (neigh_forced_gc(tbl) == 0 &&
-			    tbl->entries > tbl->gc_thresh3)
-				return NULL;
-		}
 	}
 
 	n = kmalloc(tbl->entry_size, GFP_ATOMIC);
@@ -221,6 +230,7 @@
 	memset(n, 0, tbl->entry_size);
 
 	skb_queue_head_init(&n->arp_queue);
+	n->lock = RW_LOCK_UNLOCKED;
 	n->updated = n->used = now;
 	n->nud_state = NUD_NONE;
 	n->output = neigh_blackhole;
@@ -233,9 +243,8 @@
 	return n;
 }
 
-
-struct neighbour * __neigh_lookup(struct neigh_table *tbl, const void *pkey,
-				    struct device *dev, int creat)
+struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
+			       struct device *dev)
 {
 	struct neighbour *n;
 	u32 hash_val;
@@ -247,17 +256,26 @@
 	hash_val ^= hash_val>>3;
 	hash_val = (hash_val^dev->ifindex)&NEIGH_HASHMASK;
 
+	read_lock_bh(&tbl->lock);
 	for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
 		if (dev == n->dev &&
 		    memcmp(n->primary_key, pkey, key_len) == 0) {
 			atomic_inc(&n->refcnt);
-			return n;
+			break;
 		}
 	}
-	if (!creat)
-		return NULL;
+	read_unlock_bh(&tbl->lock);
+	return n;
+}
 
-	n = neigh_alloc(tbl, creat);
+struct neighbour * neigh_create(struct neigh_table *tbl, const void *pkey,
+				struct device *dev)
+{
+	struct neighbour *n, *n1;
+	u32 hash_val;
+	int key_len = tbl->key_len;
+
+	n = neigh_alloc(tbl);
 	if (n == NULL)
 		return NULL;
 
@@ -277,11 +295,30 @@
 	}
 
 	n->confirmed = jiffies - (n->parms->base_reachable_time<<1);
-	atomic_set(&n->refcnt, 1);
+
+	hash_val = *(u32*)(pkey + key_len - 4);
+	hash_val ^= (hash_val>>16);
+	hash_val ^= hash_val>>8;
+	hash_val ^= hash_val>>3;
+	hash_val = (hash_val^dev->ifindex)&NEIGH_HASHMASK;
+
+	write_lock_bh(&tbl->lock);
+	for (n1 = tbl->hash_buckets[hash_val]; n1; n1 = n1->next) {
+		if (dev == n1->dev &&
+		    memcmp(n1->primary_key, pkey, key_len) == 0) {
+			atomic_inc(&n1->refcnt);
+			write_unlock_bh(&tbl->lock);
+			neigh_destroy(n);
+			return n1;
+		}
+	}
+
 	tbl->entries++;
+	n->tbl = tbl;
+	atomic_set(&n->refcnt, 1);
 	n->next = tbl->hash_buckets[hash_val];
 	tbl->hash_buckets[hash_val] = n;
-	n->tbl = tbl;
+	write_unlock_bh(&tbl->lock);
 	NEIGH_PRINTK2("neigh %p is created.\n", n);
 	return n;
 }
@@ -393,7 +430,9 @@
 	while ((hh = neigh->hh) != NULL) {
 		neigh->hh = hh->hh_next;
 		hh->hh_next = NULL;
+		write_lock_bh(&hh->hh_lock);
 		hh->hh_output = neigh_blackhole;
+		write_unlock_bh(&hh->hh_lock);
 		if (atomic_dec_and_test(&hh->hh_refcnt))
 			kfree(hh);
 	}
@@ -411,6 +450,8 @@
 
 /* Neighbour state is suspicious;
    disable fast path.
+
+   Called with write_locked neigh.
  */
 static void neigh_suspect(struct neighbour *neigh)
 {
@@ -418,6 +459,8 @@
 
 	NEIGH_PRINTK2("neigh %p is suspecteded.\n", neigh);
 
+	ASSERT_WL(neigh);
+
 	neigh->output = neigh->ops->output;
 
 	for (hh = neigh->hh; hh; hh = hh->hh_next)
@@ -426,6 +469,8 @@
 
 /* Neighbour state is OK;
    enable fast path.
+
+   Called with write_locked neigh.
  */
 static void neigh_connect(struct neighbour *neigh)
 {
@@ -433,6 +478,8 @@
 
 	NEIGH_PRINTK2("neigh %p is connected.\n", neigh);
 
+	ASSERT_WL(neigh);
+
 	neigh->output = neigh->ops->connected_output;
 
 	for (hh = neigh->hh; hh; hh = hh->hh_next)
@@ -448,6 +495,8 @@
 
    If a routine wants to know TRUE entry state, it calls
    neigh_sync before checking state.
+
+   Called with write_locked neigh.
  */
 
 static void neigh_sync(struct neighbour *n)
@@ -455,6 +504,7 @@
 	unsigned long now = jiffies;
 	u8 state = n->nud_state;
 
+	ASSERT_WL(n);
 	if (state&(NUD_NOARP|NUD_PERMANENT))
 		return;
 	if (state&NUD_REACHABLE) {
@@ -478,11 +528,8 @@
 	unsigned long now = jiffies;
 	int i;
 
-	if (atomic_read(&tbl->lock)) {
-		tbl->gc_timer.expires = now + 1*HZ;
-		add_timer(&tbl->gc_timer);
-		return;
-	}
+
+	write_lock(&tbl->lock);
 
 	/*
 	 *	periodicly recompute ReachableTime from random function
@@ -500,10 +547,15 @@
 
 		np = &tbl->hash_buckets[i];
 		while ((n = *np) != NULL) {
-			unsigned state = n->nud_state;
+			unsigned state;
+
+			write_lock(&n->lock);
 
-			if (state&(NUD_PERMANENT|NUD_IN_TIMER))
+			state = n->nud_state;
+			if (state&(NUD_PERMANENT|NUD_IN_TIMER)) {
+				write_unlock(&n->lock);
 				goto next_elt;
+			}
 
 			if ((long)(n->used - n->confirmed) < 0)
 				n->used = n->confirmed;
@@ -514,6 +566,7 @@
 				n->tbl = NULL;
 				n->next = NULL;
 				tbl->entries--;
+				write_unlock(&n->lock);
 				neigh_destroy(n);
 				continue;
 			}
@@ -523,6 +576,7 @@
 				n->nud_state = NUD_STALE;
 				neigh_suspect(n);
 			}
+			write_unlock(&n->lock);
 
 next_elt:
 			np = &n->next;
@@ -531,6 +585,7 @@
 
 	tbl->gc_timer.expires = now + tbl->gc_interval;
 	add_timer(&tbl->gc_timer);
+	write_unlock(&tbl->lock);
 }
 
 static __inline__ int neigh_max_probes(struct neighbour *n)
@@ -546,11 +601,17 @@
 {
 	unsigned long now = jiffies;
 	struct neighbour *neigh = (struct neighbour*)arg;
-	unsigned state = neigh->nud_state;
+	unsigned state;
+	int notify = 0;
+
+	write_lock(&neigh->lock);
+	atomic_inc(&neigh->refcnt);
+
+	state = neigh->nud_state;
 
 	if (!(state&NUD_IN_TIMER)) {
 		NEIGH_PRINTK1("neigh: timer & !nud_in_timer\n");
-		return;
+		goto out;
 	}
 
 	if ((state&NUD_VALID) &&
@@ -558,18 +619,19 @@
 		neigh->nud_state = NUD_REACHABLE;
 		NEIGH_PRINTK2("neigh %p is still alive.\n", neigh);
 		neigh_connect(neigh);
-		return;
+		goto out;
 	}
 	if (state == NUD_DELAY) {
 		NEIGH_PRINTK2("neigh %p is probed.\n", neigh);
 		neigh->nud_state = NUD_PROBE;
-		neigh->probes = 0;
+		atomic_set(&neigh->probes, 0);
 	}
 
-	if (neigh->probes >= neigh_max_probes(neigh)) {
+	if (atomic_read(&neigh->probes) >= neigh_max_probes(neigh)) {
 		struct sk_buff *skb;
 
 		neigh->nud_state = NUD_FAILED;
+		notify = 1;
 		neigh->tbl->stats.res_failed++;
 		NEIGH_PRINTK2("neigh %p is failed.\n", neigh);
 
@@ -578,44 +640,60 @@
 		   
 		   So that, we try to be accurate and avoid dead loop. --ANK
 		 */
-		while(neigh->nud_state==NUD_FAILED && (skb=__skb_dequeue(&neigh->arp_queue)) != NULL)
+		while(neigh->nud_state==NUD_FAILED && (skb=__skb_dequeue(&neigh->arp_queue)) != NULL) {
+			write_unlock(&neigh->lock);
 			neigh->ops->error_report(neigh, skb);
+			write_lock(&neigh->lock);
+		}
 		skb_queue_purge(&neigh->arp_queue);
-		return;
+		goto out;
 	}
 
 	neigh->timer.expires = now + neigh->parms->retrans_time;
 	add_timer(&neigh->timer);
+	write_unlock(&neigh->lock);
 
 	neigh->ops->solicit(neigh, skb_peek(&neigh->arp_queue));
-	neigh->probes++;
+	atomic_inc(&neigh->probes);
+	neigh_release(neigh);
+	return;
+
+out:
+	write_unlock(&neigh->lock);
+#ifdef CONFIG_ARPD
+	if (notify && neigh->parms->app_probes)
+		neigh_app_notify(neigh);
+#endif
+	neigh_release(neigh);
 }
 
 int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
 {
-	start_bh_atomic();
+	write_lock_bh(&neigh->lock);
 	if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE))) {
 		if (!(neigh->nud_state&(NUD_STALE|NUD_INCOMPLETE))) {
 			if (neigh->tbl == NULL) {
 				NEIGH_PRINTK2("neigh %p used after death.\n", neigh);
 				if (skb)
 					kfree_skb(skb);
-				end_bh_atomic();
+				write_unlock_bh(&neigh->lock);
 				return 1;
 			}
 			if (neigh->parms->mcast_probes + neigh->parms->app_probes) {
-				neigh->probes = neigh->parms->ucast_probes;
+				atomic_set(&neigh->probes, neigh->parms->ucast_probes);
 				neigh->nud_state = NUD_INCOMPLETE;
 				neigh->timer.expires = jiffies + neigh->parms->retrans_time;
 				add_timer(&neigh->timer);
-
+				write_unlock_bh(&neigh->lock);
 				neigh->ops->solicit(neigh, skb);
-				neigh->probes++;
+				atomic_inc(&neigh->probes);
+				write_lock_bh(&neigh->lock);
 			} else {
 				neigh->nud_state = NUD_FAILED;
+				write_unlock_bh(&neigh->lock);
+
 				if (skb)
 					kfree_skb(skb);
-				end_bh_atomic();
 				return 1;
 			}
 		}
@@ -629,7 +707,7 @@
 				}
 				__skb_queue_head(&neigh->arp_queue, skb);
 			}
-			end_bh_atomic();
+			write_unlock_bh(&neigh->lock);
 			return 1;
 		}
 		if (neigh->nud_state == NUD_STALE) {
@@ -639,7 +717,7 @@
 			add_timer(&neigh->timer);
 		}
 	}
-	end_bh_atomic();
+	write_unlock_bh(&neigh->lock);
 	return 0;
 }
 
@@ -651,9 +729,9 @@
 
 	if (update) {
 		for (hh=neigh->hh; hh; hh=hh->hh_next) {
-			write_lock_irq(&hh->hh_lock);
+			write_lock_bh(&hh->hh_lock);
 			update(hh, neigh->dev, neigh->ha);
-			write_unlock_irq(&hh->hh_lock);
+			write_unlock_bh(&hh->hh_lock);
 		}
 	}
 }
@@ -665,15 +743,23 @@
    -- new    is new state.
    -- override==1 allows to override existing lladdr, if it is different.
    -- arp==0 means that the change is administrative.
+
+   Caller MUST hold reference count on the entry.
  */
 
 int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int arp)
 {
-	u8 old = neigh->nud_state;
+	u8 old;
+	int err;
+	int notify = 0;
 	struct device *dev = neigh->dev;
 
+	write_lock_bh(&neigh->lock);
+	old = neigh->nud_state;
+
+	err = -EPERM;
 	if (arp && (old&(NUD_NOARP|NUD_PERMANENT)))
-		return -EPERM;
+		goto out;
 
 	if (!(new&NUD_VALID)) {
 		if (old&NUD_IN_TIMER)
@@ -681,7 +767,9 @@
 		if (old&NUD_CONNECTED)
 			neigh_suspect(neigh);
 		neigh->nud_state = new;
-		return 0;
+		err = 0;
+		notify = old&NUD_VALID;
+		goto out;
 	}
 
 	/* Compare new lladdr with cached one */
@@ -698,14 +786,15 @@
 			if (memcmp(lladdr, neigh->ha, dev->addr_len) == 0)
 				lladdr = neigh->ha;
 			else if (!override)
-				return -EPERM;
+				goto out;
 		}
 	} else {
 		/* No address is supplied; if we know something,
 		   use it, otherwise discard the request.
 		 */
+		err = -EINVAL;
 		if (!(old&NUD_VALID))
-			return -EINVAL;
+			goto out;
 		lladdr = neigh->ha;
 	}
 
@@ -718,10 +807,11 @@
 	/* If entry was valid and address is not changed,
 	   do not change entry state, if new one is STALE.
 	 */
+	err = 0;
 	if (old&NUD_VALID) {
 		if (lladdr == neigh->ha)
 			if (new == old || (new == NUD_STALE && (old&NUD_CONNECTED)))
-				return 0;
+				goto out;
 	}
 	if (old&NUD_IN_TIMER)
 		del_timer(&neigh->timer);
@@ -731,12 +821,11 @@
 		neigh_update_hhs(neigh);
 		neigh->confirmed = jiffies - (neigh->parms->base_reachable_time<<1);
 #ifdef CONFIG_ARPD
-		if (neigh->parms->app_probes)
-			neigh_app_notify(neigh);
+		notify = 1;
 #endif
 	}
 	if (new == old)
-		return 0;
+		goto out;
 	if (new&NUD_CONNECTED)
 		neigh_connect(neigh);
 	else
@@ -749,14 +838,22 @@
 		while (neigh->nud_state&NUD_VALID &&
 		       (skb=__skb_dequeue(&neigh->arp_queue)) != NULL) {
 			struct neighbour *n1 = neigh;
+			write_unlock_bh(&neigh->lock);
 			/* On shaper/eql skb->dst->neighbour != neigh :( */
 			if (skb->dst && skb->dst->neighbour)
 				n1 = skb->dst->neighbour;
 			n1->output(skb);
+			write_lock_bh(&neigh->lock);
 		}
 		skb_queue_purge(&neigh->arp_queue);
 	}
-	return 0;
+out:
+	write_unlock_bh(&neigh->lock);
+#ifdef CONFIG_ARPD
+	if (notify && neigh->parms->app_probes)
+		neigh_app_notify(neigh);
+#endif
+	return err;
 }
 
 struct neighbour * neigh_event_ns(struct neigh_table *tbl,
@@ -839,15 +936,15 @@
 		int err;
 		struct device *dev = neigh->dev;
 		if (dev->hard_header_cache && dst->hh == NULL) {
-			start_bh_atomic();
+			write_lock_bh(&neigh->lock);
 			if (dst->hh == NULL)
 				neigh_hh_init(neigh, dst, dst->ops->protocol);
 			err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
-			end_bh_atomic();
+			write_unlock_bh(&neigh->lock);
 		} else {
-			start_bh_atomic();
+			read_lock_bh(&neigh->lock);
 			err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
-			end_bh_atomic();
+			read_unlock_bh(&neigh->lock);
 		}
 		if (err >= 0)
 			return neigh->ops->queue_xmit(skb);
@@ -873,9 +970,9 @@
 
 	__skb_pull(skb, skb->nh.raw - skb->data);
 
-	start_bh_atomic();
+	read_lock_bh(&neigh->lock);
 	err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
-	end_bh_atomic();
+	read_unlock_bh(&neigh->lock);
 	if (err >= 0)
 		return neigh->ops->queue_xmit(skb);
 	kfree_skb(skb);
@@ -949,8 +1046,10 @@
 				return NULL;
 			}
 		}
+		write_lock_bh(&tbl->lock);
 		p->next = tbl->parms.next;
 		tbl->parms.next = p;
+		write_unlock_bh(&tbl->lock);
 	}
 	return p;
 }
@@ -961,10 +1060,11 @@
 	
 	if (parms == NULL || parms == &tbl->parms)
 		return;
+	write_lock_bh(&tbl->lock);
 	for (p = &tbl->parms.next; *p; p = &(*p)->next) {
 		if (*p == parms) {
 			*p = parms->next;
-			synchronize_bh();
+			write_unlock_bh(&tbl->lock);
 #ifdef CONFIG_SYSCTL
 			neigh_sysctl_unregister(parms);
 #endif
@@ -972,6 +1072,7 @@
 			return;
 		}
 	}
+	write_unlock_bh(&tbl->lock);
 	NEIGH_PRINTK1("neigh_release_parms: not found\n");
 }
 
@@ -983,6 +1084,7 @@
 	tbl->parms.reachable_time = neigh_rand_reach_time(tbl->parms.base_reachable_time);
 
 	init_timer(&tbl->gc_timer);
+	tbl->lock = RW_LOCK_UNLOCKED;
 	tbl->gc_timer.data = (unsigned long)tbl;
 	tbl->gc_timer.function = neigh_periodic_timer;
 	tbl->gc_timer.expires = now + tbl->gc_interval + tbl->parms.reachable_time;
@@ -995,29 +1097,30 @@
 
 	tbl->last_flush = now;
 	tbl->last_rand = now + tbl->parms.reachable_time*20;
+	write_lock(&neigh_tbl_lock);
 	tbl->next = neigh_tables;
 	neigh_tables = tbl;
+	write_unlock(&neigh_tbl_lock);
 }
 
 int neigh_table_clear(struct neigh_table *tbl)
 {
 	struct neigh_table **tp;
 
-	start_bh_atomic();
 	del_timer(&tbl->gc_timer);
 	del_timer(&tbl->proxy_timer);
 	skb_queue_purge(&tbl->proxy_queue);
 	neigh_ifdown(tbl, NULL);
-	end_bh_atomic();
 	if (tbl->entries)
 		printk(KERN_CRIT "neighbour leakage\n");
+	write_lock(&neigh_tbl_lock);
 	for (tp = &neigh_tables; *tp; tp = &(*tp)->next) {
 		if (*tp == tbl) {
 			*tp = tbl->next;
-			synchronize_bh();
 			break;
 		}
 	}
+	write_unlock(&neigh_tbl_lock);
 #ifdef CONFIG_SYSCTL
 	neigh_sysctl_unregister(&tbl->parms);
 #endif
@@ -1039,12 +1142,14 @@
 			return -ENODEV;
 	}
 
+	read_lock(&neigh_tbl_lock);
 	for (tbl=neigh_tables; tbl; tbl = tbl->next) {
 		int err = 0;
 		struct neighbour *n;
 
 		if (tbl->family != ndm->ndm_family)
 			continue;
+		read_unlock(&neigh_tbl_lock);
 
 		if (nda[NDA_DST-1] == NULL ||
 		    nda[NDA_DST-1]->rta_len != RTA_LENGTH(tbl->key_len))
@@ -1056,15 +1161,14 @@
 		if (dev == NULL)
 			return -EINVAL;
 
-		start_bh_atomic();
-		n = __neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 0);
+		n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev);
 		if (n) {
 			err = neigh_update(n, NULL, NUD_FAILED, 1, 0);
 			neigh_release(n);
 		}
-		end_bh_atomic();
 		return err;
 	}
+	read_unlock(&neigh_tbl_lock);
 
 	return -EADDRNOTAVAIL;
 }
@@ -1081,12 +1185,15 @@
 			return -ENODEV;
 	}
 
+	read_lock(&neigh_tbl_lock);
 	for (tbl=neigh_tables; tbl; tbl = tbl->next) {
 		int err = 0;
 		struct neighbour *n;
 
 		if (tbl->family != ndm->ndm_family)
 			continue;
+		read_unlock(&neigh_tbl_lock);
+
 		if (nda[NDA_DST-1] == NULL ||
 		    nda[NDA_DST-1]->rta_len != RTA_LENGTH(tbl->key_len))
 			return -EINVAL;
@@ -1100,8 +1207,7 @@
 		if (nda[NDA_LLADDR-1] != NULL &&
 		    nda[NDA_LLADDR-1]->rta_len != RTA_LENGTH(dev->addr_len))
 			return -EINVAL;
-		start_bh_atomic();
-		n = __neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 0);
+		n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev);
 		if (n) {
 			if (nlh->nlmsg_flags&NLM_F_EXCL)
 				err = -EEXIST;
@@ -1119,9 +1225,9 @@
 		}
 		if (n)
 			neigh_release(n);
-		end_bh_atomic();
 		return err;
 	}
+	read_unlock(&neigh_tbl_lock);
 
 	return -EADDRNOTAVAIL;
 }
@@ -1141,15 +1247,17 @@
 	ndm->ndm_family = n->ops->family;
 	ndm->ndm_flags = n->flags;
 	ndm->ndm_type = n->type;
-	ndm->ndm_state = n->nud_state;
 	ndm->ndm_ifindex = n->dev->ifindex;
 	RTA_PUT(skb, NDA_DST, n->tbl->key_len, n->primary_key);
+	read_lock_bh(&n->lock);
+	ndm->ndm_state = n->nud_state;
 	if (n->nud_state&NUD_VALID)
 		RTA_PUT(skb, NDA_LLADDR, n->dev->addr_len, n->ha);
 	ci.ndm_used = now - n->used;
 	ci.ndm_confirmed = now - n->confirmed;
 	ci.ndm_updated = now - n->updated;
 	ci.ndm_refcnt = atomic_read(&n->refcnt);
+	read_unlock_bh(&n->lock);
 	RTA_PUT(skb, NDA_CACHEINFO, sizeof(ci), &ci);
 	nlh->nlmsg_len = skb->tail - b;
 	return skb->len;
@@ -1173,20 +1281,20 @@
 		if (h < s_h) continue;
 		if (h > s_h)
 			s_idx = 0;
-		start_bh_atomic();
+		read_lock_bh(&tbl->lock);
 		for (n = tbl->hash_buckets[h], idx = 0; n;
 		     n = n->next, idx++) {
 			if (idx < s_idx)
 				continue;
 			if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid,
 					    cb->nlh->nlmsg_seq, RTM_NEWNEIGH) <= 0) {
-				end_bh_atomic();
+				read_unlock_bh(&tbl->lock);
 				cb->args[1] = h;
 				cb->args[2] = idx;
 				return -1;
 			}
 		}
-		end_bh_atomic();
+		read_unlock_bh(&tbl->lock);
 	}
 
 	cb->args[1] = h;
@@ -1203,6 +1311,7 @@
 
 	s_t = cb->args[0];
 
+	read_lock(&neigh_tbl_lock);
 	for (tbl=neigh_tables, t=0; tbl; tbl = tbl->next, t++) {
 		if (t < s_t) continue;
 		if (family && tbl->family != family)
@@ -1212,6 +1321,7 @@
 		if (neigh_dump_table(tbl, skb, cb) < 0) 
 			break;
 	}
+	read_unlock(&neigh_tbl_lock);
 
 	cb->args[0] = t;
 
diff -u --recursive --new-file v2.3.5/linux/net/core/rtnetlink.c linux/net/core/rtnetlink.c
--- v2.3.5/linux/net/core/rtnetlink.c	Mon May 31 22:28:07 1999
+++ linux/net/core/rtnetlink.c	Wed Jun  9 14:45:36 1999
@@ -50,22 +50,22 @@
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 
-atomic_t rtnl_rlockct;
-DECLARE_WAIT_QUEUE_HEAD(rtnl_wait);
+DECLARE_MUTEX(rtnl_sem);
 
-
-void rtnl_lock()
+void rtnl_lock(void)
 {
 	rtnl_shlock();
 	rtnl_exlock();
 }
-
-void rtnl_unlock()
+ 
+void rtnl_unlock(void)
 {
 	rtnl_exunlock();
 	rtnl_shunlock();
 }
 
+
+
 int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len)
 {
 	memset(tb, 0, sizeof(struct rtattr*)*maxattr);
@@ -82,8 +82,6 @@
 #ifdef CONFIG_RTNETLINK
 struct sock *rtnl;
 
-unsigned long rtnl_wlockct;
-
 struct rtnetlink_link * rtnetlink_links[NPROTO];
 
 #define _S	1	/* superuser privileges required */
@@ -189,14 +187,14 @@
 	int s_idx = cb->args[0];
 	struct device *dev;
 
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
 		if (idx < s_idx)
 			continue;
 		if (rtnetlink_fill_ifinfo(skb, dev, RTM_NEWLINK, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq) <= 0)
 			break;
 	}
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 	cb->args[0] = idx;
 
 	return skb->len;
@@ -218,9 +216,7 @@
 			continue;
 		if (idx > s_idx)
 			memset(&cb->args[0], 0, sizeof(cb->args));
-		if (rtnetlink_links[idx][type].dumpit(skb, cb) == 0)
-			continue;
-		if (skb_tailroom(skb) < 256)
+		if (rtnetlink_links[idx][type].dumpit(skb, cb))
 			break;
 	}
 	cb->family = idx;
@@ -247,8 +243,6 @@
 
 static int rtnetlink_done(struct netlink_callback *cb)
 {
-	if (cap_raised(NETLINK_CB(cb->skb).eff_cap, CAP_NET_ADMIN) && cb->nlh->nlmsg_flags&NLM_F_ATOMIC)
-		rtnl_shunlock();
 	return 0;
 }
 
@@ -316,15 +310,9 @@
 		if (link->dumpit == NULL)
 			goto err_inval;
 
-		/* Super-user locks all the tables to get atomic snapshot */
-		if (cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)
-		    && nlh->nlmsg_flags&NLM_F_ATOMIC)
-			atomic_inc(&rtnl_rlockct);
 		if ((*errp = netlink_dump_start(rtnl, skb, nlh,
 						link->dumpit,
 						rtnetlink_done)) != 0) {
-			if (cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN) && nlh->nlmsg_flags&NLM_F_ATOMIC)
-				atomic_dec(&rtnl_rlockct);
 			return -1;
 		}
 		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
diff -u --recursive --new-file v2.3.5/linux/net/decnet/dn_neigh.c linux/net/decnet/dn_neigh.c
--- v2.3.5/linux/net/decnet/dn_neigh.c	Mon May 31 22:28:07 1999
+++ linux/net/decnet/dn_neigh.c	Wed Jun  9 14:45:36 1999
@@ -561,12 +561,13 @@
 
 	len += sprintf(buffer + len, "Addr    Flags State Use Blksize Dev\n");
 
-	neigh_table_lock(&dn_neigh_table);
 	for(i=0;i <= NEIGH_HASHMASK; i++) {
+		read_lock_bh(&dn_neigh_table.lock);
 		n = dn_neigh_table.hash_buckets[i];
 		for(; n != NULL; n = n->next) {
 			struct dn_neigh *dn = (struct dn_neigh *)n;
 
+			read_lock(&n->lock);
 			len += sprintf(buffer+len, "%-7s %s%s%s   %02x    %02d  %07ld %-8s\n",
 					dn_addr2asc(dn_ntohs(dn_eth2dn(dn->addr)), buf),
 					(dn->flags&DN_NDFLAG_R1) ? "1" : "-",
@@ -576,6 +577,7 @@
 					atomic_read(&dn->n.refcnt),
 					dn->blksize,
 					(dn->n.dev) ? dn->n.dev->name : "?");
+			read_unlock(&n->lock);
 
 			pos = begin + len;
 
@@ -584,11 +586,15 @@
                         	begin = pos;
                 	}
 
-                	if (pos > offset + length)
-                       		break;
+                	if (pos > offset + length) {
+				read_unlock_bh(&dn_neigh_table.lock);
+                       		goto done;
+			}
 		}
+		read_unlock_bh(&dn_neigh_table.lock);
 	}
-	neigh_table_unlock(&dn_neigh_table);
+
+done:
 
         *start = buffer + (offset - begin);
         len   -= offset - begin;
diff -u --recursive --new-file v2.3.5/linux/net/ethernet/eth.c linux/net/ethernet/eth.c
--- v2.3.5/linux/net/ethernet/eth.c	Wed Jun  2 14:44:39 1999
+++ linux/net/ethernet/eth.c	Wed Jun  9 14:45:36 1999
@@ -248,6 +248,7 @@
 	eth->h_proto = type;
 	memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
 	memcpy(eth->h_dest, neigh->ha, dev->addr_len);
+	hh->hh_len = ETH_HLEN;
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.3.5/linux/net/ipv4/af_inet.c	Mon May 31 22:28:07 1999
+++ linux/net/ipv4/af_inet.c	Wed Jun  9 14:45:36 1999
@@ -5,7 +5,7 @@
  *
  *		PF_INET protocol family socket handler.
  *
- * Version:	$Id: af_inet.c,v 1.90 1999/05/29 04:30:38 davem Exp $
+ * Version:	$Id: af_inet.c,v 1.91 1999/06/09 08:28:55 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -371,7 +371,7 @@
 		if (protocol && protocol != IPPROTO_UDP)
 			goto free_and_noproto;
 		protocol = IPPROTO_UDP;
-		sk->no_check = UDP_NO_CHECK;
+		sk->no_check = UDP_CSUM_DEFAULT;
 		sk->ip_pmtudisc = IP_PMTUDISC_DONT;
 		prot=&udp_prot;
 		sock->ops = &inet_dgram_ops;
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.3.5/linux/net/ipv4/arp.c	Sun Mar 21 07:22:00 1999
+++ linux/net/ipv4/arp.c	Wed Jun  9 14:45:36 1999
@@ -1,6 +1,6 @@
 /* linux/net/inet/arp.c
  *
- * Version:	$Id: arp.c,v 1.77 1999/03/21 05:22:30 davem Exp $
+ * Version:	$Id: arp.c,v 1.78 1999/06/09 10:10:36 davem Exp $
  *
  * Copyright (C) 1994 by Florian  La Roche
  *
@@ -119,6 +119,11 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+static char *ax2asc2(ax25_address *a, char *buf);
+#endif
+
+
 /*
  *	Interface to generic neighbour cache.
  */
@@ -304,7 +309,7 @@
 	u8  *dst_ha = NULL;
 	struct device *dev = neigh->dev;
 	u32 target = *(u32*)neigh->primary_key;
-	int probes = neigh->probes;
+	int probes = atomic_read(&neigh->probes);
 
 	if (skb && inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL)
 		saddr = skb->nh.iph->saddr;
@@ -315,6 +320,7 @@
 		if (!(neigh->nud_state&NUD_VALID))
 			printk(KERN_DEBUG "trying to ucast probe in NUD_INVALID\n");
 		dst_ha = neigh->ha;
+		read_lock_bh(&neigh->lock);
 	} else if ((probes -= neigh->parms->app_probes) < 0) {
 #ifdef CONFIG_ARPD
 		neigh_app_ns(neigh);
@@ -324,6 +330,8 @@
 
 	arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,
 		 dst_ha, dev->dev_addr, NULL);
+	if (dst_ha)
+		read_unlock_bh(&neigh->lock);
 }
 
 /* OBSOLETE FUNCTIONS */
@@ -372,29 +380,25 @@
 	if (arp_set_predefined(inet_addr_type(paddr), haddr, paddr, dev))
 		return 0;
 
-	start_bh_atomic();
 	n = __neigh_lookup(&arp_tbl, &paddr, dev, 1);
 
 	if (n) {
 		n->used = jiffies;
 		if (n->nud_state&NUD_VALID || neigh_event_send(n, skb) == 0) {
-			memcpy(haddr, n->ha, dev->addr_len);
+			read_lock_bh(&n->lock);
+ 			memcpy(haddr, n->ha, dev->addr_len);
+			read_unlock_bh(&n->lock);
 			neigh_release(n);
-			end_bh_atomic();
 			return 0;
 		}
+		neigh_release(n);
 	} else
 		kfree_skb(skb);
-	neigh_release(n);
-	end_bh_atomic();
 	return 1;
 }
 
 /* END OF OBSOLETE FUNCTIONS */
 
-/*
- * Note: requires bh_atomic locking.
- */
 int arp_bind_neighbour(struct dst_entry *dst)
 {
 	struct device *dev = dst->dev;
@@ -672,7 +676,8 @@
 			    (addr_type == RTN_UNICAST  && rt->u.dst.dev != dev &&
 			     (IN_DEV_PROXY_ARP(in_dev) || pneigh_lookup(&arp_tbl, &tip, dev, 0)))) {
 				n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
-				neigh_release(n);
+				if (n)
+					neigh_release(n);
 
 				if (skb->stamp.tv_sec == 0 ||
 				    skb->pkt_type == PACKET_HOST ||
@@ -785,7 +790,6 @@
 		return -EINVAL;
 
 	err = -ENOBUFS;
-	start_bh_atomic();
 	neigh = __neigh_lookup(&arp_tbl, &ip, dev, 1);
 	if (neigh) {
 		unsigned state = NUD_STALE;
@@ -795,7 +799,6 @@
 				   r->arp_ha.sa_data : NULL, state, 1, 0);
 		neigh_release(neigh);
 	}
-	end_bh_atomic();
 	return err;
 }
 
@@ -819,17 +822,17 @@
 	struct neighbour *neigh;
 	int err = -ENXIO;
 
-	start_bh_atomic();
-	neigh = __neigh_lookup(&arp_tbl, &ip, dev, 0);
+	neigh = neigh_lookup(&arp_tbl, &ip, dev);
 	if (neigh) {
+		read_lock_bh(&neigh->lock);
 		memcpy(r->arp_ha.sa_data, neigh->ha, dev->addr_len);
+		r->arp_flags = arp_state_to_flags(neigh);
+		read_unlock_bh(&neigh->lock);
 		r->arp_ha.sa_family = dev->type;
 		strncpy(r->arp_dev, dev->name, sizeof(r->arp_dev));
-		r->arp_flags = arp_state_to_flags(neigh);
 		neigh_release(neigh);
 		err = 0;
 	}
-	end_bh_atomic();
 	return err;
 }
 
@@ -867,14 +870,12 @@
 			return -EINVAL;
 	}
 	err = -ENXIO;
-	start_bh_atomic();
-	neigh = __neigh_lookup(&arp_tbl, &ip, dev, 0);
+	neigh = neigh_lookup(&arp_tbl, &ip, dev);
 	if (neigh) {
 		if (neigh->nud_state&~NUD_NOARP)
 			err = neigh_update(neigh, NULL, NUD_FAILED, 1, 0);
 		neigh_release(neigh);
 	}
-	end_bh_atomic();
 	return err;
 }
 
@@ -961,16 +962,16 @@
 	char hbuffer[HBUFFERLEN];
 	int i,j,k;
 	const char hexbuf[] =  "0123456789ABCDEF";
+	char abuf[16];
 
 	size = sprintf(buffer,"IP address       HW type     Flags       HW address            Mask     Device\n");
 
 	pos+=size;
 	len+=size;
 
-	neigh_table_lock(&arp_tbl);
-
-	for(i=0; i<=NEIGH_HASHMASK; i++)	{
+	for(i=0; i<=NEIGH_HASHMASK; i++) {
 		struct neighbour *n;
+		read_lock_bh(&arp_tbl.lock);
 		for (n=arp_tbl.hash_buckets[i]; n; n=n->next) {
 			struct device *dev = n->dev;
 			int hatype = dev->type;
@@ -979,17 +980,14 @@
 			if (!(n->nud_state&~NUD_NOARP))
 				continue;
 
-			/* I'd get great pleasure deleting
-			   this ugly code. Let's output it in hexadecimal format.
-			   "arp" utility will eventually repaired  --ANK
-			 */
-#if 1 /* UGLY CODE */
+			read_lock(&n->lock);
+
 /*
  *	Convert hardware address to XX:XX:XX:XX ... form.
  */
 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 			if (hatype == ARPHRD_AX25 || hatype == ARPHRD_NETROM)
-			     strcpy(hbuffer,ax2asc((ax25_address *)n->ha));
+				ax2asc2((ax25_address *)n->ha, hbuffer);
 			else {
 #endif
 			for (k=0,j=0;k<HBUFFERLEN-3 && j<dev->addr_len;j++) {
@@ -998,37 +996,33 @@
 				hbuffer[k++]=':';
 			}
 			hbuffer[--k]=0;
-	
+
 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 		}
 #endif
-#else
-			if ((neigh->nud_state&NUD_VALID) && dev->addr_len) {
-				int j;
-				for (j=0; j < dev->addr_len; j++)
-					sprintf(hbuffer+2*j, "%02x", neigh->ha[j]);
-			} else
-				sprintf(hbuffer, "0");
-#endif
 
 			size = sprintf(buffer+len,
 				"%-17s0x%-10x0x%-10x%s",
-				in_ntoa(*(u32*)n->primary_key),
+				in_ntoa2(*(u32*)n->primary_key, abuf),
 				hatype,
 				arp_state_to_flags(n), 
 				hbuffer);
 			size += sprintf(buffer+len+size,
 				 "     %-17s %s\n",
 				 "*", dev->name);
+			read_unlock(&n->lock);
 
 			len += size;
 			pos += size;
 		  
 			if (pos <= offset)
 				len=0;
-			if (pos >= offset+length)
-				goto done;
+			if (pos >= offset+length) {
+				read_unlock_bh(&arp_tbl.lock);
+ 				goto done;
+			}
 		}
+		read_unlock_bh(&arp_tbl.lock);
 	}
 
 	for (i=0; i<=PNEIGH_HASHMASK; i++) {
@@ -1039,7 +1033,7 @@
 
 			size = sprintf(buffer+len,
 				"%-17s0x%-10x0x%-10x%s",
-				in_ntoa(*(u32*)n->key),
+				in_ntoa2(*(u32*)n->key, abuf),
 				hatype,
  				ATF_PUBL|ATF_PERM,
 				"00:00:00:00:00:00");
@@ -1058,7 +1052,6 @@
 	}
 
 done:
-	neigh_table_unlock(&arp_tbl);
   
 	*start = buffer+len-(pos-offset);	/* Start of wanted data */
 	len = pos-offset;			/* Start slop */
@@ -1117,14 +1110,13 @@
 }
 
 
-#ifdef CONFIG_AX25_MODULE
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 
 /*
  *	ax25 -> ASCII conversion
  */
-char *ax2asc(ax25_address *a)
+char *ax2asc2(ax25_address *a, char *buf)
 {
-	static char buf[11];
 	char c, *s;
 	int n;
 
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.3.5/linux/net/ipv4/devinet.c	Wed Jun  2 14:44:39 1999
+++ linux/net/ipv4/devinet.c	Wed Jun  9 14:45:36 1999
@@ -1,7 +1,7 @@
 /*
  *	NET3	IP device support routines.
  *
- *	Version: $Id: devinet.c,v 1.30 1999/06/01 07:49:59 davem Exp $
+ *	Version: $Id: devinet.c,v 1.32 1999/06/09 11:15:33 davem Exp $
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -636,10 +636,10 @@
 	return done;
 }
 
-u32 inet_select_addr(struct device *dev, u32 dst, int scope)
+u32 inet_select_addr(const struct device *dev, u32 dst, int scope)
 {
 	u32 addr = 0;
-	struct in_device *in_dev = dev->ip_ptr;
+	const struct in_device *in_dev = dev->ip_ptr;
 
 	if (in_dev == NULL)
 		return 0;
@@ -659,19 +659,19 @@
 	   in this case. It is importnat that lo is the first interface
 	   in dev_base list.
 	 */
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev=dev_base; dev; dev=dev->next) {
 		if ((in_dev=dev->ip_ptr) == NULL)
 			continue;
 
 		for_primary_ifa(in_dev) {
 			if (ifa->ifa_scope <= scope) {
-				read_unlock_bh(&dev_base_lock);
+				read_unlock(&dev_base_lock);
 				return ifa->ifa_local;
 			}
 		} endfor_ifa(in_dev);
 	}
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 
 	return 0;
 }
@@ -792,7 +792,7 @@
 
 	s_idx = cb->args[0];
 	s_ip_idx = ip_idx = cb->args[1];
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
 		if (idx < s_idx)
 			continue;
@@ -810,7 +810,7 @@
 		}
 	}
 done:
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 	cb->args[0] = idx;
 	cb->args[1] = ip_idx;
 
@@ -885,13 +885,13 @@
 	ipv4_devconf.accept_redirects = !on;
 	ipv4_devconf_dflt.forwarding = on;
 
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev; dev = dev->next) {
 		struct in_device *in_dev = dev->ip_ptr;
 		if (in_dev)
 			in_dev->cnf.forwarding = on;
 	}
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 
 	rt_cache_flush(0);
 
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/fib_frontend.c linux/net/ipv4/fib_frontend.c
--- v2.3.5/linux/net/ipv4/fib_frontend.c	Sun Mar 21 07:22:00 1999
+++ linux/net/ipv4/fib_frontend.c	Wed Jun  9 14:45:36 1999
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: FIB frontend.
  *
- * Version:	$Id: fib_frontend.c,v 1.15 1999/03/21 05:22:31 davem Exp $
+ * Version:	$Id: fib_frontend.c,v 1.16 1999/06/09 10:10:42 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -123,13 +123,11 @@
 		first = 0;
   	}
 
-	/* rtnl_shlock(); -- it is pointless at the moment --ANK */
 	if (main_table && count > 0) {
 		int n = main_table->tb_get_info(main_table, ptr, first, count);
 		count -= n;
 		ptr += n*128;
 	}
-	/* rtnl_shunlock(); */
 	len = ptr - *start;
 	if (len >= length)
 		return length;
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/fib_hash.c linux/net/ipv4/fib_hash.c
--- v2.3.5/linux/net/ipv4/fib_hash.c	Mon May 31 22:28:07 1999
+++ linux/net/ipv4/fib_hash.c	Wed Jun  9 14:45:36 1999
@@ -5,7 +5,7 @@
  *
  *		IPv4 FIB: lookup engine and maintenance routines.
  *
- * Version:	$Id: fib_hash.c,v 1.9 1999/05/27 00:38:05 davem Exp $
+ * Version:	$Id: fib_hash.c,v 1.10 1999/06/09 10:10:45 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -246,10 +246,10 @@
 	fz->fz_mask = inet_make_mask(z);
 
 	/* Find the first not empty zone with more specific mask */
-	write_lock_bh(&fib_hash_lock);
 	for (i=z+1; i<=32; i++)
 		if (table->fn_zones[i])
 			break;
+	write_lock_bh(&fib_hash_lock);
 	if (i>32) {
 		/* No more specific masks, we are the first. */
 		fz->fz_next = table->fn_zone_list;
@@ -270,7 +270,7 @@
 	struct fn_zone *fz;
 	struct fn_hash *t = (struct fn_hash*)tb->tb_data;
 
-	read_lock_bh(&fib_hash_lock);
+	read_lock(&fib_hash_lock);
 	for (fz = t->fn_zone_list; fz; fz = fz->fz_next) {
 		struct fib_node *f;
 		fn_key_t k = fz_key(key->dst, fz);
@@ -307,7 +307,7 @@
 	}
 	err = 1;
 out:
-	read_unlock_bh(&fib_hash_lock);
+	read_unlock(&fib_hash_lock);
 	return err;
 }
 
@@ -353,7 +353,7 @@
 	last_resort = NULL;
 	order = -1;
 
-	read_lock_bh(&fib_hash_lock);
+	read_lock(&fib_hash_lock);
 	for (f = fz->fz_hash[0]; f; f = f->fn_next) {
 		struct fib_info *next_fi = FIB_INFO(f);
 
@@ -395,7 +395,7 @@
 		res->fi = last_resort;
 	fn_hash_last_dflt = last_idx;
 out:
-	read_unlock_bh(&fib_hash_lock);
+	read_unlock(&fib_hash_lock);
 }
 
 #define FIB_SCAN(f, fp) \
@@ -469,7 +469,6 @@
 
 	fp = fz_chain_p(key, fz);
 
-	write_lock_bh(&fib_hash_lock);
 
 	/*
 	 * Scan list to find the first route with the same destination
@@ -574,12 +573,15 @@
 	 */
 
 	new_f->fn_next = f;
+	write_lock_bh(&fib_hash_lock);
 	*fp = new_f;
+	write_unlock_bh(&fib_hash_lock);
 	fz->fz_nent++;
 
 	if (del_fp) {
 		f = *del_fp;
 		/* Unlink replaced node */
+		write_lock_bh(&fib_hash_lock);
 		*del_fp = f->fn_next;
 		write_unlock_bh(&fib_hash_lock);
 
@@ -590,14 +592,12 @@
 		fn_free_node(f);
 		fz->fz_nent--;
 	} else {
-		write_unlock_bh(&fib_hash_lock);
 		rt_cache_flush(-1);
 	}
 	rtmsg_fib(RTM_NEWROUTE, new_f, z, tb->tb_id, n, req);
 	return 0;
 
 out:
-	write_unlock_bh(&fib_hash_lock);
 	fib_release_info(fi);
 	return err;
 }
@@ -635,13 +635,11 @@
 
 	fp = fz_chain_p(key, fz);
 
-	write_lock_bh(&fib_hash_lock);
 
 	FIB_SCAN(f, fp) {
 		if (fn_key_eq(f->fn_key, key))
 			break;
 		if (fn_key_leq(key, f->fn_key)) {
-			write_unlock_bh(&fib_hash_lock);
 			return -ESRCH;
 		}
 	}
@@ -658,7 +656,6 @@
 		struct fib_info * fi = FIB_INFO(f);
 
 		if (f->fn_state&FN_S_ZOMBIE) {
-			write_unlock_bh(&fib_hash_lock);
 			return -ESRCH;
 		}
 		matched++;
@@ -676,6 +673,7 @@
 		rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req);
 
 		if (matched != 1) {
+			write_lock_bh(&fib_hash_lock);
 			*del_fp = f->fn_next;
 			write_unlock_bh(&fib_hash_lock);
 
@@ -684,7 +682,6 @@
 			fn_free_node(f);
 			fz->fz_nent--;
 		} else {
-			write_unlock_bh(&fib_hash_lock);
 			f->fn_state |= FN_S_ZOMBIE;
 			if (f->fn_state&FN_S_ACCESSED) {
 				f->fn_state &= ~FN_S_ACCESSED;
@@ -696,7 +693,6 @@
 
 		return 0;
 	}
-	write_unlock_bh(&fib_hash_lock);
 	return -ESRCH;
 }
 
@@ -710,7 +706,9 @@
 		struct fib_info *fi = FIB_INFO(f);
 
 		if (fi && ((f->fn_state&FN_S_ZOMBIE) || (fi->fib_flags&RTNH_F_DEAD))) {
+			write_lock_bh(&fib_hash_lock);
 			*fp = f->fn_next;
+			write_unlock_bh(&fib_hash_lock);
 
 			fn_free_node(f);
 			found++;
@@ -728,7 +726,6 @@
 	int found = 0;
 
 	fib_hash_zombies = 0;
-	write_lock_bh(&fib_hash_lock);
 	for (fz = table->fn_zone_list; fz; fz = fz->fz_next) {
 		int i;
 		int tmp = 0;
@@ -737,7 +734,6 @@
 		fz->fz_nent -= tmp;
 		found += tmp;
 	}
-	write_unlock_bh(&fib_hash_lock);
 	return found;
 }
 
@@ -751,7 +747,7 @@
 	int pos = 0;
 	int n = 0;
 
-	read_lock_bh(&fib_hash_lock);
+	read_lock(&fib_hash_lock);
 	for (fz=table->fn_zone_list; fz; fz = fz->fz_next) {
 		int i;
 		struct fib_node *f;
@@ -782,7 +778,7 @@
 		}
 	}
 out:
-	read_unlock_bh(&fib_hash_lock);
+	read_unlock(&fib_hash_lock);
   	return n;
 }
 #endif
@@ -845,18 +841,18 @@
 	struct fn_hash *table = (struct fn_hash*)tb->tb_data;
 
 	s_m = cb->args[1];
-	read_lock_bh(&fib_hash_lock);
+	read_lock(&fib_hash_lock);
 	for (fz = table->fn_zone_list, m=0; fz; fz = fz->fz_next, m++) {
 		if (m < s_m) continue;
 		if (m > s_m)
 			memset(&cb->args[2], 0, sizeof(cb->args) - 2*sizeof(cb->args[0]));
 		if (fn_hash_dump_zone(skb, cb, tb, fz) < 0) {
 			cb->args[1] = m;
-			read_unlock_bh(&fib_hash_lock);
+			read_unlock(&fib_hash_lock);
 			return -1;
 		}
 	}
-	read_unlock_bh(&fib_hash_lock);
+	read_unlock(&fib_hash_lock);
 	cb->args[1] = m;
 	return skb->len;
 }
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/fib_rules.c linux/net/ipv4/fib_rules.c
--- v2.3.5/linux/net/ipv4/fib_rules.c	Mon May 31 22:28:07 1999
+++ linux/net/ipv4/fib_rules.c	Wed Jun  9 14:45:36 1999
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: policy rules.
  *
- * Version:	$Id: fib_rules.c,v 1.10 1999/05/27 00:38:03 davem Exp $
+ * Version:	$Id: fib_rules.c,v 1.11 1999/06/09 10:10:47 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -88,7 +88,6 @@
 	struct fib_rule *r, **rp;
 	int err = -ESRCH;
 
-	write_lock_bh(&fib_rules_lock);
 	for (rp=&fib_rules; (r=*rp) != NULL; rp=&r->r_next) {
 		if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 4) == 0) &&
 		    rtm->rtm_src_len == r->r_src_len &&
@@ -106,14 +105,15 @@
 			if (r == &local_rule)
 				break;
 
+			write_lock_bh(&fib_rules_lock);
 			*rp = r->r_next;
+			write_unlock_bh(&fib_rules_lock);
 			if (r != &default_rule && r != &main_rule)
 				kfree(r);
 			err = 0;
 			break;
 		}
 	}
-	write_unlock_bh(&fib_rules_lock);
 	return err;
 }
 
@@ -192,7 +192,6 @@
 		memcpy(&new_r->r_tclassid, RTA_DATA(rta[RTA_FLOW-1]), 4);
 #endif
 
-	write_lock_bh(&fib_rules_lock);
 	rp = &fib_rules;
 	if (!new_r->r_preference) {
 		r = fib_rules;
@@ -210,6 +209,7 @@
 	}
 
 	new_r->r_next = r;
+	write_lock_bh(&fib_rules_lock);
 	*rp = new_r;
 	write_unlock_bh(&fib_rules_lock);
 	return 0;
@@ -255,24 +255,26 @@
 {
 	struct fib_rule *r;
 
-	write_lock_bh(&fib_rules_lock);
 	for (r=fib_rules; r; r=r->r_next) {
-		if (r->r_ifindex == dev->ifindex)
+		if (r->r_ifindex == dev->ifindex) {
+			write_lock_bh(&fib_rules_lock);
 			r->r_ifindex = -1;
+			write_unlock_bh(&fib_rules_lock);
+		}
 	}
-	write_unlock_bh(&fib_rules_lock);
 }
 
 static void fib_rules_attach(struct device *dev)
 {
 	struct fib_rule *r;
 
-	write_lock_bh(&fib_rules_lock);
 	for (r=fib_rules; r; r=r->r_next) {
-		if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0)
+		if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0) {
+			write_lock_bh(&fib_rules_lock);
 			r->r_ifindex = dev->ifindex;
+			write_unlock_bh(&fib_rules_lock);
+		}
 	}
-	write_unlock_bh(&fib_rules_lock);
 }
 
 int fib_lookup(const struct rt_key *key, struct fib_result *res)
@@ -285,7 +287,7 @@
 	u32 saddr = key->src;
 
 FRprintk("Lookup: %08x <- %08x ", key->dst, key->src);
-	read_lock_bh(&fib_rules_lock);
+	read_lock(&fib_rules_lock);
 	for (r = fib_rules; r; r=r->r_next) {
 		if (((saddr^r->r_src) & r->r_srcmask) ||
 		    ((daddr^r->r_dst) & r->r_dstmask) ||
@@ -305,14 +307,14 @@
 			policy = r;
 			break;
 		case RTN_UNREACHABLE:
-			read_unlock_bh(&fib_rules_lock);
+			read_unlock(&fib_rules_lock);
 			return -ENETUNREACH;
 		default:
 		case RTN_BLACKHOLE:
-			read_unlock_bh(&fib_rules_lock);
+			read_unlock(&fib_rules_lock);
 			return -EINVAL;
 		case RTN_PROHIBIT:
-			read_unlock_bh(&fib_rules_lock);
+			read_unlock(&fib_rules_lock);
 			return -EACCES;
 		}
 
@@ -322,16 +324,16 @@
 		if (err == 0) {
 FRprintk("ok\n");
 			res->r = policy;
-			read_unlock_bh(&fib_rules_lock);
+			read_unlock(&fib_rules_lock);
 			return 0;
 		}
 		if (err < 0 && err != -EAGAIN) {
-			read_unlock_bh(&fib_rules_lock);
+			read_unlock(&fib_rules_lock);
 			return err;
 		}
 	}
 FRprintk("FAILURE\n");
-	read_unlock_bh(&fib_rules_lock);
+	read_unlock(&fib_rules_lock);
 	return -ENETUNREACH;
 }
 
@@ -418,14 +420,14 @@
 	int s_idx = cb->args[0];
 	struct fib_rule *r;
 
-	read_lock_bh(&fib_rules_lock);
+	read_lock(&fib_rules_lock);
 	for (r=fib_rules, idx=0; r; r = r->r_next, idx++) {
 		if (idx < s_idx)
 			continue;
 		if (inet_fill_rule(skb, r, cb) < 0)
 			break;
 	}
-	read_unlock_bh(&fib_rules_lock);
+	read_unlock(&fib_rules_lock);
 	cb->args[0] = idx;
 
 	return skb->len;
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v2.3.5/linux/net/ipv4/icmp.c	Wed Jun  2 14:44:39 1999
+++ linux/net/ipv4/icmp.c	Wed Jun  9 14:45:37 1999
@@ -3,7 +3,7 @@
  *	
  *		Alan Cox, <alan@redhat.com>
  *
- *	Version: $Id: icmp.c,v 1.54 1999/05/30 01:16:22 davem Exp $
+ *	Version: $Id: icmp.c,v 1.57 1999/06/09 10:10:50 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -699,8 +699,8 @@
 			case ICMP_FRAG_NEEDED:
 				if (ipv4_config.no_pmtu_disc) {
 					if (net_ratelimit())
-						printk(KERN_INFO "ICMP: %s: fragmentation needed and DF set.\n",
-					       in_ntoa(iph->daddr));
+						printk(KERN_INFO "ICMP: %d.%d.%d.%d: fragmentation needed and DF set.\n",
+						       NIPQUAD(iph->daddr));
 				} else {
 					unsigned short new_mtu;
 					new_mtu = ip_rt_frag_needed(iph, ntohs(icmph->un.frag.mtu));
@@ -711,7 +711,7 @@
 				break;
 			case ICMP_SR_FAILED:
 				if (net_ratelimit())
-					printk(KERN_INFO "ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
+					printk(KERN_INFO "ICMP: %d.%d.%d.%d: Source Route Failed.\n", NIPQUAD(iph->daddr));
 				break;
 			default:
 				break;
@@ -741,8 +741,8 @@
 		if (inet_addr_type(iph->daddr) == RTN_BROADCAST)
 		{
 			if (net_ratelimit())
-				printk(KERN_WARNING "%s sent an invalid ICMP error to a broadcast.\n",
-			       	in_ntoa(skb->nh.iph->saddr));
+				printk(KERN_WARNING "%d.%d.%d.%d sent an invalid ICMP error to a broadcast.\n",
+			       	NIPQUAD(skb->nh.iph->saddr));
 			return; 
 		}
 	}
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v2.3.5/linux/net/ipv4/igmp.c	Mon May 31 22:28:07 1999
+++ linux/net/ipv4/igmp.c	Wed Jun  9 14:45:37 1999
@@ -8,7 +8,7 @@
  *	the older version didn't come out right using gcc 2.5.8, the newer one
  *	seems to fall out with gcc 2.6.2.
  *
- *	Version: $Id: igmp.c,v 1.31 1999/05/27 00:37:59 davem Exp $
+ *	Version: $Id: igmp.c,v 1.32 1999/06/09 10:10:53 davem Exp $
  *
  *	Authors:
  *		Alan Cox <Alan.Cox@linux.org>
@@ -97,6 +97,15 @@
 #include <linux/mroute.h>
 #endif
 
+/* Big mc list lock for all the devices */
+static rwlock_t ip_mc_lock = RW_LOCK_UNLOCKED;
+/* Big mc list semaphore for all the sockets.
+   We do not refer to this list in IP data paths or from BH,
+   so that semaphore is OK.
+ */
+DECLARE_MUTEX(ip_sk_mc_sem);
+
+
 #define IP_MAX_MEMBERSHIPS 20
 
 #ifdef CONFIG_IP_MULTICAST
@@ -216,6 +225,8 @@
 	struct in_device *in_dev = im->interface;
 	int err;
 
+	read_lock(&ip_mc_lock);
+
 	im->tm_running=0;
 
 	if (IGMP_V1_SEEN(in_dev))
@@ -234,6 +245,7 @@
 		igmp_start_timer(im, IGMP_Unsolicited_Report_Interval);
 	}
 	im->reporter = 1;
+	read_unlock(&ip_mc_lock);
 }
 
 static void igmp_heard_report(struct in_device *in_dev, u32 group)
@@ -245,14 +257,16 @@
 	if (LOCAL_MCAST(group))
 		return;
 
+	read_lock(&ip_mc_lock);
 	for (im=in_dev->mc_list; im!=NULL; im=im->next) {
 		if (im->multiaddr == group) {
 			igmp_stop_timer(im);
 			im->reporter = 0;
 			im->unsolicit_count = 0;
-			return;
+			break;
 		}
 	}
+	read_unlock(&ip_mc_lock);
 }
 
 static void igmp_heard_query(struct in_device *in_dev, unsigned char max_resp_time,
@@ -281,6 +295,7 @@
 	 * - Use the igmp->igmp_code field as the maximum
 	 *   delay possible
 	 */
+	read_lock(&ip_mc_lock);
 	for (im=in_dev->mc_list; im!=NULL; im=im->next) {
 		if (group && group != im->multiaddr)
 			continue;
@@ -291,6 +306,7 @@
 			igmp_stop_timer(im);
 		igmp_start_timer(im, max_delay);
 	}
+	read_unlock(&ip_mc_lock);
 }
 
 int igmp_rcv(struct sk_buff *skb, unsigned short len)
@@ -380,9 +396,7 @@
 	if (LOCAL_MCAST(im->multiaddr))
 		return;
 
-	start_bh_atomic();
 	igmp_stop_timer(im);
-	end_bh_atomic();
 
 	if (im->reporter && !IGMP_V1_SEEN(im->interface))
 		igmp_send_report(im->interface->dev, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
@@ -400,9 +414,7 @@
 	if (LOCAL_MCAST(im->multiaddr))
 		return;
 
-	start_bh_atomic();
 	igmp_start_timer(im, IGMP_Initial_Report_Delay);
-	end_bh_atomic();
 #endif
 }
 
@@ -422,16 +434,17 @@
 
 	im = (struct ip_mc_list *)kmalloc(sizeof(*im), GFP_KERNEL);
 
+	write_lock_bh(&ip_mc_lock);
 	for (i=in_dev->mc_list; i; i=i->next) {
 		if (i->multiaddr == addr) {
 			i->users++;
 			if (im)
 				kfree(im);
-			return;
+			goto out;
 		}
 	}
 	if (!im)
-		return;
+		goto out;
 	im->users=1;
 	im->interface=in_dev;
 	im->multiaddr=addr;
@@ -447,9 +460,13 @@
 	im->next=in_dev->mc_list;
 	in_dev->mc_list=im;
 	igmp_group_added(im);
+	write_unlock_bh(&ip_mc_lock);
 	if (in_dev->dev->flags & IFF_UP)
 		ip_rt_multicast_event(in_dev);
 	return;
+out:
+	write_unlock_bh(&ip_mc_lock);
+	return;
 }
 
 /*
@@ -458,22 +475,27 @@
 
 int ip_mc_dec_group(struct in_device *in_dev, u32 addr)
 {
+	int err = -ESRCH;
 	struct ip_mc_list *i, **ip;
 
+	write_lock_bh(&ip_mc_lock);
 	for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) {
 		if (i->multiaddr==addr) {
 			if (--i->users == 0) {
 				*ip = i->next;
-				synchronize_bh();
-
 				igmp_group_dropped(i);
+
+				write_unlock_bh(&ip_mc_lock);
 				if (in_dev->dev->flags & IFF_UP)
 					ip_rt_multicast_event(in_dev);
 				kfree_s(i, sizeof(*i));
+				return 0;
 			}
-			return 0;
+			err = 0;
+			break;
 		}
 	}
+	write_unlock_bh(&ip_mc_lock);
 	return -ESRCH;
 }
 
@@ -483,8 +505,10 @@
 {
 	struct ip_mc_list *i;
 
+	read_lock_bh(&ip_mc_lock);
 	for (i=in_dev->mc_list; i; i=i->next)
 		igmp_group_dropped(i);
+	read_unlock_bh(&ip_mc_lock);
 
 	ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS);
 }
@@ -497,8 +521,10 @@
 
 	ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS);
 
+	read_lock_bh(&ip_mc_lock);
 	for (i=in_dev->mc_list; i; i=i->next)
 		igmp_group_added(i);
+	read_unlock_bh(&ip_mc_lock);
 }
 
 /*
@@ -509,11 +535,13 @@
 {
 	struct ip_mc_list *i;
 
+	write_lock_bh(&ip_mc_lock);
 	while ((i = in_dev->mc_list) != NULL) {
 		in_dev->mc_list = i->next;
 		igmp_group_dropped(i);
 		kfree_s(i, sizeof(*i));
 	}
+	write_unlock_bh(&ip_mc_lock);
 }
 
 static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
@@ -570,6 +598,7 @@
 	iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
 
 	err = -EADDRINUSE;
+	down(&ip_sk_mc_sem);
 	for (i=sk->ip_mc_list; i; i=i->next) {
 		if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) {
 			/* New style additions are reference counted */
@@ -577,13 +606,13 @@
 				i->count++;
 				err = 0;
 			}
-			goto done;
+			goto done_unlock;
 		}
 		count++;
 	}
 	err = -ENOBUFS;
 	if (iml == NULL || count >= sysctl_igmp_max_memberships)
-		goto done;
+		goto done_unlock;
 	memcpy(&iml->multi, imr, sizeof(*imr));
 	iml->next = sk->ip_mc_list;
 	iml->count = 1;
@@ -591,6 +620,9 @@
 	ip_mc_inc_group(in_dev, addr);
 	iml = NULL;
 	err = 0;
+
+done_unlock:
+	up(&ip_sk_mc_sem);
 done:
 	rtnl_shunlock();
 	if (iml)
@@ -606,6 +638,7 @@
 {
 	struct ip_mc_socklist *iml, **imlp;
 
+	down(&ip_sk_mc_sem);
 	for (imlp=&sk->ip_mc_list; (iml=*imlp)!=NULL; imlp=&iml->next) {
 		if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr &&
 		    iml->multi.imr_address.s_addr==imr->imr_address.s_addr &&
@@ -615,7 +648,7 @@
 				return 0;
 
 			*imlp = iml->next;
-			synchronize_bh();
+			up(&ip_sk_mc_sem);
 
 			in_dev = inetdev_by_index(iml->multi.imr_ifindex);
 			if (in_dev)
@@ -624,6 +657,7 @@
 			return 0;
 		}
 	}
+	up(&ip_sk_mc_sem);
 	return -EADDRNOTAVAIL;
 }
 
@@ -635,13 +669,37 @@
 {
 	struct ip_mc_socklist *iml;
 
+	down(&ip_sk_mc_sem);
 	while ((iml=sk->ip_mc_list) != NULL) {
 		struct in_device *in_dev;
 		sk->ip_mc_list = iml->next;
+		up(&ip_sk_mc_sem);
+
 		if ((in_dev = inetdev_by_index(iml->multi.imr_ifindex)) != NULL)
 			ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
 		sock_kfree_s(sk, iml, sizeof(*iml));
+
+		down(&ip_sk_mc_sem);
 	}
+	up(&ip_sk_mc_sem);
+}
+
+int ip_check_mc(struct device *dev, u32 mc_addr)
+{
+	struct in_device *in_dev = dev->ip_ptr;
+	struct ip_mc_list *im;
+
+	if (in_dev) {
+		read_lock(&ip_mc_lock);
+		for (im=in_dev->mc_list; im; im=im->next) {
+			if (im->multiaddr == mc_addr) {
+				read_unlock(&ip_mc_lock);
+				return 1;
+			}
+		}
+		read_unlock(&ip_mc_lock);
+	}
+	return 0;
 }
 
 
@@ -653,10 +711,10 @@
 	struct ip_mc_list *im;
 	int len=0;
 	struct device *dev;
-	
+
 	len=sprintf(buffer,"Idx\tDevice    : Count Querier\tGroup    Users Timer\tReporter\n");  
-	
-	read_lock_bh(&dev_base_lock);
+
+	read_lock(&dev_base_lock);
 	for(dev = dev_base; dev; dev = dev->next) {
 		struct in_device *in_dev = dev->ip_ptr;
 		char   *querier = "NONE";
@@ -669,6 +727,7 @@
 		len+=sprintf(buffer+len,"%d\t%-10s: %5d %7s\n",
 			     dev->ifindex, dev->name, dev->mc_count, querier);
 
+		read_lock(&ip_mc_lock);
 		for (im = in_dev->mc_list; im; im = im->next) {
 			len+=sprintf(buffer+len,
 				     "\t\t\t\t%08lX %5d %d:%08lX\t\t%d\n",
@@ -681,12 +740,15 @@
 				len=0;
 				begin=pos;
 			}
-			if(pos>offset+length)
+			if(pos>offset+length) {
+				read_unlock(&ip_mc_lock);
 				goto done;
+			}
 		}
+		read_unlock(&ip_mc_lock);
 	}
 done:
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 
 	*start=buffer+(offset-begin);
 	len-=(offset-begin);
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v2.3.5/linux/net/ipv4/ip_input.c	Mon May 31 22:28:07 1999
+++ linux/net/ipv4/ip_input.c	Wed Jun  9 14:45:37 1999
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) module.
  *
- * Version:	$Id: ip_input.c,v 1.39 1999/05/30 01:16:25 davem Exp $
+ * Version:	$Id: ip_input.c,v 1.40 1999/06/09 10:10:55 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -392,21 +392,17 @@
 	if (skb->dst == NULL) {
 		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))
 			goto drop; 
-#ifdef CONFIG_CPU_IS_SLOW
-		if (net_cpu_congestion > 10 && !(iph->tos&IPTOS_RELIABILITY) &&
-		    IPTOS_PREC(iph->tos) < IPTOS_PREC_INTERNETCONTROL) {
-			goto drop;
-		}
-#endif
 	}
 
 #ifdef CONFIG_NET_CLS_ROUTE
 	if (skb->dst->tclassid) {
 		u32 idx = skb->dst->tclassid;
+		write_lock(&ip_rt_acct_lock);
 		ip_rt_acct[idx&0xFF].o_packets++;
 		ip_rt_acct[idx&0xFF].o_bytes+=skb->len;
 		ip_rt_acct[(idx>>16)&0xFF].i_packets++;
 		ip_rt_acct[(idx>>16)&0xFF].i_bytes+=skb->len;
+		write_unlock(&ip_rt_acct_lock);
 	}
 #endif
 
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/ip_masq_vdolive.c linux/net/ipv4/ip_masq_vdolive.c
--- v2.3.5/linux/net/ipv4/ip_masq_vdolive.c	Wed Jun  2 14:44:39 1999
+++ linux/net/ipv4/ip_masq_vdolive.c	Wed Jun  9 14:45:37 1999
@@ -2,7 +2,7 @@
  *		IP_MASQ_VDOLIVE  - VDO Live masquerading module
  *
  *
- * Version:	@(#)$Id: ip_masq_vdolive.c,v 1.4 1998/10/06 04:49:07 davem Exp $
+ * Version:	@(#)$Id: ip_masq_vdolive.c,v 1.6 1999/06/09 08:29:03 davem Exp $
  *
  * Author:	Nigel Metheringham <Nigel.Metheringham@ThePLAnet.net>
  *		PLAnet Online Ltd
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/ip_options.c linux/net/ipv4/ip_options.c
--- v2.3.5/linux/net/ipv4/ip_options.c	Wed Jun  2 14:44:39 1999
+++ linux/net/ipv4/ip_options.c	Wed Jun  9 14:45:37 1999
@@ -5,7 +5,7 @@
  *
  *		The options processing module for ip.c
  *
- * Version:	$Id: ip_options.c,v 1.16 1999/03/21 05:22:40 davem Exp $
+ * Version:	$Id: ip_options.c,v 1.18 1999/06/09 08:29:06 davem Exp $
  *
  * Authors:	A.N.Kuznetsov
  *		
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/ipconfig.c linux/net/ipv4/ipconfig.c
--- v2.3.5/linux/net/ipv4/ipconfig.c	Mon May 31 22:28:07 1999
+++ linux/net/ipv4/ipconfig.c	Wed Jun  9 14:45:37 1999
@@ -1,5 +1,5 @@
 /*
- *  $Id: ipconfig.c,v 1.21 1999/05/27 00:38:01 davem Exp $
+ *  $Id: ipconfig.c,v 1.22 1999/06/09 10:10:57 davem Exp $
  *
  *  Automatic Configuration of IP -- use BOOTP or RARP or user-supplied
  *  information to configure own IP address and routes.
@@ -112,7 +112,7 @@
 	unsigned short oflags;
 
 	last = &ic_first_dev;
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev; dev = dev->next) {
 		if (user_dev_name[0] ? !strcmp(dev->name, user_dev_name) :
 		    (!(dev->flags & IFF_LOOPBACK) &&
@@ -144,7 +144,7 @@
 			DBG(("IP-Config: Opened %s (able=%d)\n", dev->name, able));
 		}
 	}
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 
 	*last = NULL;
 
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/ipmr.c linux/net/ipv4/ipmr.c
--- v2.3.5/linux/net/ipv4/ipmr.c	Wed Jun  2 14:44:39 1999
+++ linux/net/ipv4/ipmr.c	Wed Jun  9 14:45:37 1999
@@ -9,7 +9,7 @@
  *	as published by the Free Software Foundation; either version
  *	2 of the License, or (at your option) any later version.
  *
- *	Version: $Id: ipmr.c,v 1.40 1999/03/25 10:04:25 davem Exp $
+ *	Version: $Id: ipmr.c,v 1.43 1999/06/09 10:10:59 davem Exp $
  *
  *	Fixes:
  *	Michael Chastain	:	Incorrect size of copying.
@@ -23,6 +23,8 @@
  *	Brad Parker		:	Better behaviour on mrouted upcall
  *					overflow.
  *      Carlos Picoto           :       PIMv1 Support
+ *	Pavlin Ivanov Radoslavov:	PIMv2 Registers must checksum only PIM header
+ *					Relax this requrement to work with older peers.
  *
  */
 
@@ -431,7 +433,7 @@
 				skb_trim(skb, nlh->nlmsg_len);
 				((struct nlmsgerr*)NLMSG_DATA(nlh))->error = -EMSGSIZE;
 			}
-			err = netlink_unicast(rtnl, skb, NETLINK_CB(skb).pid, MSG_DONTWAIT);
+			err = netlink_unicast(rtnl, skb, NETLINK_CB(skb).dst_pid, MSG_DONTWAIT);
 		} else
 #endif
 			ip_mr_forward(skb, cache, 0);
@@ -1343,7 +1345,8 @@
 	    pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) ||
 	    (pim->flags&PIM_NULL_REGISTER) ||
 	    reg_dev == NULL ||
-	    ip_compute_csum((void *)pim, len)) {
+	    (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
+	     ip_compute_csum((void *)pim, len))) {
 		kfree_skb(skb);
                 return -EINVAL;
         }
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.3.5/linux/net/ipv4/route.c	Mon May 31 22:28:07 1999
+++ linux/net/ipv4/route.c	Wed Jun  9 14:45:37 1999
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.68 1999/05/27 00:37:54 davem Exp $
+ * Version:	$Id: route.c,v 1.69 1999/06/09 10:11:02 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1996,6 +1996,7 @@
 
 #ifdef CONFIG_NET_CLS_ROUTE
 struct ip_rt_acct ip_rt_acct[256];
+rwlock_t ip_rt_acct_lock = RW_LOCK_UNLOCKED;
 
 #ifdef CONFIG_PROC_FS
 static int ip_rt_acct_read(char *buffer, char **start, off_t offset,
@@ -2008,9 +2009,9 @@
 		*eof = 1;
 	}
 	if (length > 0) {
-		start_bh_atomic();
+		read_lock_bh(&ip_rt_acct_lock);
 		memcpy(buffer, ((u8*)&ip_rt_acct)+offset, length);
-		end_bh_atomic();
+		read_unlock_bh(&ip_rt_acct_lock);
 		return length;
 	}
 	return 0;
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.3.5/linux/net/ipv4/tcp_input.c	Wed Jun  2 14:44:39 1999
+++ linux/net/ipv4/tcp_input.c	Wed Jun  9 14:45:37 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.167 1999/05/29 22:37:54 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.169 1999/06/09 08:29:13 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.3.5/linux/net/ipv4/tcp_ipv4.c	Mon May 31 22:28:07 1999
+++ linux/net/ipv4/tcp_ipv4.c	Wed Jun  9 14:45:37 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.178 1999/05/30 01:16:27 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.180 1999/06/09 08:29:19 davem Exp $
  *
  *		IPv4 specific functions
  *
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.3.5/linux/net/ipv4/udp.c	Mon May 31 22:28:07 1999
+++ linux/net/ipv4/udp.c	Wed Jun  9 14:45:37 1999
@@ -5,7 +5,7 @@
  *
  *		The User Datagram Protocol (UDP).
  *
- * Version:	$Id: udp.c,v 1.67 1999/05/27 00:37:50 davem Exp $
+ * Version:	$Id: udp.c,v 1.69 1999/06/09 11:15:31 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -173,7 +173,7 @@
 	return 0;
 }
 
-/* Shared by v4/v6 tcp. */
+/* Shared by v4/v6 udp. */
 unsigned short udp_good_socknum(void)
 {
 	int result;
@@ -763,7 +763,10 @@
 	/* 4.1.3.4. It's configurable by the application via setsockopt() */
 	/* (MAY) and it defaults to on (MUST). */
 
-	err = ip_build_xmit(sk,sk->no_check ? udp_getfrag_nosum : udp_getfrag,
+	err = ip_build_xmit(sk,
+			    (sk->no_check == UDP_CSUM_NOXMIT ?
+			     udp_getfrag_nosum :
+			     udp_getfrag),
 			    &ufh, ulen, &ipc, rt, msg->msg_flags);
 
 out:
@@ -1093,6 +1096,33 @@
 }
 #endif
 
+static int udp_checksum_verify(struct sk_buff *skb, struct udphdr *uh,
+			       unsigned short ulen, u32 saddr, u32 daddr,
+			       int full_csum_deferred)
+{
+	if (!full_csum_deferred) {
+		if (uh->check) {
+			if (skb->ip_summed == CHECKSUM_HW &&
+			    udp_check(uh, ulen, saddr, daddr, skb->csum))
+				return -1;
+			if (skb->ip_summed == CHECKSUM_NONE &&
+			    udp_check(uh, ulen, saddr, daddr,
+				      csum_partial((char *)uh, ulen, 0)))
+				return -1;
+		}
+	} else {
+		if (uh->check == 0)
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+		else if (skb->ip_summed == CHECKSUM_HW) {
+			if (udp_check(uh, ulen, saddr, daddr, skb->csum))
+				return -1;
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+		} else if (skb->ip_summed != CHECKSUM_UNNECESSARY)
+			skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);
+	}
+	return 0;
+}
+
 /*
  *	All we need to do is get the socket, and then do a checksum. 
  */
@@ -1134,25 +1164,18 @@
 	}
 	skb_trim(skb, ulen);
 
-#ifndef CONFIG_UDP_DELAY_CSUM
-	if (uh->check &&
-	    (((skb->ip_summed==CHECKSUM_HW)&&udp_check(uh,ulen,saddr,daddr,skb->csum)) ||
-	     ((skb->ip_summed==CHECKSUM_NONE) &&
-	      (udp_check(uh,ulen,saddr,daddr, csum_partial((char*)uh, ulen, 0)))))) 
-		goto csum_error;
+	if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) {
+		int defer;
+
+#ifdef CONFIG_UDP_DELAY_CSUM
+		defer = 1;
 #else
-	if (uh->check==0)
-		skb->ip_summed = CHECKSUM_UNNECESSARY;
-	else if (skb->ip_summed==CHECKSUM_HW) {
-		if (udp_check(uh,ulen,saddr,daddr,skb->csum)) 
-			goto csum_error;
-		skb->ip_summed = CHECKSUM_UNNECESSARY;
-	} else if (skb->ip_summed != CHECKSUM_UNNECESSARY)
-		skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);
+		defer = 0;
 #endif
-
-	if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
+		if (udp_checksum_verify(skb, uh, ulen, saddr, daddr, defer))
+			goto csum_error;
 		return udp_v4_mcast_deliver(skb, uh, saddr, daddr);
+	}
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 	if (IPCB(skb)->redirport)
@@ -1179,6 +1202,15 @@
 		kfree_skb(skb);
 		return(0);
   	}
+	if (udp_checksum_verify(skb, uh, ulen, saddr, daddr,
+#ifdef CONFIG_UDP_DELAY_CSUM
+				1
+#else
+				(sk->no_check & UDP_CSUM_NORCV) != 0
+#endif
+		))
+		goto csum_error;
+
 	udp_deliver(sk, skb);
 	return 0;
 
diff -u --recursive --new-file v2.3.5/linux/net/ipv4/utils.c linux/net/ipv4/utils.c
--- v2.3.5/linux/net/ipv4/utils.c	Mon Jan 12 15:28:28 1998
+++ linux/net/ipv4/utils.c	Wed Jun  9 14:45:37 1999
@@ -6,7 +6,7 @@
  *		Various kernel-resident INET utility functions; mainly
  *		for format conversion and debugging output.
  *
- * Version:	$Id: utils.c,v 1.6 1997/12/13 21:53:03 kuznet Exp $
+ * Version:	$Id: utils.c,v 1.7 1999/06/09 10:11:05 davem Exp $
  *
  * Author:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *
@@ -57,6 +57,11 @@
 	return(buff);
 }
 
+char *in_ntoa2(__u32 in, char *buff)
+{
+	sprintf(buff, "%d.%d.%d.%d", NIPQUAD(in));
+	return buff;
+}
 
 /*
  *	Convert an ASCII string to binary IP. 
diff -u --recursive --new-file v2.3.5/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.3.5/linux/net/ipv6/addrconf.c	Mon May 31 22:28:07 1999
+++ linux/net/ipv6/addrconf.c	Wed Jun  9 14:45:37 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: addrconf.c,v 1.49 1999/05/27 00:38:20 davem Exp $
+ *	$Id: addrconf.c,v 1.50 1999/06/09 10:11:09 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -100,9 +100,7 @@
    1. The result of inet6_add_addr() is used only inside lock
       or from bh_atomic context.
 
-   2. inet6_get_lladdr() is used only from bh protected context.
-
-   3. The result of ipv6_chk_addr() is not used outside of bh protected context.
+   2. The result of ipv6_chk_addr() is not used outside of bh protected context.
  */
 
 static __inline__ void addrconf_lock(void)
@@ -463,7 +461,7 @@
 	return err;
 }
 
-struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev)
+int ipv6_get_lladdr(struct device *dev, struct in6_addr *addr)
 {
 	struct inet6_ifaddr *ifp = NULL;
 	struct inet6_dev *idev;
@@ -471,12 +469,15 @@
 	if ((idev = ipv6_get_idev(dev)) != NULL) {
 		addrconf_lock();
 		for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
-			if (ifp->scope == IFA_LINK)
-				break;
+			if (ifp->scope == IFA_LINK) {
+				ipv6_addr_copy(addr, &ifp->addr);
+				addrconf_unlock();
+				return 0;
+			}
 		}
 		addrconf_unlock();
 	}
-	return ifp;
+	return -EADDRNOTAVAIL;
 }
 
 /*
@@ -982,7 +983,7 @@
 		return;
 	}
 
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
         for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if (dev->ip_ptr && (dev->flags & IFF_UP)) {
 			struct in_device * in_dev = dev->ip_ptr;
@@ -1001,7 +1002,6 @@
 					flag |= IFA_HOST;
 				}
 
-				read_unlock_bh(&dev_base_lock);
 				addrconf_lock();
 				ifp = ipv6_add_addr(idev, &addr, flag);
 				if (ifp) {
@@ -1013,11 +1013,10 @@
 					ipv6_ifa_notify(RTM_NEWADDR, ifp);
 				}
 				addrconf_unlock();
-				read_lock_bh(&dev_base_lock);
 			}
 		}
         }
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 }
 
 static void init_loopback(struct device *dev)
@@ -1846,12 +1845,11 @@
 	struct device *dev;
 
 	/* This takes sense only during module load. */
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev; dev = dev->next) {
 		if (!(dev->flags&IFF_UP))
 			continue;
 
-		read_unlock_bh(&dev_base_lock);
 		switch (dev->type) {
 		case ARPHRD_LOOPBACK:	
 			init_loopback(dev);
@@ -1862,9 +1860,8 @@
 		default:
 			/* Ignore all other */
 		}
-		read_lock_bh(&dev_base_lock);
 	}
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 #endif
 	
 #ifdef CONFIG_PROC_FS
diff -u --recursive --new-file v2.3.5/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.3.5/linux/net/ipv6/af_inet6.c	Thu Apr 22 19:45:20 1999
+++ linux/net/ipv6/af_inet6.c	Wed Jun  9 14:45:37 1999
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/af_inet.c
  *
- *	$Id: af_inet6.c,v 1.43 1999/04/22 10:07:39 davem Exp $
+ *	$Id: af_inet6.c,v 1.44 1999/06/09 08:29:29 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -103,7 +103,7 @@
 		if (protocol && protocol != IPPROTO_UDP) 
 			goto free_and_noproto;
 		protocol = IPPROTO_UDP;
-		sk->no_check = UDP_NO_CHECK;
+		sk->no_check = UDP_CSUM_DEFAULT;
 		prot=&udpv6_prot;
 		sock->ops = &inet6_dgram_ops;
 	} else if(sock->type == SOCK_RAW) {
diff -u --recursive --new-file v2.3.5/linux/net/ipv6/ip6_fw.c linux/net/ipv6/ip6_fw.c
--- v2.3.5/linux/net/ipv6/ip6_fw.c	Wed Jun  2 14:44:39 1999
+++ linux/net/ipv6/ip6_fw.c	Wed Jun  9 14:45:37 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_fw.c,v 1.10 1998/08/26 12:04:57 davem Exp $
+ *	$Id: ip6_fw.c,v 1.12 1999/06/09 08:29:32 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -383,7 +383,7 @@
 }
 
 #ifdef MODULE
-void module_cleanup(void)
+void cleanup_module(void)
 {
 #ifdef CONFIG_NETLINK
 	netlink_detach(NETLINK_IP6_FW);
diff -u --recursive --new-file v2.3.5/linux/net/ipv6/ip6_output.c linux/net/ipv6/ip6_output.c
--- v2.3.5/linux/net/ipv6/ip6_output.c	Wed Jun  2 14:44:39 1999
+++ linux/net/ipv6/ip6_output.c	Wed Jun  9 14:45:37 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_output.c,v 1.17 1999/04/22 10:07:42 davem Exp $
+ *	$Id: ip6_output.c,v 1.20 1999/06/09 10:11:12 davem Exp $
  *
  *	Based on linux/net/ipv4/ip_output.c
  *
@@ -75,19 +75,10 @@
 	}
 
 	if (hh) {
-#ifdef __alpha__
-		/* Alpha has disguisting memcpy. Help it. */
-	        u64 *aligned_hdr = (u64*)(skb->data - 16);
-		u64 *aligned_hdr0 = hh->hh_data;
-		read_lock_irq(&hh->hh_lock);
-		aligned_hdr[0] = aligned_hdr0[0];
-		aligned_hdr[1] = aligned_hdr0[1];
-#else
-		read_lock_irq(&hh->hh_lock);
+		read_lock_bh(&hh->hh_lock);
 		memcpy(skb->data - 16, hh->hh_data, 16);
-#endif
-		read_unlock_irq(&hh->hh_lock);
-	        skb_push(skb, dev->hard_header_len);
+		read_unlock_bh(&hh->hh_lock);
+	        skb_push(skb, hh->hh_len);
 		return hh->hh_output(skb);
 	} else if (dst->neighbour)
 		return dst->neighbour->output(skb);
diff -u --recursive --new-file v2.3.5/linux/net/ipv6/mcast.c linux/net/ipv6/mcast.c
--- v2.3.5/linux/net/ipv6/mcast.c	Wed Jun  2 14:44:39 1999
+++ linux/net/ipv6/mcast.c	Wed Jun  9 14:45:37 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: mcast.c,v 1.20 1999/05/27 00:38:23 davem Exp $
+ *	$Id: mcast.c,v 1.23 1999/06/09 10:11:14 davem Exp $
  *
  *	Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c 
  *
@@ -53,6 +53,11 @@
 #define MDBG(x)
 #endif
 
+/* Big mc list lock for all the devices */
+static rwlock_t ipv6_mc_lock = RW_LOCK_UNLOCKED;
+/* Big mc list lock for all the sockets */
+static rwlock_t ipv6_sk_mc_lock = RW_LOCK_UNLOCKED;
+
 static struct socket *igmp6_socket;
 
 static void igmp6_join_group(struct ifmcaddr6 *ma);
@@ -115,8 +120,10 @@
 		return err;
 	}
 
+	write_lock_bh(&ipv6_sk_mc_lock);
 	mc_lst->next = np->ipv6_mc_list;
 	np->ipv6_mc_list = mc_lst;
+	write_unlock_bh(&ipv6_sk_mc_lock);
 
 	return 0;
 }
@@ -129,13 +136,14 @@
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct ipv6_mc_socklist *mc_lst, **lnk;
 
+	write_lock_bh(&ipv6_sk_mc_lock);
 	for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk = &mc_lst->next) {
 		if (mc_lst->ifindex == ifindex &&
 		    ipv6_addr_cmp(&mc_lst->addr, addr) == 0) {
 			struct device *dev;
 
 			*lnk = mc_lst->next;
-			synchronize_bh();
+			write_unlock_bh(&ipv6_sk_mc_lock);
 
 			if ((dev = dev_get_by_index(ifindex)) != NULL)
 				ipv6_dev_mc_dec(dev, &mc_lst->addr);
@@ -143,6 +151,7 @@
 			return 0;
 		}
 	}
+	write_unlock_bh(&ipv6_sk_mc_lock);
 
 	return -ENOENT;
 }
@@ -152,15 +161,38 @@
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct ipv6_mc_socklist *mc_lst;
 
+	write_lock_bh(&ipv6_sk_mc_lock);
 	while ((mc_lst = np->ipv6_mc_list) != NULL) {
-		struct device *dev = dev_get_by_index(mc_lst->ifindex);
+		struct device *dev;
+
+		np->ipv6_mc_list = mc_lst->next;
+		write_unlock_bh(&ipv6_sk_mc_lock);
 
+		dev = dev_get_by_index(mc_lst->ifindex);
 		if (dev)
 			ipv6_dev_mc_dec(dev, &mc_lst->addr);
 
-		np->ipv6_mc_list = mc_lst->next;
 		sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
+
+		write_lock_bh(&ipv6_sk_mc_lock);
 	}
+	write_unlock_bh(&ipv6_sk_mc_lock);
+}
+
+int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+{
+	struct ipv6_mc_socklist *mc;
+
+	read_lock(&ipv6_sk_mc_lock);
+	for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
+		if (ipv6_addr_cmp(&mc->addr, addr) == 0) {
+			read_unlock(&ipv6_sk_mc_lock);
+			return 1;
+		}
+	}
+	read_unlock(&ipv6_sk_mc_lock);
+
+	return 0;
 }
 
 static int igmp6_group_added(struct ifmcaddr6 *mc)
@@ -210,9 +242,11 @@
 
 	hash = ipv6_addr_hash(addr);
 
+	write_lock_bh(&ipv6_mc_lock);
 	for (mc = inet6_mcast_lst[hash]; mc; mc = mc->next) {
 		if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0 && mc->dev == dev) {
 			atomic_inc(&mc->mca_users);
+			write_unlock_bh(&ipv6_mc_lock);
 			return 0;
 		}
 	}
@@ -223,8 +257,10 @@
 
 	mc = kmalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC);
 
-	if (mc == NULL)
+	if (mc == NULL) {
+		write_unlock_bh(&ipv6_mc_lock);
 		return -ENOMEM;
+	}
 
 	memset(mc, 0, sizeof(struct ifmcaddr6));
 	mc->mca_timer.function = igmp6_timer_handler;
@@ -242,6 +278,8 @@
 
 	igmp6_group_added(mc);
 
+	write_unlock_bh(&ipv6_mc_lock);
+
 	return 0;
 }
 
@@ -257,7 +295,6 @@
 		for (lnk = &idev->mc_list; (iter = *lnk) != NULL; lnk = &iter->if_next) {
 			if (iter == ma) {
 				*lnk = iter->if_next;
-				synchronize_bh();
 				return;
 			}
 		}
@@ -274,20 +311,22 @@
 
 	hash = ipv6_addr_hash(addr);
 
+	write_lock_bh(&ipv6_mc_lock);
 	for (lnk = &inet6_mcast_lst[hash]; (ma=*lnk) != NULL; lnk = &ma->next) {
 		if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0 && ma->dev == dev) {
 			if (atomic_dec_and_test(&ma->mca_users)) {
 				igmp6_group_dropped(ma);
 
 				*lnk = ma->next;
-				synchronize_bh();
 
 				ipv6_mca_remove(dev, ma);
 				kfree(ma);
 			}
+			write_unlock_bh(&ipv6_mc_lock);
 			return 0;
 		}
 	}
+	write_unlock_bh(&ipv6_mc_lock);
 
 	return -ENOENT;
 }
@@ -302,10 +341,14 @@
 
 	hash = ipv6_addr_hash(addr);
 
+	read_lock_bh(&ipv6_mc_lock);
 	for (mc = inet6_mcast_lst[hash]; mc; mc=mc->next) {
-		if (mc->dev == dev && ipv6_addr_cmp(&mc->mca_addr, addr) == 0)
+		if (mc->dev == dev && ipv6_addr_cmp(&mc->mca_addr, addr) == 0) {
+			read_unlock_bh(&ipv6_mc_lock);
 			return 1;
+		}
 	}
+	read_unlock_bh(&ipv6_mc_lock);
 
 	return 0;
 }
@@ -364,11 +407,14 @@
 		if (idev == NULL)
 			return 0;
 
+		read_lock(&ipv6_mc_lock);
 		for (ma = idev->mc_list; ma; ma=ma->if_next)
 			igmp6_group_queried(ma, resptime);
+		read_unlock(&ipv6_mc_lock);
 	} else {
 		int hash = ipv6_addr_hash(addrp);
 
+		read_lock(&ipv6_mc_lock);
 		for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
 			if (ma->dev == skb->dev &&
 			    ipv6_addr_cmp(addrp, &ma->mca_addr) == 0) {
@@ -376,6 +422,7 @@
 				break;
 			}
 		}
+		read_unlock(&ipv6_mc_lock);
 	}
 
 	return 0;
@@ -410,6 +457,7 @@
 
 	hash = ipv6_addr_hash(addrp);
 
+	read_lock(&ipv6_mc_lock);
 	for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
 		if ((ma->dev == dev) && ipv6_addr_cmp(&ma->mca_addr, addrp) == 0) {
 			if (ma->mca_flags & MAF_TIMER_RUNNING) {
@@ -421,6 +469,7 @@
 			break;
 		}
 	}
+	read_unlock(&ipv6_mc_lock);
 
 	return 0;
 }
@@ -430,9 +479,9 @@
 	struct sock *sk = igmp6_socket->sk;
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
-	struct inet6_ifaddr *ifp;
 	struct in6_addr *snd_addr;
 	struct in6_addr *addrp;
+	struct in6_addr addr_buf;
 	struct in6_addr all_routers;
 	int err, len, payload_len, full_len;
 	u8 ra[8] = { IPPROTO_ICMPV6, 0,
@@ -461,9 +510,7 @@
 		dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, full_len);
 	}
 
-	ifp = ipv6_get_lladdr(dev);
-
-	if (ifp == NULL) {
+	if (ipv6_get_lladdr(dev, &addr_buf)) {
 #if MCAST_DEBUG >= 1
 		printk(KERN_DEBUG "igmp6: %s no linklocal address\n",
 		       dev->name);
@@ -471,7 +518,7 @@
 		return;
 	}
 
-	ip6_nd_hdr(sk, skb, dev, &ifp->addr, snd_addr, NEXTHDR_HOP, payload_len);
+	ip6_nd_hdr(sk, skb, dev, &addr_buf, snd_addr, NEXTHDR_HOP, payload_len);
 
 	memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
 
@@ -482,7 +529,7 @@
 	addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr));
 	ipv6_addr_copy(addrp, addr);
 
-	hdr->icmp6_cksum = csum_ipv6_magic(&ifp->addr, snd_addr, len,
+	hdr->icmp6_cksum = csum_ipv6_magic(&addr_buf, snd_addr, len,
 					   IPPROTO_ICMPV6,
 					   csum_partial((__u8 *) hdr, len, 0));
 
@@ -504,7 +551,6 @@
 	if ((addr_type & (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_LOOPBACK)))
 		return;
 
-	start_bh_atomic();
 	igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
 
 	delay = net_random() % IGMP6_UNSOLICITED_IVAL;
@@ -515,7 +561,6 @@
 
 	add_timer(&ma->mca_timer);
 	ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER;
-	end_bh_atomic();
 }
 
 static void igmp6_leave_group(struct ifmcaddr6 *ma)
@@ -527,22 +572,22 @@
 	if ((addr_type & IPV6_ADDR_LINKLOCAL))
 		return;
 
-	start_bh_atomic();
 	if (ma->mca_flags & MAF_LAST_REPORTER)
 		igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REDUCTION);
 
 	if (ma->mca_flags & MAF_TIMER_RUNNING)
 		del_timer(&ma->mca_timer);
-	end_bh_atomic();
 }
 
 void igmp6_timer_handler(unsigned long data)
 {
 	struct ifmcaddr6 *ma = (struct ifmcaddr6 *) data;
 
+	read_lock(&ipv6_mc_lock);
 	ma->mca_flags |=  MAF_LAST_REPORTER;
 	igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
 	ma->mca_flags &= ~MAF_TIMER_RUNNING;
+	read_unlock(&ipv6_mc_lock);
 }
 
 /* Device going down */
@@ -554,8 +599,10 @@
 
 	/* Withdraw multicast list */
 
+	read_lock_bh(&ipv6_mc_lock);
 	for (i = idev->mc_list; i; i=i->if_next)
 		igmp6_group_dropped(i);
+	read_unlock_bh(&ipv6_mc_lock);
 
 	/* Delete all-nodes address. */
 
@@ -577,8 +624,10 @@
 
 	/* Install multicast list, except for all-nodes (already installed) */
 
+	read_lock(&ipv6_mc_lock);
 	for (i = idev->mc_list; i; i=i->if_next)
 		igmp6_group_added(i);
+	read_unlock(&ipv6_mc_lock);
 }
 
 /*
@@ -590,6 +639,7 @@
 	int hash;
 	struct ifmcaddr6 *i, **lnk;
 
+	write_lock_bh(&ipv6_mc_lock);
 	while ((i = idev->mc_list) != NULL) {
 		idev->mc_list = i->if_next;
 
@@ -598,13 +648,13 @@
 		for (lnk = &inet6_mcast_lst[hash]; *lnk; lnk = &(*lnk)->next) {
 			if (*lnk == i) {
 				*lnk = i->next;
-				synchronize_bh();
 				break;
 			}
 		}
 		igmp6_group_dropped(i);
 		kfree(i);
 	}
+	write_unlock_bh(&ipv6_mc_lock);
 }
 
 #ifdef CONFIG_PROC_FS
@@ -616,13 +666,14 @@
 	int len=0;
 	struct device *dev;
 	
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev; dev = dev->next) {
 		struct inet6_dev *idev;
 
 		if ((idev = ipv6_get_idev(dev)) == NULL)
 			continue;
 
+		read_lock_bh(&ipv6_mc_lock);
 		for (im = idev->mc_list; im; im = im->if_next) {
 			int i;
 
@@ -642,14 +693,17 @@
 				len=0;
 				begin=pos;
 			}
-			if (pos > offset+length)
+			if (pos > offset+length) {
+				read_unlock_bh(&ipv6_mc_lock);
 				goto done;
+			}
 		}
+		read_unlock_bh(&ipv6_mc_lock);
 	}
 	*eof = 1;
 
 done:
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 
 	*start=buffer+(offset-begin);
 	len-=(offset-begin);
diff -u --recursive --new-file v2.3.5/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- v2.3.5/linux/net/ipv6/ndisc.c	Thu Apr 22 19:45:20 1999
+++ linux/net/ipv6/ndisc.c	Wed Jun  9 14:45:37 1999
@@ -268,14 +268,21 @@
 			ndisc_mc_map(daddr, ha, dev, 1);
 			h_dest = ha;
 		} else if (neigh) {
-			h_dest = neigh->ha;
+			read_lock_bh(&neigh->lock);
+			if (neigh->nud_state&NUD_VALID) {
+				memcpy(ha, neigh->ha, dev->addr_len);
+				h_dest = ha;
+			}
+			read_unlock_bh(&neigh->lock);
 		} else {
 			neigh = neigh_lookup(&nd_tbl, daddr, dev);
 			if (neigh) {
+				read_lock_bh(&neigh->lock);
 				if (neigh->nud_state&NUD_VALID) {
 					memcpy(ha, neigh->ha, dev->addr_len);
 					h_dest = ha;
 				}
+				read_unlock_bh(&neigh->lock);
 				neigh_release(neigh);
 			}
 		}
@@ -362,6 +369,7 @@
         struct sock *sk = ndisc_socket->sk;
         struct sk_buff *skb;
         struct nd_msg *msg;
+	struct in6_addr addr_buf;
         int len;
 	int err;
 
@@ -377,13 +385,8 @@
 	}
 
 	if (saddr == NULL) {
-		struct inet6_ifaddr *ifa;
-
-		/* use link local address */
-		ifa = ipv6_get_lladdr(dev);
-
-		if (ifa)
-			saddr = &ifa->addr;
+		if (!ipv6_get_lladdr(dev, &addr_buf))
+			saddr = &addr_buf;
 	}
 
 	if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
@@ -501,13 +504,15 @@
 	kfree_skb(skb);
 }
 
+/* Called with locked neigh: either read or both */
+
 static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
 {
 	struct in6_addr *saddr = NULL;
 	struct in6_addr mcaddr;
 	struct device *dev = neigh->dev;
 	struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
-	int probes = neigh->probes;
+	int probes = atomic_read(&neigh->probes);
 
 	if (skb && ipv6_chk_addr(&skb->nh.ipv6h->saddr, dev, 0))
 		saddr = &skb->nh.ipv6h->saddr;
@@ -774,8 +779,8 @@
 	struct sock *sk = ndisc_socket->sk;
 	int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
 	struct sk_buff *buff;
-	struct inet6_ifaddr *ifp;
 	struct icmp6hdr *icmph;
+	struct in6_addr saddr_buf;
 	struct in6_addr *addrp;
 	struct device *dev;
 	struct rt6_info *rt;
@@ -817,12 +822,10 @@
 	rd_len &= ~0x7;
 	len += rd_len;
 
-	ifp = ipv6_get_lladdr(dev);
-
-	if (ifp == NULL) {
-		ND_PRINTK1("redirect: no link_local addr for dev\n");
-		return;
-	}
+	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+ 		ND_PRINTK1("redirect: no link_local addr for dev\n");
+ 		return;
+ 	}
 
 	buff = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
 				   0, 0, &err);
@@ -838,7 +841,7 @@
 		return;
 	}
 
-	ip6_nd_hdr(sk, buff, dev, &ifp->addr, &skb->nh.ipv6h->saddr,
+	ip6_nd_hdr(sk, buff, dev, &saddr_buf, &skb->nh.ipv6h->saddr,
 		   IPPROTO_ICMPV6, len);
 
 	icmph = (struct icmp6hdr *) skb_put(buff, len);
@@ -875,7 +878,7 @@
 
 	memcpy(opt, skb->nh.ipv6h, rd_len - 8);
 
-	icmph->icmp6_cksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
+	icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &skb->nh.ipv6h->saddr,
 					     len, IPPROTO_ICMPV6,
 					     csum_partial((u8 *) icmph, len, 0));
 
@@ -1034,7 +1037,7 @@
 				   ifp->idev->dev->name);
 			return 0;
 		}
-		neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 0);
+		neigh = neigh_lookup(&nd_tbl, &msg->target, skb->dev);
 
 		if (neigh) {
 			if (neigh->flags & NTF_ROUTER) {
@@ -1083,11 +1086,10 @@
 	unsigned long now = jiffies;
 	int i;
 
-	neigh_table_lock(&nd_tbl);
-
 	for (i = 0; i <= NEIGH_HASHMASK; i++) {
 		struct neighbour *neigh;
 
+		read_lock_bh(&nd_tbl.lock);
 		for (neigh = nd_tbl.hash_buckets[i]; neigh; neigh = neigh->next) {
 			int j;
 
@@ -1097,6 +1099,7 @@
 				size += 2;
 			}
 
+			read_lock(&neigh->lock);
 			size += sprintf(buffer+len+size,
 				       " %02x %02x %02x %02x %08lx %08lx %08x %04x %04x %04x %8s ", i,
 				       128,
@@ -1118,19 +1121,22 @@
 			} else {
                                 size += sprintf(buffer+len+size, "000000000000");
 			}
+			read_unlock(&neigh->lock);
 			size += sprintf(buffer+len+size, "\n");
 			len += size;
 			pos += size;
 		  
 			if (pos <= offset)
 				len=0;
-			if (pos >= offset+length)
+			if (pos >= offset+length) {
+				read_unlock_bh(&nd_tbl.lock);
 				goto done;
+			}
 		}
+		read_unlock_bh(&nd_tbl.lock);
 	}
 
 done:
-	neigh_table_unlock(&nd_tbl);
 
 	*start = buffer+len-(pos-offset);	/* Start of wanted data */
 	len = pos-offset;			/* Start slop */
diff -u --recursive --new-file v2.3.5/linux/net/ipv6/raw.c linux/net/ipv6/raw.c
--- v2.3.5/linux/net/ipv6/raw.c	Mon May 31 22:28:07 1999
+++ linux/net/ipv6/raw.c	Wed Jun  9 14:45:37 1999
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/raw.c
  *
- *	$Id: raw.c,v 1.25 1999/05/27 00:38:16 davem Exp $
+ *	$Id: raw.c,v 1.26 1999/06/09 10:11:18 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -99,17 +99,6 @@
 	SOCKHASH_UNLOCK_WRITE();
 }
 
-static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
-{
-	struct ipv6_mc_socklist *mc;
-		
-	for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
-		if (ipv6_addr_cmp(&mc->addr, addr) == 0)
-			return 1;
-	}
-
-	return 0;
-}
 
 /* Grumble... icmp and ip_input want to get at this... */
 struct sock *raw_v6_lookup(struct sock *sk, unsigned short num,
diff -u --recursive --new-file v2.3.5/linux/net/ipv6/reassembly.c linux/net/ipv6/reassembly.c
--- v2.3.5/linux/net/ipv6/reassembly.c	Wed Jun  2 14:44:39 1999
+++ linux/net/ipv6/reassembly.c	Wed Jun  9 14:45:37 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: reassembly.c,v 1.11 1998/08/26 12:05:16 davem Exp $
+ *	$Id: reassembly.c,v 1.13 1999/06/09 08:29:40 davem Exp $
  *
  *	Based on: net/ipv4/ip_fragment.c
  *
diff -u --recursive --new-file v2.3.5/linux/net/ipv6/route.c linux/net/ipv6/route.c
--- v2.3.5/linux/net/ipv6/route.c	Sun Mar 21 07:22:00 1999
+++ linux/net/ipv6/route.c	Wed Jun  9 14:45:37 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: route.c,v 1.35 1999/03/21 05:22:57 davem Exp $
+ *	$Id: route.c,v 1.36 1999/06/09 10:11:21 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -1607,7 +1607,7 @@
 	return 0;
 }
 
-static int fib6_dump_done(struct netlink_callback *cb)
+static void fib6_dump_end(struct netlink_callback *cb)
 {
 	struct fib6_walker_t *w = (void*)cb->args[0];
 
@@ -1622,6 +1622,11 @@
 		cb->done = (void*)cb->args[1];
 		cb->args[1] = 0;
 	}
+}
+
+static int fib6_dump_done(struct netlink_callback *cb)
+{
+	fib6_dump_end(cb);
 	return cb->done(cb);
 }
 
@@ -1668,11 +1673,15 @@
 	if (res <= 0 && skb->len == 0)
 		RT6_TRACE("%p>dump end\n", w);
 #endif
+	res = res < 0 ? res : skb->len;
 	/* res < 0 is an error. (really, impossible)
 	   res == 0 means that dump is complete, but skb still can contain data.
 	   res > 0 dump is not complete, but frame is full.
 	 */
-	return res < 0 ? res : skb->len;
+	/* Destroy walker, if dump of this table is complete. */
+	if (res <= 0)
+		fib6_dump_end(cb);
+	return res;
 }
 
 int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
diff -u --recursive --new-file v2.3.5/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.3.5/linux/net/ipv6/tcp_ipv6.c	Mon May 31 22:28:07 1999
+++ linux/net/ipv6/tcp_ipv6.c	Wed Jun  9 14:45:37 1999
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.106 1999/05/27 01:12:44 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.108 1999/06/09 08:29:43 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
diff -u --recursive --new-file v2.3.5/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.3.5/linux/net/ipv6/udp.c	Mon May 31 22:28:07 1999
+++ linux/net/ipv6/udp.c	Wed Jun  9 14:45:37 1999
@@ -7,7 +7,7 @@
  *
  *	Based on linux/ipv4/udp.c
  *
- *	$Id: udp.c,v 1.41 1999/05/27 00:38:18 davem Exp $
+ *	$Id: udp.c,v 1.42 1999/06/09 10:11:24 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -499,18 +499,6 @@
 	}
   	ipv6_statistics.Ip6InDelivers++;
 	udp_stats_in6.UdpInDatagrams++;
-	return 0;
-}
-
-static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
-{
-	struct ipv6_mc_socklist *mc;
-		
-	for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
-		if (ipv6_addr_cmp(&mc->addr, addr) == 0)
-			return 1;
-	}
-
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.5/linux/net/irda/Config.in linux/net/irda/Config.in
--- v2.3.5/linux/net/irda/Config.in	Thu Apr 15 05:42:42 1999
+++ linux/net/irda/Config.in	Mon Jun  7 16:18:58 1999
@@ -2,34 +2,31 @@
 # IrDA protocol configuration
 #
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  if [ "$CONFIG_NET" != "n" ] ; then
+if [ "$CONFIG_NET" != "n" ] ; then
 
-    mainmenu_option next_comment
-    comment 'IrDA subsystem support'
-    dep_tristate 'IrDA subsystem support' CONFIG_IRDA $CONFIG_EXPERIMENTAL $CONFIG_NET
+  mainmenu_option next_comment
+  comment 'IrDA subsystem support'
+  dep_tristate 'IrDA subsystem support' CONFIG_IRDA $CONFIG_NET
 
-    if [ "$CONFIG_IRDA" != "n" ] ; then
-      comment 'IrDA protocols'
-      source net/irda/irlan/Config.in
-      source net/irda/ircomm/Config.in
-      source net/irda/irlpt/Config.in
+  if [ "$CONFIG_IRDA" != "n" ] ; then
+    comment 'IrDA protocols'
+    source net/irda/irlan/Config.in
+    source net/irda/ircomm/Config.in
+    source net/irda/irlpt/Config.in
 
-      bool 'IrDA protocol options' CONFIG_IRDA_OPTIONS
-      if [ "$CONFIG_IRDA_OPTIONS" != "n" ] ; then
-        comment '   IrDA options'
-        bool '   Cache last LSAP' CONFIG_IRDA_CACHE_LAST_LSAP
-	bool '   Fast RRs' CONFIG_IRDA_FAST_RR
-	bool '   Debug information' CONFIG_IRDA_DEBUG
-      fi
+    bool 'IrDA protocol options' CONFIG_IRDA_OPTIONS
+    if [ "$CONFIG_IRDA_OPTIONS" != "n" ] ; then
+      comment '   IrDA options'
+      bool '   Cache last LSAP' CONFIG_IRDA_CACHE_LAST_LSAP
+      bool '   Fast RRs' CONFIG_IRDA_FAST_RR
+      bool '   Debug information' CONFIG_IRDA_DEBUG
     fi
+  fi
 
-    if [ "$CONFIG_IRDA" != "n" ] ; then
-      source net/irda/compressors/Config.in
-      source drivers/net/irda/Config.in
-    fi
-  endmenu
-
+  if [ "$CONFIG_IRDA" != "n" ] ; then
+    source net/irda/compressors/Config.in
+    source drivers/net/irda/Config.in
   fi
+  endmenu
 fi
 
diff -u --recursive --new-file v2.3.5/linux/net/irda/af_irda.c linux/net/irda/af_irda.c
--- v2.3.5/linux/net/irda/af_irda.c	Mon May 31 22:28:07 1999
+++ linux/net/irda/af_irda.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun May 31 10:12:43 1998
- * Modified at:   Tue May 11 12:42:26 1999
+ * Modified at:   Wed May 19 16:12:06 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:       af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc.
  * 
@@ -145,7 +145,7 @@
 	else
 		self->max_data_size = max_sdu_size;
 
-	DEBUG(0, __FUNCTION__ "(), max_data_size=%d\n", self->max_data_size);
+	DEBUG(1, __FUNCTION__ "(), max_data_size=%d\n", self->max_data_size);
 
 	memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
 
@@ -189,7 +189,7 @@
 	else
 		self->max_data_size = max_sdu_size;
 
-	DEBUG(0, __FUNCTION__ "(), max_data_size=%d\n", self->max_data_size);
+	DEBUG(1, __FUNCTION__ "(), max_data_size=%d\n", self->max_data_size);
 
 	memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
 
@@ -250,12 +250,12 @@
 	
 	switch (flow) {
 	case FLOW_STOP:
-		DEBUG( 0, __FUNCTION__ "(), IrTTP wants us to slow down\n");
+		DEBUG(1, __FUNCTION__ "(), IrTTP wants us to slow down\n");
 		self->tx_flow = flow;
 		break;
 	case FLOW_START:
 		self->tx_flow = flow;
-		DEBUG(0, __FUNCTION__ "(), IrTTP wants us to start again\n");
+		DEBUG(1, __FUNCTION__ "(), IrTTP wants us to start again\n");
 		wake_up_interruptible(sk->sleep);
 		break;
 	default:
@@ -703,7 +703,11 @@
 
 	sock_init_data(sock, sk);
 
-	sock->ops    = &irda_stream_ops;
+	if (sock->type == SOCK_STREAM)
+		sock->ops = &irda_stream_ops;
+	else
+		sock->ops = &irda_dgram_ops;
+
 	sk->protocol = protocol;
 
 	/* Register as a client with IrLMP */
@@ -1123,7 +1127,7 @@
 {
 	struct sock *sk = sock->sk;
 
-	DEBUG(0, __FUNCTION__ "(), cmd=%#x\n", cmd);
+	DEBUG(4, __FUNCTION__ "(), cmd=%#x\n", cmd);
 	
 	switch (cmd) {
 	case TIOCOUTQ: {
@@ -1170,7 +1174,7 @@
 		return -EINVAL;
 		
 	default:
-		DEBUG(0, __FUNCTION__ "(), doing device ioctl!\n");
+		DEBUG(1, __FUNCTION__ "(), doing device ioctl!\n");
 		return dev_ioctl(cmd, (void *) arg);
 	}
 
diff -u --recursive --new-file v2.3.5/linux/net/irda/discovery.c linux/net/irda/discovery.c
--- v2.3.5/linux/net/irda/discovery.c	Wed Jun  2 14:44:39 1999
+++ linux/net/irda/discovery.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Apr  6 15:33:50 1999
- * Modified at:   Sun May  9 22:40:43 1999
+ * Modified at:   Fri May 28 20:46:38 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Modified at:   Fri May 28  3:11 CST 1999
  * Modified by:   Horst von Brand <vonbrand@sleipnir.valparaiso.cl>
@@ -227,10 +227,12 @@
 	
 	discovery = (discovery_t *) hashbin_get_first(cachelog);
 	while ( discovery != NULL) {
-		len += sprintf( buf+len, "  name: %s,", 
-				discovery->info);
+		len += sprintf(buf+len, "name: %s,", discovery->info);
 		
-		len += sprintf( buf+len, " hint: ");
+		len += sprintf(buf+len, " hint: 0x%02x%02x", 
+			       discovery->hints.byte[0], 
+			       discovery->hints.byte[1]);
+#if 0
 		if ( discovery->hints.byte[0] & HINT_PNP)
 			len += sprintf( buf+len, "PnP Compatible ");
 		if ( discovery->hints.byte[0] & HINT_PDA)
@@ -254,14 +256,14 @@
 			len += sprintf( buf+len, "IrCOMM ");
 		if ( discovery->hints.byte[1] & HINT_OBEX)
 			len += sprintf( buf+len, "IrOBEX ");
-		
+#endif		
 		len += sprintf(buf+len, ", saddr: 0x%08x", 
 			       discovery->saddr);
 
 		len += sprintf(buf+len, ", daddr: 0x%08x\n", 
 			       discovery->daddr);
 		
-		len += sprintf( buf+len, "\n");
+		len += sprintf(buf+len, "\n");
 		
 		discovery = (discovery_t *) hashbin_get_next(cachelog);
 	}
diff -u --recursive --new-file v2.3.5/linux/net/irda/ircomm/ircomm_common.c linux/net/irda/ircomm/ircomm_common.c
--- v2.3.5/linux/net/irda/ircomm/ircomm_common.c	Mon May 31 22:28:07 1999
+++ linux/net/irda/ircomm/ircomm_common.c	Mon Jun  7 16:18:58 1999
@@ -41,7 +41,7 @@
 
 #include <net/irda/ircomm_common.h>
 
-static char *revision_date = "Sun Apr 18 00:40:19 1999";
+static char *revision_date = "Tue May 18 03:11:39 1999";
 
 
 static void ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event, 
@@ -205,15 +205,17 @@
 		ircomm[i]->enq_char = 0x05;
 		ircomm[i]->ack_char = 0x06;  
 
-		ircomm[i]->max_txbuff_size = COMM_DEFAULT_DATA_SIZE;   /* 64 */
-		ircomm[i]->max_sdu_size = SAR_DISABLE;  
-		ircomm[i]->ctrl_skb = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE);
+		ircomm[i]->max_header_size = COMM_MAX_HEADER_SIZE;
+		ircomm[i]->tx_max_sdu_size = COMM_DEFAULT_SDU_SIZE;
+		ircomm[i]->rx_max_sdu_size = SAR_DISABLE;  
+		ircomm[i]->ctrl_skb = dev_alloc_skb(COMM_DEFAULT_SDU_SIZE
+						    + COMM_MAX_HEADER_SIZE);
 		if (ircomm[i]->ctrl_skb == NULL){
 			DEBUG(0,"ircomm:init_module:alloc_skb failed!\n");
 			return -ENOMEM;
 		}
 
-		skb_reserve(ircomm[i]->ctrl_skb,COMM_HEADER_SIZE);
+		skb_reserve(ircomm[i]->ctrl_skb,COMM_MAX_HEADER_SIZE);
 
 	}
 
@@ -302,14 +304,16 @@
 	DEBUG(0,__FUNCTION__"(): got connected!\n");
 
 	if (max_sdu_size == SAR_DISABLE)
-		self->max_txbuff_size = qos->data_size.value - max_header_size;
+		self->tx_max_sdu_size =(qos->data_size.value - max_header_size
+					- COMM_HEADER_SIZE);
 	else {
-		ASSERT(max_sdu_size >= COMM_DEFAULT_DATA_SIZE, return;);
-		self->max_txbuff_size = max_sdu_size; /* use fragmentation */
+		ASSERT(max_sdu_size >= COMM_DEFAULT_SDU_SIZE, return;);
+		/* use fragmentation */
+		self->tx_max_sdu_size = max_sdu_size - COMM_HEADER_SIZE;
 	}
 
 	self->qos = qos;
-	self->max_header_size = max_header_size;
+	self->max_header_size = max_header_size + COMM_HEADER_SIZE;
 	self->null_modem_mode = 0;         /* disable null modem emulation */
 
 	ircomm_do_event(self, TTP_CONNECT_CONFIRM, skb);
@@ -331,12 +335,13 @@
 	DEBUG(0,__FUNCTION__"()\n");
 
 	if (max_sdu_size == SAR_DISABLE)
-		self->max_txbuff_size = qos->data_size.value - max_header_size;
+		self->tx_max_sdu_size =(qos->data_size.value - max_header_size
+					- COMM_HEADER_SIZE);
 	else
-		self->max_txbuff_size = max_sdu_size;
+		self->tx_max_sdu_size = max_sdu_size - COMM_HEADER_SIZE;
 
 	self->qos = qos;
-	self->max_header_size = max_header_size;
+	self->max_header_size = max_header_size + COMM_HEADER_SIZE;
 
 	ircomm_do_event( self, TTP_CONNECT_INDICATION, skb);
 
@@ -558,7 +563,7 @@
 
 		irttp_connect_request(self->tsap, self->dlsap, 
 				      self->saddr, self->daddr, 
-				      NULL, self->max_sdu_size, userdata); 
+				      NULL, self->rx_max_sdu_size, userdata); 
 		break;
 
 	default:
@@ -592,7 +597,8 @@
 
 	if (self->notify.connect_indication)
 		self->notify.connect_indication(self->notify.instance, self, 
-						qos, 0, 0, skb);
+						qos, self->tx_max_sdu_size,
+						self->max_header_size, skb);
 }
     
 #if 0
@@ -611,7 +617,8 @@
 	/* give a connect_confirm to the client */
 	if( self->notify.connect_confirm )
 		self->notify.connect_confirm(self->notify.instance,
-					     self, NULL, SAR_DISABLE, 0, skb);
+					     self, NULL, self->tx_max_sdu_size,
+					     self->max_header_size, skb);
 }
 
 static void issue_connect_response(struct ircomm_cb *self,
@@ -623,7 +630,7 @@
 		DEBUG(0,__FUNCTION__"():THREE_WIRE_RAW is not implemented yet\n");
 		/* irlmp_connect_rsp(); */
 	} else
-		irttp_connect_response(self->tsap, self->max_sdu_size, skb);
+		irttp_connect_response(self->tsap, self->rx_max_sdu_size, skb);
 }
 
 static void issue_disconnect_request(struct ircomm_cb *self,
@@ -1054,7 +1061,7 @@
 
 	hints = irlmp_service_to_hint(S_COMM);
 
-	DEBUG(0,__FUNCTION__"():start discovering..\n");
+	DEBUG(1,__FUNCTION__"():start discovering..\n");
 	switch (ircomm_cs) {
 	case 0:
 		MOD_INC_USE_COUNT;
@@ -1155,12 +1162,12 @@
 	ASSERT( self->magic == IRCOMM_MAGIC, return;);
 
 
-	DEBUG(0, __FUNCTION__"():sending connect_request...\n");
+	DEBUG(1, __FUNCTION__"():sending connect_request...\n");
 
 	self->servicetype= servicetype;
 	/* ircomm_control_request(self, SERVICETYPE); */ /*servictype*/
 
-	self->max_sdu_size = SAR_DISABLE;
+	self->rx_max_sdu_size = SAR_DISABLE;
 	ircomm_do_event(self, IRCOMM_CONNECT_REQUEST, NULL);
 }
 
@@ -1181,20 +1188,18 @@
 
 	if (!userdata){
 		/* FIXME: check for errors and initialize? DB */
-		userdata = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE);
+		userdata = dev_alloc_skb(COMM_DEFAULT_SDU_SIZE + COMM_MAX_HEADER_SIZE);
 		if (userdata == NULL)
 			return;
 
-		skb_reserve(userdata,COMM_HEADER_SIZE);
+		skb_reserve(userdata,COMM_MAX_HEADER_SIZE);
 	}
 
 	/* enable null-modem emulation (i.e. server mode )*/
 	self->null_modem_mode = 1;
 
-	self->max_sdu_size = max_sdu_size;
-	if (max_sdu_size != SAR_DISABLE)
-		self->max_txbuff_size = max_sdu_size;
-
+	self->rx_max_sdu_size = max_sdu_size;
+	
 	ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata);
 }	
 
@@ -1307,10 +1312,10 @@
 	ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb);
 	self->control_ch_pending = 0;
 
-	skb = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE);
+	skb = dev_alloc_skb(COMM_DEFAULT_SDU_SIZE + COMM_MAX_HEADER_SIZE);
 	ASSERT(skb != NULL, return ;);
 
-	skb_reserve(skb,COMM_HEADER_SIZE);
+	skb_reserve(skb,COMM_MAX_HEADER_SIZE);
 	self->ctrl_skb = skb;
 }
 
diff -u --recursive --new-file v2.3.5/linux/net/irda/ircomm/irvtd_driver.c linux/net/irda/ircomm/irvtd_driver.c
--- v2.3.5/linux/net/irda/ircomm/irvtd_driver.c	Mon May 31 22:28:07 1999
+++ linux/net/irda/ircomm/irvtd_driver.c	Mon Jun  7 16:18:58 1999
@@ -51,7 +51,7 @@
 static int irvtd_refcount;
 struct irvtd_cb **irvtd = NULL;
 
-static char *revision_date = "Sun Apr 18 17:31:53 1999";
+static char *revision_date = "Wed May 26 00:49:11 1999";
 
 
 /*
@@ -83,8 +83,10 @@
 static void irvtd_send_xchar(struct tty_struct *tty, char ch);
 static void irvtd_wait_until_sent(struct tty_struct *tty, int timeout);
 
-static void irvtd_start_timer( struct irvtd_cb *driver);
-static void irvtd_timer_expired(unsigned long data);
+static void irvtd_start_tx_timer( struct irvtd_cb *driver, int timeout);
+static void irvtd_tx_timer_expired(unsigned long data);
+static void irvtd_start_rx_timer( struct irvtd_cb *driver, int timeout);
+static void irvtd_rx_timer_expired(unsigned long data);
 
 static int line_info(char *buf, struct irvtd_cb *driver);
 static int irvtd_read_proc(char *buf, char **start, off_t offset, int len,
@@ -118,7 +120,7 @@
 	if(driver->rx_disable)
 		return;
 
-	skb = skb_dequeue(&driver->rxbuff);
+ 	skb = skb_dequeue(&driver->rxbuff);
 	if(skb == NULL)
 		return; /* there's nothing */
 
@@ -211,8 +213,13 @@
 	
 	if(skb_queue_len(&driver->rxbuff)< IRVTD_RX_QUEUE_LOW &&
 	   driver->ttp_stoprx){
-		irttp_flow_request(driver->comm->tsap, FLOW_START);
+		DEBUG(1, __FUNCTION__"():FLOW_START\n");
+		/* 
+		 * next 2 lines must follow this order since irttp_flow_request()
+		 * will run its rx queue
+		 */
 		driver->ttp_stoprx = 0;
+		irttp_flow_request(driver->comm->tsap, FLOW_START);
 	}
 
 	if(skb_queue_empty(&driver->rxbuff) && driver->disconnect_pend){
@@ -236,10 +243,14 @@
 	skb_queue_tail( &driver->rxbuff, skb );
 
 	if(skb_queue_len(&driver->rxbuff) > IRVTD_RX_QUEUE_HIGH){
+		DEBUG(1, __FUNCTION__"():FLOW_STOP\n");
 		irttp_flow_request(driver->comm->tsap, FLOW_STOP);
 		driver->ttp_stoprx = 1;
 	}
 	irvtd_write_to_tty(driver);
+
+	if(!skb_queue_empty(&driver->rxbuff))
+		irvtd_start_rx_timer(driver,0);
 	return 0;
 }
 
@@ -255,22 +266,36 @@
  */
 
 
-static void irvtd_start_timer( struct irvtd_cb *driver)
+static void irvtd_start_tx_timer( struct irvtd_cb *driver, int timeout)
+{
+	ASSERT( driver != NULL, return;);
+	ASSERT( driver->magic == IRVTD_MAGIC, return;);
+
+	del_timer( &driver->tx_timer);
+	
+	driver->tx_timer.data     = (unsigned long) driver;
+	driver->tx_timer.function = &irvtd_tx_timer_expired;
+	driver->tx_timer.expires  = jiffies + timeout;
+	
+	add_timer( &driver->tx_timer);
+}
+
+static void irvtd_start_rx_timer( struct irvtd_cb *driver, int timeout)
 {
 	ASSERT( driver != NULL, return;);
 	ASSERT( driver->magic == IRVTD_MAGIC, return;);
 
-	del_timer( &driver->timer);
+	del_timer( &driver->rx_timer);
 	
-	driver->timer.data     = (unsigned long) driver;
-	driver->timer.function = &irvtd_timer_expired;
-	driver->timer.expires  = jiffies + (HZ / 5);  /* 200msec */
+	driver->rx_timer.data     = (unsigned long) driver;
+	driver->rx_timer.function = &irvtd_rx_timer_expired;
+	driver->rx_timer.expires  = jiffies + timeout;
 	
-	add_timer( &driver->timer);
+	add_timer( &driver->rx_timer);
 }
 
 
-static void irvtd_timer_expired(unsigned long data)
+static void irvtd_tx_timer_expired(unsigned long data)
 {
 	struct irvtd_cb *driver = (struct irvtd_cb *)data;
 
@@ -279,11 +304,26 @@
 	DEBUG(4, __FUNCTION__"()\n");
 
 	irvtd_send_data_request(driver);
+}
 
-	irvtd_write_to_tty(driver);
+static void irvtd_rx_timer_expired(unsigned long data)
+{
+	struct irvtd_cb *driver = (struct irvtd_cb *)data;
+
+	ASSERT(driver != NULL,return;);
+	ASSERT(driver->magic == IRVTD_MAGIC,return;);
+	DEBUG(4, __FUNCTION__"()\n");
 
-	/* start our timer again and again */
-	irvtd_start_timer(driver);
+	while(TTY_FLIPBUF_SIZE - driver->tty->flip.count
+	      && !skb_queue_empty(&driver->rxbuff))
+		irvtd_write_to_tty(driver);
+
+	DEBUG(1, __FUNCTION__"(): room in flip_buffer = %d\n",
+	      TTY_FLIPBUF_SIZE - driver->tty->flip.count);
+	
+	if(!skb_queue_empty(&driver->rxbuff))
+		/* handle it later */
+		irvtd_start_rx_timer(driver, 1);
 }
 
 
@@ -310,21 +350,23 @@
 	}
 #endif
 
-	DEBUG(1, __FUNCTION__"():sending %d octets\n",(int)skb->len );
+	DEBUG(1, __FUNCTION__"():len = %d, room = %d\n",(int)skb->len,
+	      skb_tailroom(skb));
 	driver->icount.tx += skb->len;
 	err = ircomm_data_request(driver->comm, driver->txbuff);
 	if (err){
 		ASSERT(err == 0,;);
-		DEBUG(0,"%d chars are lost\n",(int)skb->len);
+		DEBUG(1,"%d chars are lost\n",(int)skb->len);
 		skb_trim(skb, 0);
 	}
 
 	/* allocate a new frame */
-	skb = driver->txbuff = dev_alloc_skb(driver->comm->max_txbuff_size);
+	skb = driver->txbuff 
+		= dev_alloc_skb(driver->tx_max_sdu_size + driver->max_header_size);
 	if (skb == NULL){
 		printk(__FUNCTION__"():alloc_skb failed!\n");
 	} else {
-		skb_reserve(skb, COMM_HEADER_SIZE);
+		skb_reserve(skb, driver->max_header_size);
 	}
 
 	wake_up_interruptible(&driver->tty->write_wait);
@@ -355,6 +397,9 @@
 	ASSERT(driver != NULL, return;);
 	ASSERT(driver->magic == IRVTD_MAGIC, return;);
 
+
+	driver->tx_max_sdu_size = max_sdu_size;
+	driver->max_header_size = max_header_size;
 	/*
 	 * set default value
 	 */
@@ -408,6 +453,8 @@
 	ASSERT(comm != NULL, return;);
 	ASSERT(comm->magic == IRCOMM_MAGIC, return;);
 
+	driver->tx_max_sdu_size = max_sdu_size;
+	driver->max_header_size = max_header_size;
 	DEBUG(4, __FUNCTION__ "():sending connect_response...\n");
 
 	ircomm_connect_response(comm, NULL, SAR_DISABLE );
@@ -481,11 +528,12 @@
 	if(cmd == TX_READY){
 		driver->ttp_stoptx = 0;
 		driver->tty->hw_stopped = driver->cts_stoptx;
-		irvtd_start_timer( driver);
 
 		if(driver->cts_stoptx)
 			return;
 
+		/* push tx queue so that client can send at least 1 octet */
+		irvtd_send_data_request(driver);
 		/* 
 		 * driver->tty->write_wait will keep asleep if
 		 * our txbuff is full.
@@ -500,7 +548,7 @@
 
 	if(cmd == TX_BUSY){
 		driver->ttp_stoptx = driver->tty->hw_stopped = 1;
-		del_timer( &driver->timer);
+		del_timer( &driver->tx_timer);
 		return;
 	}
 
@@ -681,7 +729,7 @@
 
 	driver->blocked_open--;
 
-	DEBUG(0, __FUNCTION__"():after blocking\n");
+	DEBUG(1, __FUNCTION__"():after blocking\n");
 
 	if (retval)
 		return retval;
@@ -768,7 +816,7 @@
 	struct notify_t irvtd_notify;
 
 	/* FIXME: it should not be hard coded */
-	__u8 oct_seq[6] = { 0,1,4,1,1,1 }; 
+	__u8 oct_seq[6] = { 0,1,6,1,1,1 }; 
 
 	DEBUG(4,__FUNCTION__"()\n" );
 	if(driver->flags & ASYNC_INITIALIZED)
@@ -779,12 +827,12 @@
 	 */
 
 	skb_queue_head_init(&driver->rxbuff);
-	driver->txbuff = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE); 
+	driver->txbuff = dev_alloc_skb(COMM_DEFAULT_SDU_SIZE + COMM_MAX_HEADER_SIZE); 
 	if (!driver->txbuff){
 		DEBUG(0,__FUNCTION__"(), alloc_skb failed!\n");
 		return -ENOMEM;
 	}
-	skb_reserve(driver->txbuff, COMM_HEADER_SIZE);
+	skb_reserve(driver->txbuff, COMM_MAX_HEADER_SIZE);
 
 	irda_notify_init(&irvtd_notify);
 	irvtd_notify.data_indication = irvtd_receive_data;
@@ -813,22 +861,20 @@
 
 	driver->flags |= ASYNC_INITIALIZED;
 
-	/*
-	 * discover a peer device
-	 *	   TODO: other servicetype(i.e. 3wire,3wireraw) support
-	 */
-	ircomm_connect_request(driver->comm, NINE_WIRE);
-	
-	/*
-	 * TODO:we have to initialize control-channel here!
-	 *   i.e.set something into RTS,CTS and so on....
-	 */
-
 	if (driver->tty)
 		clear_bit(TTY_IO_ERROR, &driver->tty->flags);
 
 	change_speed(driver);
-	irvtd_start_timer( driver);
+
+	/*
+	 * discover a peer device
+	 */
+	if(driver->tty->termios->c_cflag & CRTSCTS)
+	  ircomm_connect_request(driver->comm, NINE_WIRE);
+	else
+	  ircomm_connect_request(driver->comm, THREE_WIRE);
+
+	/* irvtd_start_timer( driver); */
 
 	driver->rx_disable = 0;
 	driver->tx_disable = 1;
@@ -991,7 +1037,8 @@
 	if (driver->tty)
 		set_bit(TTY_IO_ERROR, &driver->tty->flags);
 	
-	del_timer( &driver->timer);
+	del_timer( &driver->tx_timer);
+	del_timer( &driver->rx_timer);
 
 	irias_delete_object("IrDA:IrCOMM");
 
@@ -1146,13 +1193,21 @@
 	DEBUG(4, __FUNCTION__"()\n");
 
 	save_flags(flags);
-	while(1){
+	while(count > 0){
 		cli();
 		skb = driver->txbuff;
 		ASSERT(skb != NULL, break;);
 		c = MIN(count, (skb_tailroom(skb)));
 		if (c <= 0)
-			break;
+		{
+			if(!driver->ttp_stoptx)
+			{
+				irvtd_send_data_request(driver);
+				continue;
+			}
+			else
+				break;
+		}
 
 		/* write to the frame */
 
@@ -1166,9 +1221,9 @@
 		wrote += c;
 		count -= c;
 		buf += c;
-		irvtd_send_data_request(driver);
 	}
 	restore_flags(flags);
+	irvtd_send_data_request(driver);
 	return (wrote);
 }
 
@@ -1201,19 +1256,27 @@
 	DEBUG(4, __FUNCTION__"()\n");
 
 
+ again:
 	save_flags(flags);cli();
 	skb = driver->txbuff;
 	ASSERT(skb != NULL,return;);
+	if(!skb_tailroom(skb))
+	{
+		restore_flags(flags);
+		irvtd_send_data_request(driver);
+		goto again;
+	}
 	ASSERT(skb_tailroom(skb) > 0, return;);
-	DEBUG(4, "irvtd_put_char(0x%02x) skb_len(%d) MAX(%d):\n",
+	DEBUG(4, "irvtd_put_char(0x%02x) skb_len(%d) room(%d):\n",
 	      (int)ch ,(int)skb->len,
-	      driver->comm->max_txbuff_size - COMM_HEADER_SIZE);
+	      skb_tailroom(skb));
 
 	/* append a character  */
 	frame = skb_put(skb,1);
 	frame[0] = ch;
 
 	restore_flags(flags);
+	irvtd_start_tx_timer(driver,20);
 	return;
 }
 
@@ -1637,6 +1700,7 @@
 	driver->comm->dte = driver->mcr;
 	ircomm_control_request(driver->comm, DTELINE_STATE );
 
+	DEBUG(1, __FUNCTION__"():FLOW_STOP\n");
         irttp_flow_request(driver->comm->tsap, FLOW_STOP);
 }
 
@@ -1651,6 +1715,7 @@
 	driver->comm->dte = driver->mcr;
 	ircomm_control_request(driver->comm, DTELINE_STATE );
 
+	DEBUG(1, __FUNCTION__"():FLOW_START\n");
         irttp_flow_request(driver->comm->tsap, FLOW_START);
 }
 
@@ -1860,6 +1925,12 @@
                 ret += sprintf(buf+ret, "|CD");
 	if (driver->msr & MSR_RI) 
 		ret += sprintf(buf+ret, "|RI");
+
+	ret += sprintf(buf+ret, "\n");
+	ret += sprintf(buf+ret, "rx queue:%d",
+		       skb_queue_len( &driver->rxbuff));
+	ret += sprintf(buf+ret, "ttp_stoprx:%s",
+		       driver->ttp_stoprx?"TRUE":"FALSE");
 
  exit:
 	ret += sprintf(buf+ret, "\n");
diff -u --recursive --new-file v2.3.5/linux/net/irda/irda_device.c linux/net/irda/irda_device.c
--- v2.3.5/linux/net/irda/irda_device.c	Wed Jun  2 14:44:39 1999
+++ linux/net/irda/irda_device.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Wed Sep  2 20:22:08 1998
- * Modified at:   Mon May 10 23:02:47 1999
+ * Modified at:   Tue Jun  1 09:05:13 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Modified at:   Fri May 28  3:11 CST 1999
  * Modified by:   Horst von Brand <vonbrand@sleipnir.valparaiso.cl>
@@ -105,6 +105,12 @@
 #ifdef CONFIG_NSC_FIR
 	pc87108_init();
 #endif
+#ifdef CONFIG_TOSHIBA_FIR
+	toshoboe_init();
+#endif
+#ifdef CONFIG_SMC_IRCC_FIR
+	ircc_init();
+#endif
 #ifdef CONFIG_ESI_DONGLE
 	esi_init();
 #endif
@@ -117,6 +123,10 @@
 #ifdef CONFIG_GIRBIL_DONGLE
 	girbil_init();
 #endif
+#ifdef CONFIG_GIRBIL_DONGLE
+	litelink_init();
+#endif
+
 	return 0;
 }
 
@@ -169,6 +179,8 @@
 	/* Initialize timers */
 	init_timer(&self->media_busy_timer);	
 
+	self->lock = SPIN_LOCK_UNLOCKED;
+
 	/* A pointer to the low level implementation */
 	self->priv = priv;
 
@@ -200,7 +212,7 @@
 	/* Open network device */
 	dev_open(&self->netdev);
 
-	MESSAGE("IrDA: Registred device %s\n", self->name);
+	MESSAGE("IrDA: Registered device %s\n", self->name);
 
 	irda_device_set_media_busy(self, FALSE);
 
@@ -307,6 +319,8 @@
  */
 static void __irda_device_change_speed(struct irda_device *self, int speed)
 {
+	int n = 0;
+
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRDA_DEVICE_MAGIC, return;);
 	
@@ -314,22 +328,37 @@
 	 *  Is is possible to change speed yet? Wait until the last byte 
 	 *  has been transmitted.
 	 */
-	if (self->wait_until_sent) {
-		self->wait_until_sent(self);
-		
-		if (self->dongle)
-			self->dongle->change_speed(self, speed);
-		
-		if (self->change_speed) {
-			self->change_speed(self, speed);
-			
-			/* Update the QoS value only */
-			self->qos.baud_rate.value = speed;
+	if (!self->wait_until_sent) {
+		ERROR("IrDA: wait_until_sent() "
+		      "has not implemented by the IrDA device driver!\n");
+		return;
+	}
+
+	/* Make sure all transmitted data has actually been sent */
+	self->wait_until_sent(self);
+
+	/* Make sure nobody tries to transmit during the speed change */
+	while (irda_lock((void *) &self->netdev.tbusy) == FALSE) {
+		WARNING(__FUNCTION__ "(), device locked!\n");
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(MSECS_TO_JIFFIES(10));
+
+		if (n++ > 10) {
+			WARNING(__FUNCTION__ "(), breaking loop!\n");
+			break;
 		}
-	} else {
-		WARNING("IrDA: wait_until_sent() "
-			"has not implemented by the IrDA device driver!\n");
 	}
+	
+	if (self->dongle)
+		self->dongle->change_speed(self, speed);
+	
+	if (self->change_speed) {
+		self->change_speed(self, speed);
+		
+		/* Update the QoS value only */
+		self->qos.baud_rate.value = speed;
+	}
+	self->netdev.tbusy = FALSE;
 }
 
 /*
@@ -340,8 +369,6 @@
  */
 inline void irda_device_change_speed(struct irda_device *self, int speed)
 {
-	DEBUG(4, __FUNCTION__ "()\n");
-
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRDA_DEVICE_MAGIC, return;);
 
@@ -352,27 +379,27 @@
 
 inline int irda_device_is_media_busy( struct irda_device *self)
 {
-	ASSERT( self != NULL, return FALSE;);
-	ASSERT( self->magic == IRDA_DEVICE_MAGIC, return FALSE;);
+	ASSERT(self != NULL, return FALSE;);
+	ASSERT(self->magic == IRDA_DEVICE_MAGIC, return FALSE;);
 	
 	return self->media_busy;
 }
 
 inline int irda_device_is_receiving( struct irda_device *self)
 {
-	ASSERT( self != NULL, return FALSE;);
-	ASSERT( self->magic == IRDA_DEVICE_MAGIC, return FALSE;);
+	ASSERT(self != NULL, return FALSE;);
+	ASSERT(self->magic == IRDA_DEVICE_MAGIC, return FALSE;);
 
-	if ( self->is_receiving)
-		return self->is_receiving( self);
+	if (self->is_receiving)
+		return self->is_receiving(self);
 	else
 		return FALSE;
 }
 
-inline struct qos_info *irda_device_get_qos( struct irda_device *self)
+inline struct qos_info *irda_device_get_qos(struct irda_device *self)
 {
-	ASSERT( self != NULL, return NULL;);
-	ASSERT( self->magic == IRDA_DEVICE_MAGIC, return NULL;);
+	ASSERT(self != NULL, return NULL;);
+	ASSERT(self->magic == IRDA_DEVICE_MAGIC, return NULL;);
 
 	return &self->qos;
 }
@@ -394,8 +421,6 @@
 {
 	struct irda_device *self;
 
-	DEBUG(4, __FUNCTION__ "()\n");
-
 	ASSERT(dev != NULL, return -1;);
 
 	self = (struct irda_device *) dev->priv;
@@ -467,6 +492,8 @@
      return 0;  
 }
 
+
+#define SIOCSDONGLE     SIOCDEVPRIVATE
 static int irda_device_net_ioctl(struct device *dev, /* ioctl device */
 				 struct ifreq *rq,   /* Data passed */
 				 int	cmd)	     /* Ioctl number */
@@ -577,6 +604,10 @@
 #endif 
 		break;
 #endif
+	case SIOCSDONGLE: /* Set dongle */
+		/* Initialize dongle */
+		irda_device_init_dongle(self, (int) rq->ifr_data);
+		break;
 	default:
 		ret = -EOPNOTSUPP;
 	}
@@ -652,6 +683,11 @@
 		ERROR("IrDA: Unable to find requested dongle\n");
 		return;
 	}
+	
+	/* Check if we're already using a dongle */
+	if (self->dongle) {
+		self->dongle->close(self);
+	}
 
 	/* Set the dongle to be used by this driver */
 	self->dongle = node->dongle;
@@ -661,7 +697,7 @@
 	node->dongle->qos_init(self, &self->qos);
 	
 	/* Reset dongle */
-	node->dongle->reset(self, 0);
+	node->dongle->reset(self);
 
 	/* Set to default baudrate */
 	irda_device_change_speed(self, 9600);
diff -u --recursive --new-file v2.3.5/linux/net/irda/irlan/irlan_client.c linux/net/irda/irlan/irlan_client.c
--- v2.3.5/linux/net/irda/irlan/irlan_client.c	Mon May 31 22:28:07 1999
+++ linux/net/irda/irlan/irlan_client.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Tue May 11 00:22:39 1999
+ * Modified at:   Mon May 31 14:19:34 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:       skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
  *                slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
@@ -213,7 +213,7 @@
 {
 	struct irlan_cb *self;
 	
-	DEBUG(4, __FUNCTION__ "()\n");
+	DEBUG(2, __FUNCTION__ "()\n");
 	
 	self = (struct irlan_cb *) instance;
 	
@@ -222,6 +222,12 @@
 	ASSERT(skb != NULL, return -1;);
 	
 	irlan_do_client_event(self, IRLAN_DATA_INDICATION, skb); 
+
+	/* Ready for a new command */
+	self->client.tx_busy = FALSE;
+
+	/* Check if we have some queued commands waiting to be sent */
+	irlan_run_ctrl_tx_queue(self);
 
 	return 0;
 }
diff -u --recursive --new-file v2.3.5/linux/net/irda/irlan/irlan_client_event.c linux/net/irda/irlan/irlan_client_event.c
--- v2.3.5/linux/net/irda/irlan/irlan_client_event.c	Mon May 31 22:28:07 1999
+++ linux/net/irda/irlan/irlan_client_event.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Thu May  6 13:42:38 1999
+ * Modified at:   Fri May 14 23:08:15 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
diff -u --recursive --new-file v2.3.5/linux/net/irda/irlan/irlan_common.c linux/net/irda/irlan/irlan_common.c
--- v2.3.5/linux/net/irda/irlan/irlan_common.c	Mon May 31 22:28:07 1999
+++ linux/net/irda/irlan/irlan_common.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:37 1997
- * Modified at:   Sun May  9 11:48:49 1999
+ * Modified at:   Mon May 31 14:25:19 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>, 
@@ -301,7 +301,9 @@
 	init_timer(&self->client.kick_timer);
 
 	hashbin_insert(irlan, (QUEUE *) self, daddr, NULL);
-		
+	
+	skb_queue_head_init(&self->client.txq);
+	
 	irlan_next_client_state(self, IRLAN_IDLE);
 	irlan_next_provider_state(self, IRLAN_IDLE);
 
@@ -321,7 +323,7 @@
  */
 static void __irlan_close(struct irlan_cb *self)
 {
-	DEBUG(0, __FUNCTION__ "()\n");
+	DEBUG(2, __FUNCTION__ "()\n");
 	
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -423,8 +425,6 @@
 {
 	struct irlan_cb *self;
 
-	DEBUG(2, __FUNCTION__ "()\n");
-
 	self = (struct irlan_cb *) instance;
 
 	ASSERT(self != NULL, return;);
@@ -444,9 +444,15 @@
 	 */
 	irlan_get_unicast_addr(self);
 	irlan_open_unicast_addr(self);
+	
+	/* Open broadcast and multicast filter by default */
+ 	irlan_set_broadcast_filter(self, TRUE);
+ 	irlan_set_multicast_filter(self, TRUE);
 
 	/* Ready to transfer Ethernet frames */
 	self->dev.tbusy = 0;
+
+	irlan_eth_send_gratuitous_arp(&self->dev);
 }
 
 /*
@@ -495,9 +501,6 @@
 		break;
 	}
 	
-	/* Stop IP from transmitting more packets */
-	/* irlan_client_flow_indication(handle, FLOW_STOP, priv); */
-
 	irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
 	irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
 }
@@ -507,7 +510,7 @@
 	struct notify_t notify;
 	struct tsap_cb *tsap;
 
-	DEBUG(0, __FUNCTION__ "()\n");
+	DEBUG(2, __FUNCTION__ "()\n");
 
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -522,7 +525,7 @@
 	notify.udata_indication      = irlan_eth_receive;
 	notify.connect_indication    = irlan_connect_indication;
 	notify.connect_confirm       = irlan_connect_confirm;
- 	notify.flow_indication       = irlan_eth_flow_indication;
+ 	/*notify.flow_indication       = irlan_eth_flow_indication;*/
 	notify.disconnect_indication = irlan_disconnect_indication;
 	notify.instance              = self;
 	strncpy(notify.name, "IrLAN data", NOTIFY_MAX_NAME);
@@ -555,7 +558,6 @@
 		irttp_disconnect_request(self->tsap_data, NULL, P_NORMAL);
 		irttp_close_tsap(self->tsap_data);
 		self->tsap_data = NULL;
-		
 	}
 	if (self->client.tsap_ctrl) {
 		irttp_disconnect_request(self->client.tsap_ctrl, NULL, 
@@ -608,15 +610,60 @@
 		irias_add_string_attrib(obj, "Name", "Linux");
 #endif
 		irias_add_string_attrib(obj, "DeviceID", "HWP19F0");
-		irias_add_integer_attrib(obj, "CompCnt", 2);
-		irias_add_string_attrib(obj, "Comp#01", "PNP8294");
-		irias_add_string_attrib(obj, "Comp#02", "PNP8389");
+		irias_add_integer_attrib(obj, "CompCnt", 1);
+		if (self->provider.access_type == ACCESS_PEER)
+			irias_add_string_attrib(obj, "Comp#02", "PNP8389");
+		else
+			irias_add_string_attrib(obj, "Comp#01", "PNP8294");
+
 		irias_add_string_attrib(obj, "Manufacturer", "Linux-IrDA Project");
 		irias_insert_object(obj);
 	}
 }
 
 /*
+ * Function irlan_run_ctrl_tx_queue (self)
+ *
+ *    Try to send the next command in the control transmit queue
+ *
+ */
+int irlan_run_ctrl_tx_queue(struct irlan_cb *self)
+{
+	struct sk_buff *skb;
+
+	if (irda_lock(&self->client.tx_busy) == FALSE)
+		return -EBUSY;
+
+	skb = skb_dequeue(&self->client.txq);
+	if (!skb) {
+		self->client.tx_busy = FALSE;
+		return 0;
+	}
+	if (self->client.tsap_ctrl == NULL) {
+		self->client.tx_busy = FALSE;
+		dev_kfree_skb(skb);
+		return -1;
+	}
+
+	return irttp_data_request(self->client.tsap_ctrl, skb);
+}
+
+/*
+ * Function irlan_ctrl_data_request (self, skb)
+ *
+ *    This function makes sure that commands on the control channel is being
+ *    sent in a command/response fashion
+ */
+void irlan_ctrl_data_request(struct irlan_cb *self, struct sk_buff *skb)
+{
+	/* Queue command */
+	skb_queue_tail(&self->client.txq, skb);
+
+	/* Try to send command */
+	irlan_run_ctrl_tx_queue(self);
+}
+
+/*
  * Function irlan_get_provider_info (self)
  *
  *    Send Get Provider Information command to peer IrLAN layer
@@ -645,7 +692,8 @@
  	frame[0] = CMD_GET_PROVIDER_INFO;
 	frame[1] = 0x00;                 /* Zero parameters */
 	
-	irttp_data_request(self->client.tsap_ctrl, skb);
+	/* irttp_data_request(self->client.tsap_ctrl, skb); */
+	irlan_ctrl_data_request(self, skb);
 }
 
 /*
@@ -683,7 +731,8 @@
 
 /* 	self->use_udata = TRUE; */
 
-	irttp_data_request(self->client.tsap_ctrl, skb);
+	/* irttp_data_request(self->client.tsap_ctrl, skb); */
+	irlan_ctrl_data_request(self, skb);
 }
 
 void irlan_close_data_channel(struct irlan_cb *self) 
@@ -696,6 +745,10 @@
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRLAN_MAGIC, return;);
 
+	/* Check if the TSAP is still there */
+	if (self->client.tsap_ctrl == NULL)
+		return;
+
 	skb = dev_alloc_skb(64);
 	if (!skb)
 		return;
@@ -711,7 +764,8 @@
 
 	irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data);
 
-	irttp_data_request(self->client.tsap_ctrl, skb);
+	/* irttp_data_request(self->client.tsap_ctrl, skb); */
+	irlan_ctrl_data_request(self, skb);
 }
 
 /*
@@ -747,7 +801,8 @@
  	irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED");
  	irlan_insert_string_param(skb, "FILTER_MODE", "FILTER"); 
 	
-	irttp_data_request(self->client.tsap_ctrl, skb);
+	/* irttp_data_request(self->client.tsap_ctrl, skb); */
+	irlan_ctrl_data_request(self, skb);
 }
 
 /*
@@ -787,8 +842,9 @@
 		irlan_insert_string_param(skb, "FILTER_MODE", "FILTER"); 
 	else
 		irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); 
-	
-	irttp_data_request(self->client.tsap_ctrl, skb);
+
+	/* irttp_data_request(self->client.tsap_ctrl, skb); */
+	irlan_ctrl_data_request(self, skb);
 }
 
 /*
@@ -826,8 +882,9 @@
 		irlan_insert_string_param(skb, "FILTER_MODE", "ALL"); 
 	else
 		irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); 
-	
-	irttp_data_request(self->client.tsap_ctrl, skb);
+
+	/* irttp_data_request(self->client.tsap_ctrl, skb); */
+	irlan_ctrl_data_request(self, skb);
 }
 
 /*
@@ -864,7 +921,8 @@
  	irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED");
  	irlan_insert_string_param(skb, "FILTER_OPERATION", "DYNAMIC"); 
 	
-	irttp_data_request(self->client.tsap_ctrl, skb);
+	/* irttp_data_request(self->client.tsap_ctrl, skb); */
+	irlan_ctrl_data_request(self, skb);
 }
 
 /*
@@ -899,7 +957,8 @@
 	
 	irlan_insert_string_param(skb, "MEDIA", "802.3");
 	
-	irttp_data_request(self->client.tsap_ctrl, skb);
+	/* irttp_data_request(self->client.tsap_ctrl, skb); */
+	irlan_ctrl_data_request(self, skb);
 }
 
 /*
@@ -1153,34 +1212,34 @@
 		printk(KERN_INFO "Success\n");
 		break;
 	case 1:
-		printk(KERN_WARNING "Insufficient resources\n");
+		WARNING("IrLAN: Insufficient resources\n");
 		break;
 	case 2:
-		printk(KERN_WARNING "Invalid command format\n");
+		WARNING("IrLAN: Invalid command format\n");
 		break;
 	case 3:
-		printk(KERN_WARNING "Command not supported\n");
+		WARNING("IrLAN: Command not supported\n");
 		break;
 	case 4:
-		printk(KERN_WARNING "Parameter not supported\n");
+		WARNING("IrLAN: Parameter not supported\n");
 		break;
 	case 5:
-		printk(KERN_WARNING "Value not supported\n");
+		WARNING("IrLAN: Value not supported\n");
 		break;
 	case 6:
-		printk(KERN_WARNING "Not open\n");
+		WARNING("IrLAN: Not open\n");
 		break;
 	case 7:
-		printk(KERN_WARNING "Authentication required\n");
+		WARNING("IrLAN: Authentication required\n");
 		break;
 	case 8:
-		printk(KERN_WARNING "Invalid password\n");
+		WARNING("IrLAN: Invalid password\n");
 		break;
 	case 9:
-		printk(KERN_WARNING "Protocol error\n");
+		WARNING("IrLAN: Protocol error\n");
 		break;
 	case 255:
-		printk(KERN_WARNING "Asynchronous status\n");
+		WARNING("IrLAN: Asynchronous status\n");
 		break;
 	}
 }
diff -u --recursive --new-file v2.3.5/linux/net/irda/irlan/irlan_eth.c linux/net/irda/irlan/irlan_eth.c
--- v2.3.5/linux/net/irda/irlan/irlan_eth.c	Mon May 31 22:28:07 1999
+++ linux/net/irda/irlan/irlan_eth.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Thu Oct 15 08:37:58 1998
- * Modified at:   Mon May 10 20:23:49 1999
+ * Modified at:   Mon May 31 19:57:08 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:       skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
  *                slip.c by Laurence Culhane,   <loz@holmes.demon.co.uk>
@@ -50,7 +50,7 @@
 	struct irmanager_event mgr_event;
 	struct irlan_cb *self;
 
-	DEBUG(0, __FUNCTION__"()\n");
+	DEBUG(2, __FUNCTION__"()\n");
 
 	ASSERT(dev != NULL, return -1;);
        
@@ -66,7 +66,12 @@
 	
 	ether_setup(dev);
 	
-	dev->tx_queue_len = TTP_MAX_QUEUE;
+	/* 
+	 * Lets do all queueing in IrTTP instead of this device driver.
+	 * Queueing here as well can introduce some strange latency
+	 * problems, which we will avoid by setting the queue size to 0.
+	 */
+	dev->tx_queue_len = 0;
 
 	if (self->provider.access_type == ACCESS_DIRECT) {
 		/*  
@@ -110,7 +115,7 @@
 {
 	struct irlan_cb *self;
 	
-	DEBUG(0, __FUNCTION__ "()\n");
+	DEBUG(2, __FUNCTION__ "()\n");
 
 	ASSERT(dev != NULL, return -1;);
 
@@ -145,7 +150,7 @@
 {
 	struct irlan_cb *self = (struct irlan_cb *) dev->priv;
 
-	DEBUG(0, __FUNCTION__ "()\n");
+	DEBUG(2, __FUNCTION__ "()\n");
 	
 	/* Stop device */
 	dev->tbusy = 1;
@@ -180,26 +185,16 @@
 int irlan_eth_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct irlan_cb *self;
+	int ret;
 
 	self = (struct irlan_cb *) dev->priv;
 
 	ASSERT(self != NULL, return 0;);
 	ASSERT(self->magic == IRLAN_MAGIC, return 0;);
 
-	/* Lock transmit buffer */
-	if (irda_lock((void *) &dev->tbusy) == FALSE) {
-		/*
-		 * If we get here, some higher level has decided we are broken.
-		 * There should really be a "kick me" function call instead.
-		 */
-		int tickssofar = jiffies - dev->trans_start; 
-		
-		if (tickssofar < 5) 
- 			return -EBUSY;
-		
- 		dev->tbusy = 0;
- 		dev->trans_start = jiffies;
-	}
+	/* Check if IrTTP can accept more frames */
+	if (dev->tbusy)
+		return -EBUSY;
 	
 	/* skb headroom large enough to contain all IrDA-headers? */
 	if ((skb_headroom(skb) < self->max_header_size) || (skb_shared(skb))) {
@@ -218,31 +213,30 @@
 	} 
 
 	dev->trans_start = jiffies;
-	self->stats.tx_packets++;
-	self->stats.tx_bytes += skb->len; 
 
 	/* Now queue the packet in the transport layer */
 	if (self->use_udata)
-		irttp_udata_request(self->tsap_data, skb);
-	else {
-		if (irttp_data_request(self->tsap_data, skb) < 0) {
-			/*   
-			 * IrTTPs tx queue is full, so we just have to
-			 * drop the frame! You might think that we should
-			 * just return -1 and don't deallocate the frame,
-			 * but that is dangerous since it's possible that
-			 * we have replaced the original skb with a new
-			 * one with larger headroom, and that would really
-			 * confuse do_dev_queue_xmit() in dev.c! I have
-			 * tried :-) DB 
-			 */
-			dev_kfree_skb(skb);
-			++self->stats.tx_dropped;
-		
-			return 0;
-		}
+		ret = irttp_udata_request(self->tsap_data, skb);
+	else
+		ret = irttp_data_request(self->tsap_data, skb);
+
+	if (ret < 0) {
+		/*   
+		 * IrTTPs tx queue is full, so we just have to
+		 * drop the frame! You might think that we should
+		 * just return -1 and don't deallocate the frame,
+		 * but that is dangerous since it's possible that
+		 * we have replaced the original skb with a new
+		 * one with larger headroom, and that would really
+		 * confuse do_dev_queue_xmit() in dev.c! I have
+		 * tried :-) DB 
+		 */
+		dev_kfree_skb(skb);
+		self->stats.tx_dropped++;
+	} else {
+		self->stats.tx_packets++;
+		self->stats.tx_bytes += skb->len; 
 	}
-	dev->tbusy = 0; /* Finished! */
 	
 	return 0;
 }
@@ -276,11 +270,11 @@
 	skb->dev = &self->dev;
 	skb->protocol=eth_type_trans(skb, skb->dev); /* Remove eth header */
 	
-	netif_rx(skb);   /* Eat it! */
-	
 	self->stats.rx_packets++;
 	self->stats.rx_bytes += skb->len; 
 
+	netif_rx(skb);   /* Eat it! */
+	
 	return 0;
 }
 
@@ -295,8 +289,6 @@
 	struct irlan_cb *self;
 	struct device *dev;
 
-	DEBUG(4, __FUNCTION__ "()\n");
-
 	self = (struct irlan_cb *) instance;
 
 	ASSERT(self != NULL, return;);
@@ -344,7 +336,7 @@
  *    Send gratuitous ARP to announce that we have changed
  *    hardware address, so that all peers updates their ARP tables
  */
-void irlan_etc_send_gratuitous_arp(struct device *dev) 
+void irlan_eth_send_gratuitous_arp(struct device *dev)
 {
 	struct in_device *in_dev;
 
@@ -375,16 +367,21 @@
 
  	self = dev->priv; 
 
-	DEBUG(0, __FUNCTION__ "()\n");
-	return;
+	DEBUG(2, __FUNCTION__ "()\n");
+
  	ASSERT(self != NULL, return;); 
  	ASSERT(self->magic == IRLAN_MAGIC, return;);
 
-	if (dev->flags&IFF_PROMISC) {
-		/* Enable promiscuous mode */
-		DEBUG(0, "Promiscous mode not implemented\n");
-		/* outw(MULTICAST|PROMISC, ioaddr); */
+	/* Check if data channel has been connected yet */
+	if (self->client.state != IRLAN_DATA) {
+		DEBUG(1, __FUNCTION__ "(), delaying!\n");
+		return;
 	}
+
+	if (dev->flags & IFF_PROMISC) {
+		/* Enable promiscuous mode */
+		WARNING("Promiscous mode not implemented by IrLAN!\n");
+	} 
 	else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > HW_MAX_ADDRS) {
 		/* Disable promiscuous mode, use normal mode. */
 		DEBUG(4, __FUNCTION__ "(), Setting multicast filter\n");
@@ -404,13 +401,10 @@
 		irlan_set_multicast_filter(self, FALSE);
 	}
 
-	if (dev->flags & IFF_BROADCAST) {
-		DEBUG(4, __FUNCTION__ "(), Setting broadcast filter\n");
+	if (dev->flags & IFF_BROADCAST)
 		irlan_set_broadcast_filter(self, TRUE);
-	} else {
-		DEBUG(4, __FUNCTION__ "(), Clearing broadcast filter\n");
+	else
 		irlan_set_broadcast_filter(self, FALSE);
-	}
 }
 
 /*
diff -u --recursive --new-file v2.3.5/linux/net/irda/irlan/irlan_filter.c linux/net/irda/irlan/irlan_filter.c
--- v2.3.5/linux/net/irda/irlan/irlan_filter.c	Mon May 31 22:28:07 1999
+++ linux/net/irda/irlan/irlan_filter.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Fri Jan 29 11:16:38 1999
- * Modified at:   Sat May  8 15:25:23 1999
+ * Modified at:   Fri May 14 23:11:01 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
@@ -23,6 +23,7 @@
  ********************************************************************/
 
 #include <linux/skbuff.h>
+#include <linux/random.h>
 
 #include <net/irda/irlan_common.h>
 
diff -u --recursive --new-file v2.3.5/linux/net/irda/irlap.c linux/net/irda/irlap.c
--- v2.3.5/linux/net/irda/irlap.c	Sat Apr 24 17:50:06 1999
+++ linux/net/irda/irlap.c	Mon Jun  7 16:18:58 1999
@@ -1,26 +1,31 @@
 /*********************************************************************
  *                
  * Filename:      irlap.c
- * Version:       0.9
- * Description:   An IrDA LAP driver for Linux
- * Status:        Stable.
+ * Version:       1.0
+ * Description:   IrLAP implementation for Linux
+ * Status:        Stable
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Aug  4 20:40:53 1997
- * Modified at:   Fri Apr 23 10:12:29 1999
+ * Modified at:   Mon May 31 21:43:55 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>, 
- *     All Rights Reserved.
+ *     Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
  *     
- *     This program is free software; you can redistribute iyt and/or 
+ *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
  *     published by the Free Software Foundation; either version 2 of 
  *     the License, or (at your option) any later version.
- *
- *     Neither Dag Brattli nor University of Tromsø admit liability nor
- *     provide warranty for any of this software. This material is 
- *     provided "AS-IS" and at no charge.
- *
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License 
+ *     along with this program; if not, write to the Free Software 
+ *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
+ *     MA 02111-1307 USA
+ *     
  ********************************************************************/
 
 #include <linux/config.h>
@@ -60,23 +65,23 @@
 };
 
 #ifdef CONFIG_PROC_FS
-int irlap_proc_read( char *, char **, off_t, int, int);
+int irlap_proc_read(char *, char **, off_t, int, int);
 
 #endif /* CONFIG_PROC_FS */
 
-__initfunc(int irlap_init( void))
+__initfunc(int irlap_init(void))
 {
 	/* Allocate master array */
-	irlap = hashbin_new( HB_LOCAL);
-	if ( irlap == NULL) {
-		printk( KERN_WARNING "IrLAP: Can't allocate irlap hashbin!\n");
+	irlap = hashbin_new(HB_LOCAL);
+	if (irlap == NULL) {
+		printk(KERN_WARNING "IrLAP: Can't allocate irlap hashbin!\n");
 		return -ENOMEM;
 	}
 
 #ifdef CONFIG_IRDA_COMPRESSION
-	irlap_compressors = hashbin_new( HB_LOCAL);
-	if ( irlap_compressors == NULL) {
-		printk( KERN_WARNING "IrLAP: Can't allocate compressors hashbin!\n");
+	irlap_compressors = hashbin_new(HB_LOCAL);
+	if (irlap_compressors == NULL) {
+		printk(KERN_WARNING "IrLAP: Can't allocate compressors hashbin!\n");
 		return -ENOMEM;
 	}
 #endif
@@ -86,12 +91,12 @@
 
 void irlap_cleanup(void)
 {
-	ASSERT( irlap != NULL, return;);
+	ASSERT(irlap != NULL, return;);
 
-	hashbin_delete( irlap, (FREE_FUNC) __irlap_close);
+	hashbin_delete(irlap, (FREE_FUNC) __irlap_close);
 
 #ifdef CONFIG_IRDA_COMPRESSION
-	hashbin_delete( irlap_compressors, (FREE_FUNC) kfree);
+	hashbin_delete(irlap_compressors, (FREE_FUNC) kfree);
 #endif
 }
 
@@ -101,32 +106,32 @@
  *    Initialize IrLAP layer
  *
  */
-struct irlap_cb *irlap_open( struct irda_device *irdev)
+struct irlap_cb *irlap_open(struct irda_device *irdev)
 {
 	struct irlap_cb *self;
 
-	DEBUG( 4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
 	
-	ASSERT( irdev != NULL, return NULL;);
-	ASSERT( irdev->magic == IRDA_DEVICE_MAGIC, return NULL;);
+	ASSERT(irdev != NULL, return NULL;);
+	ASSERT(irdev->magic == IRDA_DEVICE_MAGIC, return NULL;);
 
 	/* Initialize the irlap structure. */
-	self = kmalloc( sizeof( struct irlap_cb), GFP_KERNEL);
-	if ( self == NULL)
+	self = kmalloc(sizeof(struct irlap_cb), GFP_KERNEL);
+	if (self == NULL)
 		return NULL;
 	
-	memset( self, 0, sizeof(struct irlap_cb));
+	memset(self, 0, sizeof(struct irlap_cb));
 	self->magic = LAP_MAGIC;
 
 	/* Make a binding between the layers */
 	self->irdev = irdev;
 	self->netdev = &irdev->netdev;
 
-	irlap_next_state( self, LAP_OFFLINE);
+	irlap_next_state(self, LAP_OFFLINE);
 
 	/* Initialize transmitt queue */
-	skb_queue_head_init( &self->tx_list);
-	skb_queue_head_init( &self->wx_list);
+	skb_queue_head_init(&self->tx_list);
+	skb_queue_head_init(&self->wx_list);
 
 	/* My unique IrLAP device address! */
 	get_random_bytes(&self->saddr, sizeof(self->saddr));
@@ -140,21 +145,21 @@
 		self->caddr &= 0xfe;
 	}
 
-	init_timer( &self->slot_timer);
-	init_timer( &self->query_timer);
-	init_timer( &self->discovery_timer);
-	init_timer( &self->final_timer);		
-	init_timer( &self->poll_timer);
-	init_timer( &self->wd_timer);
-	init_timer( &self->backoff_timer);
+	init_timer(&self->slot_timer);
+	init_timer(&self->query_timer);
+	init_timer(&self->discovery_timer);
+	init_timer(&self->final_timer);		
+	init_timer(&self->poll_timer);
+	init_timer(&self->wd_timer);
+	init_timer(&self->backoff_timer);
 
-	irlap_apply_default_connection_parameters( self);
+	irlap_apply_default_connection_parameters(self);
 	
-	irlap_next_state( self, LAP_NDM);
+	irlap_next_state(self, LAP_NDM);
 
-	hashbin_insert( irlap, (QUEUE *) self, self->saddr, NULL);
+	hashbin_insert(irlap, (QUEUE *) self, self->saddr, NULL);
 
-	irlmp_register_link( self, self->saddr, &self->notify);
+	irlmp_register_link(self, self->saddr, &self->notify);
 	
 	return self;
 }
@@ -165,26 +170,26 @@
  *    Remove IrLAP and all allocated memory. Stop any pending timers.
  *
  */
-static void __irlap_close( struct irlap_cb *self)
+static void __irlap_close(struct irlap_cb *self)
 {
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
 
 	/* Stop timers */
-	del_timer( &self->slot_timer);
-	del_timer( &self->query_timer);
-	del_timer( &self->discovery_timer);
-	del_timer( &self->final_timer);		
-	del_timer( &self->poll_timer);
-	del_timer( &self->wd_timer);
-	del_timer( &self->backoff_timer);
+	del_timer(&self->slot_timer);
+	del_timer(&self->query_timer);
+	del_timer(&self->discovery_timer);
+	del_timer(&self->final_timer);		
+	del_timer(&self->poll_timer);
+	del_timer(&self->wd_timer);
+	del_timer(&self->backoff_timer);
 
-	irlap_flush_all_queues( self);
+	irlap_flush_all_queues(self);
        
 	self->irdev = NULL;
 	self->magic = 0;
 	
-	kfree( self);
+	kfree(self);
 }
 
 /*
@@ -193,27 +198,27 @@
  *    Remove IrLAP instance
  *
  */
-void irlap_close( struct irlap_cb *self) 
+void irlap_close(struct irlap_cb *self) 
 {
 	struct irlap_cb *lap;
 
-	DEBUG( 4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
 	
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
 
-	irlap_disconnect_indication( self, LAP_DISC_INDICATION);
+	irlap_disconnect_indication(self, LAP_DISC_INDICATION);
 
 	irlmp_unregister_link(self->saddr);
 	self->notify.instance = NULL;
 
 	/* Be sure that we manage to remove ourself from the hash */
-	lap = hashbin_remove( irlap, self->saddr, NULL);
-	if ( !lap) {
-		DEBUG( 1, __FUNCTION__ "(), Didn't find myself!\n");
+	lap = hashbin_remove(irlap, self->saddr, NULL);
+	if (!lap) {
+		DEBUG(1, __FUNCTION__ "(), Didn't find myself!\n");
 		return;
 	}
-	__irlap_close( lap);
+	__irlap_close(lap);
 }
 
 /*
@@ -243,7 +248,7 @@
  */
 void irlap_connect_response(struct irlap_cb *self, struct sk_buff *skb) 
 {
-	DEBUG( 4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
 	
 	irlap_do_event(self, CONNECT_RESPONSE, skb, NULL);
 }
@@ -324,23 +329,23 @@
  *    Received some data that was sent unreliable
  *
  */
-void irlap_unit_data_indication( struct irlap_cb *self, struct sk_buff *skb)
+void irlap_unit_data_indication(struct irlap_cb *self, struct sk_buff *skb)
 {
-	DEBUG( 1, __FUNCTION__ "()\n"); 
+	DEBUG(1, __FUNCTION__ "()\n"); 
 
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
-	ASSERT( skb != NULL, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
+	ASSERT(skb != NULL, return;);
 
 	/* Hide LAP header from IrLMP layer */
-	skb_pull( skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
+	skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
 
 #ifdef CONFIG_IRDA_COMPRESSION
-	if ( self->qos_tx.compression.value) {
+	if (self->qos_tx.compression.value) {
 		
-		skb = irlap_decompress_frame( self, skb);
-		if ( !skb) {
-			DEBUG( 1, __FUNCTION__ "(), Decompress error!\n");
+		skb = irlap_decompress_frame(self, skb);
+		if (!skb) {
+			DEBUG(1, __FUNCTION__ "(), Decompress error!\n");
 			return;
 		}
 	}
@@ -354,40 +359,35 @@
  *    Queue data for transmission, must wait until XMIT state
  *
  */
-inline void irlap_data_request( struct irlap_cb *self, struct sk_buff *skb,
+inline void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb,
 				int reliable)
 {
-	DEBUG( 4, __FUNCTION__ "()\n");
-       
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
-	ASSERT( skb != NULL, return;);
-
-	DEBUG( 4, __FUNCTION__ "(), tx_list=%d\n", 
-		   skb_queue_len( &self->tx_list));
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
+	ASSERT(skb != NULL, return;);
 
 #ifdef CONFIG_IRDA_COMPRESSION
-	if ( self->qos_tx.compression.value) {
-		skb = irlap_compress_frame( self, skb);
-		if ( !skb) {
-			DEBUG( 1, __FUNCTION__ "(), Compress error!\n");
+	if (self->qos_tx.compression.value) {
+		skb = irlap_compress_frame(self, skb);
+		if (!skb) {
+			DEBUG(1, __FUNCTION__ "(), Compress error!\n");
 			return;
 		}
 	}
 #endif
 	
-	ASSERT( skb_headroom( skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), 
-		return;);
-	skb_push( skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
+	ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), 
+	       return;);
+	skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
 
 	/*  
 	 *  Must set frame format now so that the rest of the code knows 
 	 *  if its dealing with an I or an UI frame
 	 */
-	if ( reliable)
+	if (reliable)
 		skb->data[1] = I_FRAME;
 	else {
-		DEBUG( 4, __FUNCTION__ "(), queueing unreliable frame\n");
+		DEBUG(4, __FUNCTION__ "(), queueing unreliable frame\n");
 		skb->data[1] = UI_FRAME;
 	}
 
@@ -395,20 +395,20 @@
 	 *  Send event if this frame only if we are in the right state 
 	 *  FIXME: udata should be sent first! (skb_queue_head?)
 	 */
-  	if (( self->state == LAP_XMIT_P) || (self->state == LAP_XMIT_S)) {
+  	if ((self->state == LAP_XMIT_P) || (self->state == LAP_XMIT_S)) {
 		/*
 		 *  Check if the transmit queue contains some unsent frames,
 		 *  and if so, make sure they are sent first
 		 */
-		if ( !skb_queue_empty( &self->tx_list)) {
-			skb_queue_tail( &self->tx_list, skb);
-			skb = skb_dequeue( &self->tx_list);
+		if (!skb_queue_empty(&self->tx_list)) {
+			skb_queue_tail(&self->tx_list, skb);
+			skb = skb_dequeue(&self->tx_list);
 			
-			ASSERT( skb != NULL, return;);
+			ASSERT(skb != NULL, return;);
 		}
-		irlap_do_event( self, SEND_I_CMD, skb, NULL);
+		irlap_do_event(self, SEND_I_CMD, skb, NULL);
 	} else
-		skb_queue_tail( &self->tx_list, skb);	
+		skb_queue_tail(&self->tx_list, skb);	
 }
 
 /*
@@ -444,33 +444,33 @@
  *    Disconnect request from other device
  *
  */
-void irlap_disconnect_indication( struct irlap_cb *self, LAP_REASON reason) 
+void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason) 
 {
-	DEBUG( 1, __FUNCTION__ "(), reason=%s\n", lap_reasons[reason]); 
+	DEBUG(1, __FUNCTION__ "(), reason=%s\n", lap_reasons[reason]); 
 
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
 
 #ifdef CONFIG_IRDA_COMPRESSION
-	irda_free_compression( self);
+	irda_free_compression(self);
 #endif
 	/* Flush queues */
-	irlap_flush_all_queues( self);
+	irlap_flush_all_queues(self);
 	
-	switch( reason) {
+	switch(reason) {
 	case LAP_RESET_INDICATION:
-		DEBUG( 1, __FUNCTION__ "(), Sending reset request!\n");
-		irlap_do_event( self, RESET_REQUEST, NULL, NULL);
+		DEBUG(1, __FUNCTION__ "(), Sending reset request!\n");
+		irlap_do_event(self, RESET_REQUEST, NULL, NULL);
 		break;
 	case LAP_NO_RESPONSE:	   /* FALLTROUGH */	
 	case LAP_DISC_INDICATION:  /* FALLTROUGH */
 	case LAP_FOUND_NONE:       /* FALLTROUGH */
 	case LAP_MEDIA_BUSY:
-		irlmp_link_disconnect_indication( self->notify.instance, 
+		irlmp_link_disconnect_indication(self->notify.instance, 
 						  self, reason, NULL);
 		break;
 	default:
-		DEBUG( 1, __FUNCTION__ "(), Reason %d not implemented!\n", 
+		DEBUG(1, __FUNCTION__ "(), Reason %d not implemented!\n", 
 		       reason);
 	}
 }
@@ -485,22 +485,22 @@
 {
 	struct irlap_info info;
 	
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
-	ASSERT( discovery != NULL, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
+	ASSERT(discovery != NULL, return;);
 	
-	DEBUG( 4, __FUNCTION__ "(), nslots = %d\n", discovery->nslots);
+	DEBUG(4, __FUNCTION__ "(), nslots = %d\n", discovery->nslots);
 
-	ASSERT(( discovery->nslots == 1) || ( discovery->nslots == 6) ||
-	       ( discovery->nslots == 8) || ( discovery->nslots == 16), 
+	ASSERT((discovery->nslots == 1) || (discovery->nslots == 6) ||
+	       (discovery->nslots == 8) || (discovery->nslots == 16), 
 	       return;);
 	
   	/*
 	 *  Discovery is only possible in NDM mode
 	 */ 
-	if ( self->state == LAP_NDM) {
-		ASSERT( self->discovery_log == NULL, return;);
-		self->discovery_log= hashbin_new( HB_LOCAL);
+	if (self->state == LAP_NDM) {
+		ASSERT(self->discovery_log == NULL, return;);
+		self->discovery_log= hashbin_new(HB_LOCAL);
 		
 		info.S = discovery->nslots; /* Number of slots */
 		info.s = 0; /* Current slot */
@@ -526,11 +526,11 @@
 
 		self->slot_timeout = sysctl_slot_timeout * HZ / 1000;
 		
-		irlap_do_event( self, DISCOVERY_REQUEST, NULL, &info);
+		irlap_do_event(self, DISCOVERY_REQUEST, NULL, &info);
 	} else { 
- 		DEBUG( 4, __FUNCTION__ 
+ 		DEBUG(4, __FUNCTION__ 
 		       "(), discovery only possible in NDM mode\n");
-		irlap_discovery_confirm( self, NULL);
+		irlap_discovery_confirm(self, NULL);
  	} 
 }
 
@@ -540,12 +540,12 @@
  *    A device has been discovered in front of this station, we
  *    report directly to LMP.
  */
-void irlap_discovery_confirm( struct irlap_cb *self, hashbin_t *discovery_log) 
+void irlap_discovery_confirm(struct irlap_cb *self, hashbin_t *discovery_log) 
 {
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
 	
-	ASSERT( self->notify.instance != NULL, return;);
+	ASSERT(self->notify.instance != NULL, return;);
 	
 	/* 
 	 * Check for successful discovery, since we are then allowed to clear 
@@ -556,7 +556,7 @@
 		irda_device_set_media_busy(self->irdev, FALSE);
 	
 	/* Inform IrLMP */
-	irlmp_link_discovery_confirm( self->notify.instance, discovery_log);
+	irlmp_link_discovery_confirm(self->notify.instance, discovery_log);
 	
 	/* 
 	 *  IrLMP has now the responsibilities for the discovery_log 
@@ -572,13 +572,13 @@
  */
 void irlap_discovery_indication(struct irlap_cb *self, discovery_t *discovery) 
 {
-	DEBUG( 4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
 
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
-	ASSERT( discovery != NULL, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
+	ASSERT(discovery != NULL, return;);
 
-	ASSERT( self->notify.instance != NULL, return;);
+	ASSERT(self->notify.instance != NULL, return;);
 	
 	irlmp_link_discovery_indication(self->notify.instance, discovery);
 }
@@ -591,12 +591,12 @@
  */
 void irlap_status_indication(int quality_of_link) 
 {
-	switch( quality_of_link) {
+	switch(quality_of_link) {
 	case STATUS_NO_ACTIVITY:
-		printk( KERN_INFO "IrLAP, no activity on link!\n");
+		printk(KERN_INFO "IrLAP, no activity on link!\n");
 		break;
 	case STATUS_NOISY:
-		printk( KERN_INFO "IrLAP, noisy link!\n");
+		printk(KERN_INFO "IrLAP, noisy link!\n");
 		break;
 	default:
 		break;
@@ -610,17 +610,17 @@
  *    
  *
  */
-void irlap_reset_indication( struct irlap_cb *self)
+void irlap_reset_indication(struct irlap_cb *self)
 {
-	DEBUG( 1, __FUNCTION__ "()\n");
+	DEBUG(1, __FUNCTION__ "()\n");
 
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
 	
-	if ( self->state == LAP_RESET_WAIT)
-		irlap_do_event( self, RESET_REQUEST, NULL, NULL);
+	if (self->state == LAP_RESET_WAIT)
+		irlap_do_event(self, RESET_REQUEST, NULL, NULL);
 	else
-		irlap_do_event( self, RESET_RESPONSE, NULL, NULL);
+		irlap_do_event(self, RESET_RESPONSE, NULL, NULL);
 }
 
 /*
@@ -631,7 +631,7 @@
  */
 void irlap_reset_confirm(void)
 {
- 	DEBUG( 1, __FUNCTION__ "()\n");
+ 	DEBUG(1, __FUNCTION__ "()\n");
 }
 
 /*
@@ -641,15 +641,15 @@
  *    S = Number of slots (0 -> S-1)
  *    s = Current slot
  */
-int irlap_generate_rand_time_slot( int S, int s) 
+int irlap_generate_rand_time_slot(int S, int s) 
 {
 	int slot;
 	
-	ASSERT(( S - s) > 0, return 0;);
+	ASSERT((S - s) > 0, return 0;);
 
 	slot = s + jiffies % (S-s);
 	
-	ASSERT(( slot >= s) || ( slot < S), return 0;);
+	ASSERT((slot >= s) || (slot < S), return 0;);
 	
 	return slot;
 }
@@ -661,51 +661,51 @@
  *    not intuitive and you should not try to change it. If you think it
  *    contains bugs, please mail a patch to the author instead.
  */
-void irlap_update_nr_received( struct irlap_cb *self, int nr) 
+void irlap_update_nr_received(struct irlap_cb *self, int nr) 
 {
 	struct sk_buff *skb = NULL;
 	int count = 0;
 
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
 
 	/*
          * Remove all the ack-ed frames from the window queue.
          */
 
-	DEBUG( 4, "--> wx_list=%d, va=%d, nr=%d\n", 
-	       skb_queue_len( &self->wx_list), self->va, nr);
+	DEBUG(4, "--> wx_list=%d, va=%d, nr=%d\n", 
+	       skb_queue_len(&self->wx_list), self->va, nr);
 
 	/* 
 	 *  Optimize for the common case. It is most likely that the receiver
 	 *  will acknowledge all the frames we have sent! So in that case we
 	 *  delete all frames stored in window.
 	 */
-	if ( nr == self->vs) {
-		while (( skb = skb_dequeue( &self->wx_list)) != NULL) {
+	if (nr == self->vs) {
+		while ((skb = skb_dequeue(&self->wx_list)) != NULL) {
 		     dev_kfree_skb(skb);
 		}
 		/* The last acked frame is the next to send minus one */
 		self->va = nr - 1;
 	} else {
 		/* Remove all acknowledged frames in current window */
-		while (( skb_peek( &self->wx_list) != NULL) && 
-		       ((( self->va+1) % 8) != nr)) 
+		while ((skb_peek(&self->wx_list) != NULL) && 
+		       (((self->va+1) % 8) != nr)) 
 		{
-			skb = skb_dequeue( &self->wx_list);
+			skb = skb_dequeue(&self->wx_list);
 			dev_kfree_skb(skb);
 			
 			self->va = (self->va + 1) % 8;
 			count++;
 		}
 		
-		DEBUG( 4, "irlap_update_nr_received(), removed %d\n", count);
-		DEBUG( 4, "wx_list=%d, va=%d, nr=%d -->\n", 
-		       skb_queue_len( &self->wx_list), self->va, nr);
+		DEBUG(4, "irlap_update_nr_received(), removed %d\n", count);
+		DEBUG(4, "wx_list=%d, va=%d, nr=%d -->\n", 
+		       skb_queue_len(&self->wx_list), self->va, nr);
 	}
 	
 	/* Advance window */
-	self->window = self->window_size - skb_queue_len( &self->wx_list);
+	self->window = self->window_size - skb_queue_len(&self->wx_list);
 }
 
 /*
@@ -713,14 +713,14 @@
  *
  *    Validate the next to send (ns) field from received frame.
  */
-int irlap_validate_ns_received( struct irlap_cb *self, int ns) 
+int irlap_validate_ns_received(struct irlap_cb *self, int ns) 
 {
-	ASSERT( self != NULL, return -ENODEV;);
-	ASSERT( self->magic == LAP_MAGIC, return -EBADR;);
+	ASSERT(self != NULL, return -ENODEV;);
+	ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
 
 	/*  ns as expected?  */
-	if ( ns == self->vr) {
-		DEBUG( 4, __FUNCTION__ "(), expected!\n");
+	if (ns == self->vr) {
+		DEBUG(4, __FUNCTION__ "(), expected!\n");
 		return NS_EXPECTED;
 	}
 	/*
@@ -737,14 +737,14 @@
  *    Validate the next to receive (nr) field from received frame.
  *
  */
-int irlap_validate_nr_received( struct irlap_cb *self, int nr) 
+int irlap_validate_nr_received(struct irlap_cb *self, int nr) 
 {
-	ASSERT( self != NULL, return -ENODEV;);
-	ASSERT( self->magic == LAP_MAGIC, return -EBADR;);
+	ASSERT(self != NULL, return -ENODEV;);
+	ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
 
 	/*  nr as expected?  */
-	if ( nr == self->vs) {
-		DEBUG( 4, __FUNCTION__ "(), expected!\n");
+	if (nr == self->vs) {
+		DEBUG(4, __FUNCTION__ "(), expected!\n");
 		return NR_EXPECTED;
 	}
 
@@ -752,11 +752,11 @@
 	 *  unexpected nr? (but within current window), first we check if the 
 	 *  ns numbers of the frames in the current window wrap.
 	 */
-	if ( self->va < self->vs) {
-		if (( nr >= self->va) && ( nr <= self->vs))
+	if (self->va < self->vs) {
+		if ((nr >= self->va) && (nr <= self->vs))
 			return NR_UNEXPECTED;
 	} else {
-		if (( nr >= self->va) || ( nr <= self->vs)) 
+		if ((nr >= self->va) || (nr <= self->vs)) 
 			return NR_UNEXPECTED;
 	}
 	
@@ -770,12 +770,12 @@
  *    Initialize the connection state parameters
  *
  */
-void irlap_initiate_connection_state( struct irlap_cb *self) 
+void irlap_initiate_connection_state(struct irlap_cb *self) 
 {
-	DEBUG( 4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
 	
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
 
 	/* Next to send and next to receive */
 	self->vs = self->vr = 0;
@@ -829,24 +829,24 @@
  *    Flush all queues
  *
  */
-void irlap_flush_all_queues( struct irlap_cb *self) 
+void irlap_flush_all_queues(struct irlap_cb *self) 
 {
 	struct sk_buff* skb;
 
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
 
 	/* Free transmission queue */
-	while (( skb = skb_dequeue( &self->tx_list)) != NULL)
-		dev_kfree_skb( skb);
+	while ((skb = skb_dequeue(&self->tx_list)) != NULL)
+		dev_kfree_skb(skb);
 	
 	/* Free sliding window buffered packets */
-	while (( skb = skb_dequeue( &self->wx_list)) != NULL)
-		dev_kfree_skb( skb);
+	while ((skb = skb_dequeue(&self->wx_list)) != NULL)
+		dev_kfree_skb(skb);
 
 #ifdef CONFIG_IRDA_RECYCLE_RR
-	if ( self->recycle_rr_skb) { 
- 		dev_kfree_skb( self->recycle_rr_skb);
+	if (self->recycle_rr_skb) { 
+ 		dev_kfree_skb(self->recycle_rr_skb);
  		self->recycle_rr_skb = NULL;
  	}
 #endif
@@ -866,7 +866,7 @@
 	ASSERT(self->magic == LAP_MAGIC, return;);
 
 	if (!self->irdev) {
-		DEBUG( 1, __FUNCTION__ "(), driver missing!\n");
+		DEBUG(1, __FUNCTION__ "(), driver missing!\n");
 		return;
 	}
 
@@ -883,8 +883,8 @@
 	__u8 mask; /* Current bit tested */
 	int i;
 
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
 
 	/* 
 	 *  Find out which compressors we support. We do this be checking that
@@ -892,24 +892,24 @@
 	 *  actually been loaded. Ths is sort of hairy code but that is what 
 	 *  you get when you do a little bit flicking :-)
 	 */
-	DEBUG( 4, __FUNCTION__ "(), comp bits 0x%02x\n", 
+	DEBUG(4, __FUNCTION__ "(), comp bits 0x%02x\n", 
 	       self->qos_rx.compression.bits); 
 	mask = 0x80; /* Start with testing MSB */
-	for ( i=0;i<8;i++) {
-		DEBUG( 4, __FUNCTION__ "(), testing bit %d\n", 8-i);
-		if ( self->qos_rx.compression.bits & mask) {
-			DEBUG( 4, __FUNCTION__ "(), bit %d is set by defalt\n",
+	for (i=0;i<8;i++) {
+		DEBUG(4, __FUNCTION__ "(), testing bit %d\n", 8-i);
+		if (self->qos_rx.compression.bits & mask) {
+			DEBUG(4, __FUNCTION__ "(), bit %d is set by defalt\n",
 			       8-i);
-			comp = hashbin_find( irlap_compressors, 
+			comp = hashbin_find(irlap_compressors, 
 					     compression[ msb_index(mask)], 
 					     NULL);
-			if ( !comp) {
+			if (!comp) {
 				/* Protocol not supported, so clear the bit */
-				DEBUG( 4, __FUNCTION__ "(), Compression "
+				DEBUG(4, __FUNCTION__ "(), Compression "
 				       "protocol %d has not been loaded!\n", 
 				       compression[msb_index(mask)]);
 				self->qos_rx.compression.bits &= ~mask;
-				DEBUG( 4, __FUNCTION__ 
+				DEBUG(4, __FUNCTION__ 
 				       "(), comp bits 0x%02x\n", 
 				       self->qos_rx.compression.bits); 
 			}
@@ -931,20 +931,20 @@
 void irlap_init_qos_capabilities(struct irlap_cb *self,
 				 struct qos_info *qos_user)
 {
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
-	ASSERT( self->irdev != NULL, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
+	ASSERT(self->irdev != NULL, return;);
 
 	/* Start out with the maximum QoS support possible */
-	irda_init_max_qos_capabilies( &self->qos_rx);
+	irda_init_max_qos_capabilies(&self->qos_rx);
 
 #ifdef CONFIG_IRDA_COMPRESSION
-	irlap_init_comp_qos_capabilities( self);
+	irlap_init_comp_qos_capabilities(self);
 #endif
 
 	/* Apply drivers QoS capabilities */
-	irda_qos_compute_intersection( &self->qos_rx, 
-				       irda_device_get_qos( self->irdev));
+	irda_qos_compute_intersection(&self->qos_rx, 
+				      irda_device_get_qos(self->irdev));
 
 	/*
 	 *  Check for user supplied QoS parameters. The service user is only 
@@ -952,17 +952,17 @@
 	 *  user may not have set all of them.
 	 */
 	if (qos_user) {
-		DEBUG( 1, __FUNCTION__ "(), Found user specified QoS!\n");
+		DEBUG(1, __FUNCTION__ "(), Found user specified QoS!\n");
 
-		if ( qos_user->baud_rate.bits)
+		if (qos_user->baud_rate.bits)
 			self->qos_rx.baud_rate.bits &= qos_user->baud_rate.bits;
 
-		if ( qos_user->max_turn_time.bits)
+		if (qos_user->max_turn_time.bits)
 			self->qos_rx.max_turn_time.bits &= qos_user->max_turn_time.bits;
-		if ( qos_user->data_size.bits)
+		if (qos_user->data_size.bits)
 			self->qos_rx.data_size.bits &= qos_user->data_size.bits;
 
-		if ( qos_user->link_disc_time.bits)
+		if (qos_user->link_disc_time.bits)
 			self->qos_rx.link_disc_time.bits &= qos_user->link_disc_time.bits;
 #ifdef CONFIG_IRDA_COMPRESSION
 		self->qos_rx.compression.bits &= qos_user->compression.bits;
@@ -984,7 +984,7 @@
 	/* Set disconnect time */
 	self->qos_rx.link_disc_time.bits &= 0x07;
 
-	irda_qos_bits_to_value( &self->qos_rx);
+	irda_qos_bits_to_value(&self->qos_rx);
 }
 
 /*
@@ -993,14 +993,14 @@
  *    Use the default connection and transmission parameters
  * 
  */
-void irlap_apply_default_connection_parameters( struct irlap_cb *self)
+void irlap_apply_default_connection_parameters(struct irlap_cb *self)
 {
-	DEBUG( 4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
 
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
 
-	irlap_change_speed( self, 9600);
+	irlap_change_speed(self, 9600);
 
 	/* Default value in NDM */
 	self->bofs_count = 11;
@@ -1028,12 +1028,12 @@
 void irlap_apply_connection_parameters(struct irlap_cb *self, 
 				       struct qos_info *qos) 
 {
-	DEBUG( 4, __FUNCTION__ "()\n");
+	DEBUG(4, __FUNCTION__ "()\n");
 	
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
+	ASSERT(self != NULL, return;);
+	ASSERT(self->magic == LAP_MAGIC, return;);
 
-	irlap_change_speed( self, qos->baud_rate.value);
+	irlap_change_speed(self, qos->baud_rate.value);
 
 	self->window_size = qos->window_size.value;
 	self->window      = qos->window_size.value;
@@ -1045,7 +1045,7 @@
 	 */
 	self->window_bytes = qos->baud_rate.value 
 		* qos->max_turn_time.value / 10000;
-	DEBUG( 4, "Setting window_bytes = %d\n", self->window_bytes);
+	DEBUG(4, "Setting window_bytes = %d\n", self->window_bytes);
 
 	/*
 	 *  Set N1 to 0 if Link Disconnect/Threshold Time = 3 and set it to 
@@ -1058,10 +1058,10 @@
 	else
 		self->N1 = 3000 / qos->max_turn_time.value;
 	
-	DEBUG( 4, "Setting N1 = %d\n", self->N1);
+	DEBUG(4, "Setting N1 = %d\n", self->N1);
 	
 	self->N2 = qos->link_disc_time.value * 1000 / qos->max_turn_time.value;
-	DEBUG( 4, "Setting N2 = %d\n", self->N2);
+	DEBUG(4, "Setting N2 = %d\n", self->N2);
 
 	/* 
 	 *  Initialize timeout values, some of the rules are listed on 
@@ -1072,11 +1072,11 @@
 	self->wd_timeout = self->poll_timeout * 2;
 
 #ifdef CONFIG_IRDA_COMPRESSION
-	if ( qos->compression.value) {
-		DEBUG( 1, __FUNCTION__ "(), Initializing compression\n");
-		irda_set_compression( self, qos->compression.value);
+	if (qos->compression.value) {
+		DEBUG(1, __FUNCTION__ "(), Initializing compression\n");
+		irda_set_compression(self, qos->compression.value);
 
-		irlap_compressor_init( self, 0);
+		irlap_compressor_init(self, 0);
 	}
 #endif
 }
@@ -1088,7 +1088,7 @@
  *    Give some info to the /proc file system
  *
  */
-int irlap_proc_read( char *buf, char **start, off_t offset, int len, 
+int irlap_proc_read(char *buf, char **start, off_t offset, int len, 
 		     int unused)
 {
 	struct irlap_cb *self;
@@ -1100,81 +1100,81 @@
 
 	len = 0;
 
-	self = (struct irlap_cb *) hashbin_get_first( irlap);
-	while ( self != NULL) {
-		ASSERT( self != NULL, return -ENODEV;);
-		ASSERT( self->magic == LAP_MAGIC, return -EBADR;);
+	self = (struct irlap_cb *) hashbin_get_first(irlap);
+	while (self != NULL) {
+		ASSERT(self != NULL, return -ENODEV;);
+		ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
 
-		len += sprintf( buf+len, "irlap%d <-> %s ",
+		len += sprintf(buf+len, "irlap%d <-> %s ",
 				i++, self->irdev->name);
-		len += sprintf( buf+len, "state: %s\n", 
+		len += sprintf(buf+len, "state: %s\n", 
 				irlap_state[ self->state]);
 		
-		len += sprintf( buf+len, "  caddr: %#02x, ", self->caddr);
-		len += sprintf( buf+len, "saddr: %#08x, ", self->saddr);
-		len += sprintf( buf+len, "daddr: %#08x\n", self->daddr);
+		len += sprintf(buf+len, "  caddr: %#02x, ", self->caddr);
+		len += sprintf(buf+len, "saddr: %#08x, ", self->saddr);
+		len += sprintf(buf+len, "daddr: %#08x\n", self->daddr);
 		
-		len += sprintf( buf+len, "  win size: %d, ", 
+		len += sprintf(buf+len, "  win size: %d, ", 
 				self->window_size);
-		len += sprintf( buf+len, "win: %d, ", self->window);
-		len += sprintf( buf+len, "win bytes: %d, ", self->window_bytes);
-		len += sprintf( buf+len, "bytes left: %d\n", self->bytes_left);
-
-		len += sprintf( buf+len, "  tx queue len: %d ", 
-				skb_queue_len( &self->tx_list));
-		len += sprintf( buf+len, "win queue len: %d ", 
-				skb_queue_len( &self->wx_list));
-		len += sprintf( buf+len, "rbusy: %s\n", self->remote_busy ? 
+		len += sprintf(buf+len, "win: %d, ", self->window);
+		len += sprintf(buf+len, "win bytes: %d, ", self->window_bytes);
+		len += sprintf(buf+len, "bytes left: %d\n", self->bytes_left);
+
+		len += sprintf(buf+len, "  tx queue len: %d ", 
+				skb_queue_len(&self->tx_list));
+		len += sprintf(buf+len, "win queue len: %d ", 
+				skb_queue_len(&self->wx_list));
+		len += sprintf(buf+len, "rbusy: %s\n", self->remote_busy ? 
 				"TRUE" : "FALSE");
 		
-		len += sprintf( buf+len, "  retrans: %d ", self->retry_count);
-		len += sprintf( buf+len, "vs: %d ", self->vs);
-		len += sprintf( buf+len, "vr: %d ", self->vr);
-		len += sprintf( buf+len, "va: %d\n", self->va);
+		len += sprintf(buf+len, "  retrans: %d ", self->retry_count);
+		len += sprintf(buf+len, "vs: %d ", self->vs);
+		len += sprintf(buf+len, "vr: %d ", self->vr);
+		len += sprintf(buf+len, "va: %d\n", self->va);
 		
-		len += sprintf( buf+len, "  qos\tbps\tmaxtt\tdsize\twinsize\taddbofs\tmintt\tldisc\tcomp\n");
+		len += sprintf(buf+len, "  qos\tbps\tmaxtt\tdsize\twinsize\taddbofs\tmintt\tldisc\tcomp\n");
 		
-		len += sprintf( buf+len, "  tx\t%d\t", 
+		len += sprintf(buf+len, "  tx\t%d\t", 
 				self->qos_tx.baud_rate.value);
-		len += sprintf( buf+len, "%d\t", 
+		len += sprintf(buf+len, "%d\t", 
 				self->qos_tx.max_turn_time.value);
-		len += sprintf( buf+len, "%d\t",
+		len += sprintf(buf+len, "%d\t",
 				self->qos_tx.data_size.value);
-		len += sprintf( buf+len, "%d\t",
+		len += sprintf(buf+len, "%d\t",
 				self->qos_tx.window_size.value);
-		len += sprintf( buf+len, "%d\t",
+		len += sprintf(buf+len, "%d\t",
 				self->qos_tx.additional_bofs.value);
-		len += sprintf( buf+len, "%d\t", 
+		len += sprintf(buf+len, "%d\t", 
 				self->qos_tx.min_turn_time.value);
-		len += sprintf( buf+len, "%d\t", 
+		len += sprintf(buf+len, "%d\t", 
 				self->qos_tx.link_disc_time.value);
 #ifdef CONFIG_IRDA_COMPRESSION
-		len += sprintf( buf+len, "%d",
+		len += sprintf(buf+len, "%d",
 				self->qos_tx.compression.value);
 #endif
-		len += sprintf( buf+len, "\n");
+		len += sprintf(buf+len, "\n");
 
-		len += sprintf( buf+len, "  rx\t%d\t", 
+		len += sprintf(buf+len, "  rx\t%d\t", 
 				self->qos_rx.baud_rate.value);
-		len += sprintf( buf+len, "%d\t", 
+		len += sprintf(buf+len, "%d\t", 
 				self->qos_rx.max_turn_time.value);
-		len += sprintf( buf+len, "%d\t",
+		len += sprintf(buf+len, "%d\t",
 				self->qos_rx.data_size.value);
-		len += sprintf( buf+len, "%d\t",
+		len += sprintf(buf+len, "%d\t",
 				self->qos_rx.window_size.value);
-		len += sprintf( buf+len, "%d\t",
+		len += sprintf(buf+len, "%d\t",
 				self->qos_rx.additional_bofs.value);
-		len += sprintf( buf+len, "%d\t", 
+		len += sprintf(buf+len, "%d\t", 
 				self->qos_rx.min_turn_time.value);
-		len += sprintf( buf+len, "%d\t", 
+		len += sprintf(buf+len, "%d\t", 
 				self->qos_rx.link_disc_time.value);
 #ifdef CONFIG_IRDA_COMPRESSION
-		len += sprintf( buf+len, "%d",
+		len += sprintf(buf+len, "%d",
 				self->qos_rx.compression.value);
 #endif
-		len += sprintf( buf+len, "\n");
+		len += sprintf(buf+len, "\n");
 		
-		self = (struct irlap_cb *) hashbin_get_next( irlap);
+		self = (struct irlap_cb *) hashbin_get_next(irlap);
 	}
 	restore_flags(flags);
 
diff -u --recursive --new-file v2.3.5/linux/net/irda/irlap_event.c linux/net/irda/irlap_event.c
--- v2.3.5/linux/net/irda/irlap_event.c	Mon May 31 22:28:07 1999
+++ linux/net/irda/irlap_event.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sat Aug 16 00:59:29 1997
- * Modified at:   Sun May  9 22:44:32 1999
+ * Modified at:   Mon May 31 21:55:42 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
@@ -209,8 +209,8 @@
  *    Rushes through the state machine without any delay. If state == XMIT
  *    then send queued data frames. 
  */
-void irlap_do_event( struct irlap_cb *self, IRLAP_EVENT event, 
-		     struct sk_buff *skb, struct irlap_info *info) 
+void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event, 
+		    struct sk_buff *skb, struct irlap_info *info) 
 {
 	int ret;
 	
@@ -218,7 +218,7 @@
 		return;
 	
   	DEBUG(4, __FUNCTION__ "(), event = %s, state = %s\n", 
-	      irlap_event[ event], irlap_state[ self->state]); 
+	      irlap_event[event], irlap_state[self->state]); 
 	
 	ret = (*state[ self->state]) (self, event, skb, info);
 	
@@ -236,13 +236,12 @@
 		if (skb_queue_len(&self->tx_list)) {
 			/* Try to send away all queued data frames */
 			while ((skb = skb_dequeue(&self->tx_list)) != NULL) {
-				ret = (*state[ self->state])(self, SEND_I_CMD,
-							     skb, NULL);
+				ret = (*state[self->state])(self, SEND_I_CMD,
+							    skb, NULL);
 				if ( ret == -EPROTO)
 					break; /* Try again later! */
 			}
 		} else if (self->disconnect_pending) {
-			DEBUG(0, __FUNCTION__ "(), disconnecting!\n");
 			self->disconnect_pending = FALSE;
 			
 			ret = (*state[self->state])(self, DISCONNECT_REQUEST,
@@ -761,36 +760,30 @@
  *    stations.
  * 
  */
-static int irlap_state_xmit_p( struct irlap_cb *self, IRLAP_EVENT event, 
-			       struct sk_buff *skb, struct irlap_info *info) 
+static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event, 
+			      struct sk_buff *skb, struct irlap_info *info) 
 {
 	int ret = 0;
 	
-	ASSERT( self != NULL, return -ENODEV;);
-	ASSERT( self->magic == LAP_MAGIC, return -EBADR;);
-
-	DEBUG( 4, __FUNCTION__ "(), event=%s, vs=%d, vr=%d", 
-	       irlap_event[ event], self->vs, self->vr); 
+	DEBUG(4, __FUNCTION__ "(), event=%s, vs=%d, vr=%d", 
+	      irlap_event[event], self->vs, self->vr);
 		
 	switch (event) {
 	case SEND_I_CMD:
-		ASSERT( skb != NULL, return -1;);
-		DEBUG( 4, __FUNCTION__ "(), Window=%d\n", self->window);
-		
 		/*
 		 *  Only send frame if send-window > 0.
 		 */ 
-		if (( self->window > 0) && ( !self->remote_busy)) {
+		if ((self->window > 0) && (!self->remote_busy)) {
 
 			/*
 			 *  Test if we have transmitted more bytes over the 
 			 *  link than its possible to do with the current 
 			 *  speed and turn-around-time.
 			 */
-			if (( skb->len+self->bofs_count) > self->bytes_left) {
-				DEBUG( 4, __FUNCTION__ "(), Not allowed to "
-				       "transmit more bytes!\n");
-				skb_queue_head( &self->tx_list, skb);
+			if ((skb->len+self->bofs_count) > self->bytes_left) {
+				DEBUG(4, __FUNCTION__ "(), Not allowed to "
+				      "transmit more bytes!\n");
+				skb_queue_head(&self->tx_list, skb);
 
 				/*
 				 *  We should switch state to LAP_NRM_P, but
@@ -802,7 +795,7 @@
 				 */
 				return -EPROTO;
 			}
-			self->bytes_left -= ( skb->len + self->bofs_count);
+			self->bytes_left -= (skb->len + self->bofs_count);
 
 			/*
 			 *  Send data with poll bit cleared only if window > 1
@@ -811,11 +804,9 @@
 			if (( self->window > 1) && 
 			    skb_queue_len( &self->tx_list) > 0) 
 			{   
-				DEBUG( 4, __FUNCTION__ "(), window > 1\n");
 				irlap_send_data_primary( self, skb);
 				irlap_next_state( self, LAP_XMIT_P);
 			} else {
-				DEBUG( 4, __FUNCTION__ "(), window <= 1\n");
 				irlap_send_data_primary_poll( self, skb);
 				irlap_next_state( self, LAP_NRM_P);
 				
@@ -933,9 +924,6 @@
 	int ns_status;
 	int nr_status;
 
-	ASSERT(self != NULL, return -1;);
-	ASSERT(self->magic == LAP_MAGIC, return -1;);
-
 	switch (event) {
 	case RECV_I_RSP: /* Optimize for the common case */
 		/* FIXME: must check for remote_busy below */
@@ -947,7 +935,6 @@
 		 */
 		self->fast_RR = FALSE;
 #endif
-
 		ASSERT( info != NULL, return -1;);
 
 		ns_status = irlap_validate_ns_received(self, info->ns);
@@ -1141,13 +1128,6 @@
 		}
 		break;
 	case RECV_RR_RSP:
-		DEBUG(4, __FUNCTION__ "(), RECV_RR_FRAME: "
-		      "Retrans:%d, nr=%d, va=%d, vs=%d, vr=%d\n",
-		      self->retry_count, info->nr, self->va, self->vs, 
-		      self->vr);
-
-		ASSERT(info != NULL, return -1;);
-
 		/*  
 		 *  If you get a RR, the remote isn't busy anymore, 
 		 *  no matter what the NR 
@@ -1194,14 +1174,6 @@
 			/* Resend rejected frames */
 			irlap_resend_rejected_frames( self, CMD_FRAME);
 			
-			/*
-			 *  Start only if not running, DB
-			 *  TODO: Should this one be here?
-			 */
-			/* if ( !self->final_timer.prev) */
-/* 				irda_start_timer( FINAL_TIMER, self->final_timeout);  */
-
-			/* Keep state */
 			irlap_next_state( self, LAP_NRM_P);
 		} else if (ret == NR_INVALID) {
 			DEBUG(1, "irlap_state_nrm_p: received RR with "
@@ -1210,8 +1182,7 @@
 
 			irlap_next_state( self, LAP_RESET_WAIT);
 
-			irlap_disconnect_indication( self, 
-						     LAP_RESET_INDICATION);
+			irlap_disconnect_indication(self, LAP_RESET_INDICATION);
 			self->xmitflag = TRUE;
 		}
 		if (skb)
@@ -1479,13 +1450,13 @@
 		/*
 		 *  Send frame only if send window > 1
 		 */ 
-		if (( self->window > 0) && ( !self->remote_busy)) {
+		if ((self->window > 0) && ( !self->remote_busy)) {
 			/*
 			 *  Test if we have transmitted more bytes over the 
 			 *  link than its possible to do with the current 
 			 *  speed and turn-around-time.
 			 */
-			if (( skb->len+self->bofs_count) > self->bytes_left) {
+			if ((skb->len+self->bofs_count) > self->bytes_left) {
 				DEBUG( 4, "IrDA: Not allowed to transmit more bytes!\n");
 				skb_queue_head( &self->tx_list, skb);
 				/*
@@ -1507,11 +1478,9 @@
 			if (( self->window > 1) && 
 			    skb_queue_len( &self->tx_list) > 0) 
 			{   
-				DEBUG( 4, __FUNCTION__ "(), window > 1\n");
 				irlap_send_data_secondary( self, skb);
 				irlap_next_state( self, LAP_XMIT_S);
 			} else {
-				DEBUG( 4, "(), window <= 1\n");
 				irlap_send_data_secondary_final( self, skb);
 				irlap_next_state( self, LAP_NRM_S);
 
@@ -1635,8 +1604,7 @@
 		/*
 		 *  Check for Unexpected next to send (Ns)
 		 */
-		if (( ns_status == NS_UNEXPECTED) && 
-		    ( nr_status == NR_EXPECTED)) 
+		if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED))
 		{
 			/* Unexpected next to send, with final bit cleared */
 			if ( !info->pf) {
@@ -1659,8 +1627,7 @@
 		/* 
 		 *  Unexpected Next to Receive(NR) ?
 		 */
-		if (( ns_status == NS_EXPECTED) && 
-		    ( nr_status == NR_UNEXPECTED))
+		if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED))
 		{
 			if ( info->pf) {
 				DEBUG( 4, "RECV_I_RSP: frame(s) lost\n");
@@ -1756,20 +1723,20 @@
 				irlap_update_nr_received( self, info->nr);
 				del_timer( &self->wd_timer);
 				
-				irlap_wait_min_turn_around( self, &self->qos_tx);
+				irlap_wait_min_turn_around(self, &self->qos_tx);
 				irlap_next_state( self, LAP_XMIT_S);
 			} else {			
 				self->remote_busy = FALSE;
 				/* Update Nr received */
-				irlap_update_nr_received( self, info->nr);
-				irlap_wait_min_turn_around( self, &self->qos_tx);
+				irlap_update_nr_received(self, info->nr);
+				irlap_wait_min_turn_around(self, &self->qos_tx);
 				
-				irlap_send_rr_frame( self, RSP_FRAME);
+				irlap_send_rr_frame(self, RSP_FRAME);
 				
-				irlap_start_wd_timer( self, self->wd_timeout);
-				irlap_next_state( self, LAP_NRM_S);
+				irlap_start_wd_timer(self, self->wd_timeout);
+				irlap_next_state(self, LAP_NRM_S);
 			}
-		} else if ( nr_status == NR_UNEXPECTED) {
+		} else if (nr_status == NR_UNEXPECTED) {
 			self->remote_busy = FALSE;
 			irlap_update_nr_received( self, info->nr);
 			irlap_resend_rejected_frames( self, RSP_FRAME);
@@ -1781,8 +1748,8 @@
 		} else {
 			DEBUG(1, __FUNCTION__ "(), invalid nr not implemented!\n");
 		} 
-		if ( skb)
-			dev_kfree_skb( skb);
+		if (skb)
+			dev_kfree_skb(skb);
 
 		break;
 	case RECV_SNRM_CMD:
@@ -1894,7 +1861,7 @@
 	ASSERT( self != NULL, return -ENODEV;);
 	ASSERT( self->magic == LAP_MAGIC, return -EBADR;);
 	
-	switch( event) {
+	switch(event) {
 	case RESET_RESPONSE:
 		irlap_send_ua_response_frame( self, &self->qos_rx);
 		irlap_initiate_connection_state( self);
diff -u --recursive --new-file v2.3.5/linux/net/irda/irlap_frame.c linux/net/irda/irlap_frame.c
--- v2.3.5/linux/net/irda/irlap_frame.c	Mon May 31 22:28:07 1999
+++ linux/net/irda/irlap_frame.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Aug 19 10:27:26 1997
- * Modified at:   Sun May  9 22:55:11 1999
+ * Modified at:   Mon May 31 09:29:13 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Resrved.
@@ -1001,10 +1001,6 @@
 {
 	__u8  *frame;
 	
-	ASSERT( self != NULL, return;);
-	ASSERT( self->magic == LAP_MAGIC, return;);
-	ASSERT( skb != NULL, return;);
-
 	frame = skb->data;
 	
 	/* Insert connection address */
@@ -1014,15 +1010,6 @@
 	/* Insert next to receive (Vr) */
 	frame[1] |= (self->vr << 5);  /* insert nr */
 
-#if 0
-	{
-		int ns;
-		ns = (frame[1] >> 1) & 0x07; /* Next to send */
-
-		DEBUG(0, __FUNCTION__ "(), ns=%d\n", ns);
-	}
-#endif
-
 	irlap_queue_xmit(self, skb);
 }
 
@@ -1240,7 +1227,7 @@
 	 *  Optimize for the common case and check if the frame is an
 	 *  I(nformation) frame. Only I-frames have bit 0 set to 0
 	 */
-	if(~control & 0x01) {
+	if (~control & 0x01) {
 		irlap_recv_i_frame(self, skb, &info, command);
 		self->stats.rx_packets++;
 		return 0;
diff -u --recursive --new-file v2.3.5/linux/net/irda/irlmp.c linux/net/irda/irlmp.c
--- v2.3.5/linux/net/irda/irlmp.c	Mon May 31 22:28:07 1999
+++ linux/net/irda/irlmp.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Stable.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 17 20:54:32 1997
- * Modified at:   Sun May  9 22:45:06 1999
+ * Modified at:   Mon May 31 21:49:41 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
@@ -451,17 +451,16 @@
 	ASSERT(skb != NULL, return;);
 	ASSERT(self->lap != NULL, return;);
 
-	DEBUG(0, __FUNCTION__ "(), slsap_sel=%02x, dlsap_sel=%02x\n", 
+	DEBUG(2, __FUNCTION__ "(), slsap_sel=%02x, dlsap_sel=%02x\n", 
 	      self->slsap_sel, self->dlsap_sel);
 
 	self->qos = *self->lap->qos;
 
-	lap_header_size = irlap_get_header_size(self->lap->irlap);
-
-	max_seg_size = self->lap->qos->data_size.value-LMP_HEADER-
-		lap_header_size;
+	max_seg_size = self->lap->qos->data_size.value-LMP_HEADER;
 	DEBUG(2, __FUNCTION__ "(), max_seg_size=%d\n", max_seg_size);
 	
+	lap_header_size = irlap_get_header_size(self->lap->irlap);
+
 	max_header_size = LMP_HEADER + lap_header_size;
 
 	DEBUG(2, __FUNCTION__ "(), max_header_size=%d\n", max_header_size);
@@ -519,11 +518,10 @@
 	ASSERT(self->lap != NULL, return;);
 	self->qos = *self->lap->qos;
 
-	lap_header_size = irlap_get_header_size(self->lap->irlap);
-
-	max_seg_size = self->lap->qos->data_size.value-LMP_HEADER-
-		lap_header_size;
+	max_seg_size = self->lap->qos->data_size.value-LMP_HEADER;
 	DEBUG(2, __FUNCTION__ "(), max_seg_size=%d\n", max_seg_size);
+
+	lap_header_size = irlap_get_header_size(self->lap->irlap);
 	
 	max_header_size = LMP_HEADER + lap_header_size;
 
diff -u --recursive --new-file v2.3.5/linux/net/irda/irlmp_frame.c linux/net/irda/irlmp_frame.c
--- v2.3.5/linux/net/irda/irlmp_frame.c	Mon May 31 22:28:07 1999
+++ linux/net/irda/irlmp_frame.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Aug 19 02:09:59 1997
- * Modified at:   Sun May  9 21:00:05 1999
+ * Modified at:   Mon May 31 09:53:16 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>
@@ -134,17 +134,17 @@
 				       self->lsaps);
 	
 	if (lsap == NULL) {
-		DEBUG(0, "IrLMP, Sorry, no LSAP for received frame!\n");
-		DEBUG(0, __FUNCTION__ 
+		DEBUG(2, "IrLMP, Sorry, no LSAP for received frame!\n");
+		DEBUG(2, __FUNCTION__ 
 		      "(), slsap_sel = %02x, dlsap_sel = %02x\n", slsap_sel, 
 		      dlsap_sel);
 		if (fp[0] & CONTROL_BIT) {
-			DEBUG(0, __FUNCTION__ 
+			DEBUG(2, __FUNCTION__ 
 			      "(), received control frame %02x\n", fp[2]);
 		} else {
-			DEBUG(0, __FUNCTION__ "(), received data frame\n");
+			DEBUG(2, __FUNCTION__ "(), received data frame\n");
 		}
-		dev_kfree_skb( skb);
+		dev_kfree_skb(skb);
 		return;
 	}
 
diff -u --recursive --new-file v2.3.5/linux/net/irda/irmod.c linux/net/irda/irmod.c
--- v2.3.5/linux/net/irda/irmod.c	Mon May 31 22:28:07 1999
+++ linux/net/irda/irmod.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Dec 15 13:55:39 1997
- * Modified at:   Mon May 10 15:28:49 1999
+ * Modified at:   Fri May 14 13:46:02 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved.
@@ -45,7 +45,7 @@
 #include <net/irda/wrapper.h>
 #include <net/irda/timer.h>
 
-extern struct proc_dir_entry proc_irda;
+extern struct proc_dir_entry *proc_irda;
 
 struct irda_cb irda; /* One global instance */
 
diff -u --recursive --new-file v2.3.5/linux/net/irda/irttp.c linux/net/irda/irttp.c
--- v2.3.5/linux/net/irda/irttp.c	Mon May 31 22:28:07 1999
+++ linux/net/irda/irttp.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:31 1997
- * Modified at:   Mon May 10 17:12:53 1999
+ * Modified at:   Mon May 31 10:29:56 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
@@ -35,7 +35,7 @@
 #include <net/irda/irlmp.h>
 #include <net/irda/irttp.h>
 
-struct irttp_cb *irttp = NULL;
+static struct irttp_cb *irttp = NULL;
 
 static void __irttp_close_tsap(struct tsap_cb *self);
 
@@ -44,8 +44,7 @@
 static int irttp_udata_indication(void *instance, void *sap, 
 				  struct sk_buff *skb);
 static void irttp_disconnect_indication(void *instance, void *sap,  
-					LM_REASON reason,
-					struct sk_buff *);
+					LM_REASON reason, struct sk_buff *);
 static void irttp_connect_indication(void *instance, void *sap, 
 				     struct qos_info *qos, __u32 max_sdu_size,
 				     __u8 header_size, struct sk_buff *skb);
@@ -57,8 +56,8 @@
 
 static void irttp_flush_queues(struct tsap_cb *self);
 static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb);
-static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self);
 static void irttp_start_todo_timer(struct tsap_cb *self, int timeout);
+static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self);
 
 /*
  * Function irttp_init (void)
@@ -298,7 +297,7 @@
 
 	/* Check that nothing bad happens */
 	if ((skb->len == 0) || (!self->connected)) {
-		DEBUG(4, __FUNCTION__ "(), No data, or not connected\n");
+		ERROR(__FUNCTION__ "(), No data, or not connected\n");
 		return -ENOTCONN;
 	}
 
@@ -307,8 +306,8 @@
 	 *  inside an IrLAP frame
 	 */
 	if ((self->tx_max_sdu_size == 0) && (skb->len > self->max_seg_size)) {
-		DEBUG(1, __FUNCTION__ 
-		       "(), SAR disabled, and data is to large for IrLAP!\n");
+		ERROR(__FUNCTION__ 
+		      "(), SAR disabled, and data is to large for IrLAP!\n");
 		return -EMSGSIZE;
 	}
 
@@ -320,8 +319,8 @@
 	    (self->tx_max_sdu_size != SAR_UNBOUND) && 
 	    (skb->len > self->tx_max_sdu_size))
 	{
-		DEBUG(1, __FUNCTION__ "(), SAR enabled, "
-		       "but data is larger than TxMaxSduSize!\n");
+		ERROR(__FUNCTION__ "(), SAR enabled, "
+		      "but data is larger than TxMaxSduSize!\n");
 		return -EMSGSIZE;
 	}
 	/* 
@@ -343,7 +342,6 @@
 		frame = skb_push(skb, TTP_HEADER);
 		frame[0] = 0x00; /* Clear more bit */
 		
-		DEBUG(4, __FUNCTION__ "(), queueing original skb\n");
 		skb_queue_tail(&self->tx_queue, skb);
 	} else {
 		/*
@@ -384,12 +382,8 @@
 {
 	struct sk_buff *skb = NULL;
 	unsigned long flags;
-	__u8 *frame;
 	int n;
 
-	ASSERT(self != NULL, return;);
-	ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-	
 	if (irda_lock(&self->tx_queue_lock) == FALSE)
 		return;
 
@@ -424,12 +418,7 @@
 		 *  More bit must be set by the data_request() or fragment() 
 		 *  functions
 		 */
-		frame = skb->data;
-
-		DEBUG(4, __FUNCTION__ "(), More=%s\n", frame[0] & 0x80 ? 
-		       "TRUE" : "FALSE" );
-
-		frame[0] |= (__u8) (n & 0x7f);
+		skb->data[0] |= (n & 0x7f);
 		
 		irlmp_data_request(self->lsap, skb);
 		self->stats.tx_packets++;
@@ -437,12 +426,12 @@
 		/* Check if we can accept more frames from client */
 		if ((self->tx_sdu_busy) && 
 		    (skb_queue_len(&self->tx_queue) < LOW_THRESHOLD)) 
-		{ 
+		{
 			self->tx_sdu_busy = FALSE;
 			
 			if (self->notify.flow_indication)
 				self->notify.flow_indication(
-					self->notify.instance, self, 
+					self->notify.instance, self,
 					FLOW_START);
 		}
 	}
@@ -541,23 +530,14 @@
 				 struct sk_buff *skb)
 {
 	struct tsap_cb *self;
-	int more;
 	int n;
-	__u8 *frame;
-	
+
 	self = (struct tsap_cb *) instance;
 
 	ASSERT(self != NULL, return -1;);
 	ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
-	ASSERT(skb != NULL, return -1;);
 
-	frame = skb->data;
-	
-	n = frame[0] & 0x7f;     /* Extract the credits */
-	more = frame[0] & 0x80;
-
- 	DEBUG(3, __FUNCTION__"(), got %d credits, TSAP sel=%02x\n", 
-	      n, self->stsap_sel);
+	n = skb->data[0] & 0x7f;     /* Extract the credits */
 
 	self->stats.rx_packets++;
 
@@ -565,10 +545,9 @@
 	 *  Data or dataless frame? Dataless frames only contain the 
 	 *  TTP_HEADER
 	 */
-	if (skb->len == 1) {
-		/* Dataless flowdata TTP-PDU */
-		self->send_credit += n;
-	} else {
+	if (skb->len == 1)
+		self->send_credit += n;	/* Dataless flowdata TTP-PDU */
+	else {
 		/* Deal with inbound credit */
 		self->send_credit += n;
 		self->remote_credit--;
@@ -768,6 +747,10 @@
 	self->connected = TRUE;
 
 	parameters = frame[0] & 0x80;	
+
+	ASSERT(skb->len >= TTP_HEADER, return;);
+	skb_pull(skb, TTP_HEADER);
+		
 	if (parameters) {
 		plen = frame[1];
 		pi   = frame[2];
@@ -793,13 +776,15 @@
 
 		DEBUG(4, __FUNCTION__ "(), RxMaxSduSize=%d\n", 
 		      self->tx_max_sdu_size);
+		
+		/* Remove parameters */
+		ASSERT(skb->len >= (plen+1), return;);
+		skb_pull(skb, plen+1);
 	}
 	
 	DEBUG(4, __FUNCTION__ "() send=%d,avail=%d,remote=%d\n", 
 	      self->send_credit, self->avail_credit, self->remote_credit);
 
-	skb_pull(skb, TTP_HEADER);
-
 	if (self->notify.connect_confirm) {
 		self->notify.connect_confirm(self->notify.instance, self, qos,
 					     self->tx_max_sdu_size, 
@@ -814,7 +799,7 @@
  *
  */
 void irttp_connect_indication(void *instance, void *sap, struct qos_info *qos,
-			      __u32 max_seg_size,  __u8 max_header_size, 
+			      __u32 max_seg_size, __u8 max_header_size, 
 			      struct sk_buff *skb) 
 {
 	struct tsap_cb *self;
@@ -847,7 +832,11 @@
 	self->send_credit = n;
 	self->tx_max_sdu_size = 0;
 	
-	parameters = frame[0] & 0x80;	
+	parameters = frame[0] & 0x80;
+
+	ASSERT(skb->len >= TTP_HEADER, return;);
+	skb_pull(skb, TTP_HEADER);
+		
 	if (parameters) {
 		DEBUG(3, __FUNCTION__ "(), Contains parameters!\n");
 		plen = frame[1];
@@ -871,7 +860,10 @@
 			       "() illegal value length for max_sdu_size!\n");
 			self->tx_max_sdu_size = 0;
 		};
-
+		
+		/* Remove parameters */
+		ASSERT(skb->len >= (plen+1), return;);
+		skb_pull(skb, plen+1);
 
 		DEBUG(3, __FUNCTION__ "(), MaxSduSize=%d\n", 
 		      self->tx_max_sdu_size);
@@ -879,8 +871,6 @@
 
 	DEBUG(4, __FUNCTION__ "(), initial send_credit=%d\n", n);
 
-	skb_pull(skb, 1); /* Remove TTP header */
-
 	if (self->notify.connect_indication) {
 		self->notify.connect_indication(self->notify.instance, self, 
 						qos, self->rx_max_sdu_size, 
@@ -1403,11 +1393,9 @@
 	irttp_run_tx_queue(self);
 
 	/*  Give avay some credits to peer?  */
-	if ((skb_queue_empty(&self->tx_queue)) && 
-	    (self->remote_credit < LOW_THRESHOLD) && 
-	    (self->avail_credit > 0)) 
+	if ((self->remote_credit < LOW_THRESHOLD) && 
+	    (self->avail_credit > 0) && (skb_queue_empty(&self->tx_queue)))
 	{
-		DEBUG(4, __FUNCTION__ "(), sending credit!\n");
 		irttp_give_credit(self);
 	}
 
diff -u --recursive --new-file v2.3.5/linux/net/irda/wrapper.c linux/net/irda/wrapper.c
--- v2.3.5/linux/net/irda/wrapper.c	Wed Jun  2 14:44:39 1999
+++ linux/net/irda/wrapper.c	Mon Jun  7 16:18:58 1999
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Aug  4 20:40:53 1997
- * Modified at:   Sun May  2 21:58:00 1999
+ * Modified at:   Fri May 28 20:30:24 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Modified at:   Fri May 28  3:11 CST 1999
  * Modified by:   Horst von Brand <vonbrand@sleipnir.valparaiso.cl>
@@ -219,7 +219,7 @@
 /*
  * Function state_begin_frame (idev, byte)
  *
- *    
+ *    Begin of frame detected
  *
  */
 static void state_begin_frame(struct irda_device *idev, __u8 byte)
@@ -231,6 +231,10 @@
 	case CE:
 		/* Stuffed byte */
 		idev->rx_buff.state = LINK_ESCAPE;
+
+		/* Time to initialize receive buffer */
+		idev->rx_buff.data = idev->rx_buff.head;
+		idev->rx_buff.len = 0;
 		break;
 	case EOF:
 		/* Abort frame */
@@ -239,11 +243,11 @@
 		idev->stats.rx_errors++;
 		idev->stats.rx_frame_errors++;
 		break;
-	default:
-		/* Got first byte of frame */
+	default:	
+		/* Time to initialize receive buffer */
 		idev->rx_buff.data = idev->rx_buff.head;
 		idev->rx_buff.len = 0;
-		
+
 		idev->rx_buff.data[idev->rx_buff.len++] = byte;
 		
 		idev->rx_buff.fcs = irda_fcs(INIT_FCS, byte);
@@ -293,7 +297,7 @@
 /*
  * Function state_inside_frame (idev, byte)
  *
- *    
+ *    Handle bytes received within a frame
  *
  */
 static void state_inside_frame(struct irda_device *idev, __u8 byte)
diff -u --recursive --new-file v2.3.5/linux/net/netrom/nr_route.c linux/net/netrom/nr_route.c
--- v2.3.5/linux/net/netrom/nr_route.c	Mon May 31 22:28:07 1999
+++ linux/net/netrom/nr_route.c	Wed Jun  9 14:45:37 1999
@@ -564,13 +564,13 @@
 {
 	struct device *dev, *first = NULL;
 
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM)
 			if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
 				first = dev;
 	}
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 
 	return first;
 }
@@ -582,13 +582,13 @@
 {
 	struct device *dev;
 
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0)
 			goto out;
 	}
 out:
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 	return dev;
 }
 
diff -u --recursive --new-file v2.3.5/linux/net/netsyms.c linux/net/netsyms.c
--- v2.3.5/linux/net/netsyms.c	Mon May 31 22:28:07 1999
+++ linux/net/netsyms.c	Wed Jun  9 14:45:37 1999
@@ -170,7 +170,6 @@
 
 EXPORT_SYMBOL(neigh_table_init);
 EXPORT_SYMBOL(neigh_table_clear);
-EXPORT_SYMBOL(__neigh_lookup);
 EXPORT_SYMBOL(neigh_resolve_output);
 EXPORT_SYMBOL(neigh_connected_output);
 EXPORT_SYMBOL(neigh_update);
@@ -388,8 +387,7 @@
 EXPORT_SYMBOL(dev_set_allmulti);
 EXPORT_SYMBOL(dev_set_promiscuity);
 EXPORT_SYMBOL(sklist_remove_socket);
-EXPORT_SYMBOL(rtnl_wait);
-EXPORT_SYMBOL(rtnl_rlockct);
+EXPORT_SYMBOL(rtnl_sem);
 EXPORT_SYMBOL(rtnl_lock);
 EXPORT_SYMBOL(rtnl_unlock);
 
diff -u --recursive --new-file v2.3.5/linux/net/packet/af_packet.c linux/net/packet/af_packet.c
--- v2.3.5/linux/net/packet/af_packet.c	Sun Mar 21 07:22:00 1999
+++ linux/net/packet/af_packet.c	Wed Jun  9 14:45:37 1999
@@ -5,7 +5,7 @@
  *
  *		PACKET - implements raw packet sockets.
  *
- * Version:	$Id: af_packet.c,v 1.19 1999/03/21 05:23:03 davem Exp $
+ * Version:	$Id: af_packet.c,v 1.20 1999/06/09 10:11:32 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -286,26 +286,27 @@
 	else
 		return(-ENOTCONN);	/* SOCK_PACKET must be sent giving an address */
 
+	dev_lock_list();
+
 	/*
 	 *	Find the device first to size check it 
 	 */
 
 	saddr->spkt_device[13] = 0;
 	dev = dev_get(saddr->spkt_device);
-	if (dev == NULL) 
-	{
-		return(-ENODEV);
-  	}
+	err = -ENODEV;
+	if (dev == NULL)
+		goto out_unlock;
 	
 	/*
 	 *	You may not queue a frame bigger than the mtu. This is the lowest level
 	 *	raw protocol and you must do your own fragmentation at this level.
 	 */
 	 
-	if(len>dev->mtu+dev->hard_header_len)
-  		return -EMSGSIZE;
+	err = -EMSGSIZE;
+ 	if(len>dev->mtu+dev->hard_header_len)
+		goto out_unlock;
 
-	dev_lock_list();
 	err = -ENOBUFS;
 	skb = sock_wmalloc(sk, len+dev->hard_header_len+15, 0, GFP_KERNEL);
 
@@ -351,8 +352,8 @@
 	 *	Now send it
 	 */
 
-	dev_unlock_list();
 	dev_queue_xmit(skb);
+	dev_unlock_list();
 	return(len);
 
 out_free:
@@ -455,16 +456,18 @@
 		addr	= saddr->sll_addr;
 	}
 
+	dev_lock_list();
 	dev = dev_get_by_index(ifindex);
+	err = -ENXIO;
 	if (dev == NULL)
-		return -ENXIO;
+		goto out_unlock;
 	if (sock->type == SOCK_RAW)
 		reserve = dev->hard_header_len;
 
+	err = -EMSGSIZE;
 	if (len > dev->mtu+reserve)
-  		return -EMSGSIZE;
+		goto out_unlock;
 
-	dev_lock_list();
 
 	skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 0, 
 				msg->msg_flags & MSG_DONTWAIT, &err);
@@ -501,8 +504,8 @@
 	 *	Now send it
 	 */
 
-	dev_unlock_list();
 	dev_queue_xmit(skb);
+	dev_unlock_list();
 	return(len);
 
 out_free:
diff -u --recursive --new-file v2.3.5/linux/net/rose/rose_route.c linux/net/rose/rose_route.c
--- v2.3.5/linux/net/rose/rose_route.c	Mon May 31 22:28:07 1999
+++ linux/net/rose/rose_route.c	Wed Jun  9 14:45:37 1999
@@ -543,13 +543,13 @@
 {
 	struct device *dev, *first = NULL;
 
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE)
 			if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
 				first = dev;
 	}
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 
 	return first;
 }
@@ -561,13 +561,13 @@
 {
 	struct device *dev;
 
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0)
 			goto out;
 	}
 out:
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 	return dev;
 }
 
diff -u --recursive --new-file v2.3.5/linux/net/sched/cls_api.c linux/net/sched/cls_api.c
--- v2.3.5/linux/net/sched/cls_api.c	Thu Mar 25 09:23:34 1999
+++ linux/net/sched/cls_api.c	Wed Jun  9 14:45:37 1999
@@ -39,20 +39,24 @@
 
 static struct tcf_proto_ops *tcf_proto_base;
 
+/* Protects list of registered TC modules. It is pure SMP lock. */
+static rwlock_t cls_mod_lock = RW_LOCK_UNLOCKED;
 
 /* Find classifier type by string name */
 
 struct tcf_proto_ops * tcf_proto_lookup_ops(struct rtattr *kind)
 {
-	struct tcf_proto_ops *t;
+	struct tcf_proto_ops *t = NULL;
 
 	if (kind) {
+		read_lock(&cls_mod_lock);
 		for (t = tcf_proto_base; t; t = t->next) {
 			if (rtattr_strcmp(kind, t->kind) == 0)
-				return t;
+				break;
 		}
+		read_unlock(&cls_mod_lock);
 	}
-	return NULL;
+	return t;
 }
 
 /* Register(unregister) new classifier type */
@@ -61,12 +65,17 @@
 {
 	struct tcf_proto_ops *t, **tp;
 
-	for (tp = &tcf_proto_base; (t=*tp) != NULL; tp = &t->next)
-		if (strcmp(ops->kind, t->kind) == 0)
+	write_lock(&cls_mod_lock);
+	for (tp = &tcf_proto_base; (t=*tp) != NULL; tp = &t->next) {
+		if (strcmp(ops->kind, t->kind) == 0) {
+			write_unlock(&cls_mod_lock);
 			return -EEXIST;
+		}
+	}
 
 	ops->next = NULL;
 	*tp = ops;
+	write_unlock(&cls_mod_lock);
 	return 0;
 }
 
@@ -74,13 +83,17 @@
 {
 	struct tcf_proto_ops *t, **tp;
 
+	write_lock(&cls_mod_lock);
 	for (tp = &tcf_proto_base; (t=*tp) != NULL; tp = &t->next)
 		if (t == ops)
 			break;
 
-	if (!t)
+	if (!t) {
+		write_unlock(&cls_mod_lock);
 		return -ENOENT;
+	}
 	*tp = t->next;
+	write_unlock(&cls_mod_lock);
 	return 0;
 }
 
@@ -217,8 +230,12 @@
 			kfree(tp);
 			goto errout;
 		}
+		write_lock(&qdisc_tree_lock);
+		spin_lock_bh(&dev->queue_lock);
 		tp->next = *back;
 		*back = tp;
+		spin_unlock_bh(&dev->queue_lock);
+		write_unlock(&qdisc_tree_lock);
 	} else if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], tp->ops->kind))
 		goto errout;
 
@@ -226,8 +243,11 @@
 
 	if (fh == 0) {
 		if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
+			write_lock(&qdisc_tree_lock);
+			spin_lock_bh(&dev->queue_lock);
 			*back = tp->next;
-			synchronize_bh();
+			spin_unlock_bh(&dev->queue_lock);
+			write_unlock(&qdisc_tree_lock);
 
 			tp->ops->destroy(tp);
 			kfree(tp);
@@ -344,12 +364,16 @@
 		return skb->len;
 	if ((dev = dev_get_by_index(tcm->tcm_ifindex)) == NULL)
 		return skb->len;
+
+	read_lock(&qdisc_tree_lock);
 	if (!tcm->tcm_parent)
 		q = dev->qdisc_sleeping;
 	else
 		q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
-	if (q == NULL)
+	if (q == NULL) {
+		read_unlock(&qdisc_tree_lock);
 		return skb->len;
+	}
 	if ((cops = q->ops->cl_ops) == NULL)
 		goto errout;
 	if (TC_H_MIN(tcm->tcm_parent)) {
@@ -400,6 +424,7 @@
 	if (cl)
 		cops->put(q, cl);
 
+	read_unlock(&qdisc_tree_lock);
 	return skb->len;
 }
 
diff -u --recursive --new-file v2.3.5/linux/net/sched/cls_fw.c linux/net/sched/cls_fw.c
--- v2.3.5/linux/net/sched/cls_fw.c	Thu Apr 22 19:45:20 1999
+++ linux/net/sched/cls_fw.c	Wed Jun  9 14:45:37 1999
@@ -136,7 +136,7 @@
 			unsigned long cl;
 			head->ht[h] = f->next;
 
-			if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+			if ((cl = __cls_set_class(&f->res.class, 0)) != 0)
 				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 #ifdef CONFIG_NET_CLS_POLICE
 			tcf_police_release(f->police);
@@ -161,10 +161,11 @@
 		if (*fp == f) {
 			unsigned long cl;
 
+			tcf_tree_lock(tp);
 			*fp = f->next;
-			synchronize_bh();
+			tcf_tree_unlock(tp);
 
-			if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+			if ((cl = cls_set_class(tp, &f->res.class, 0)) != 0)
 				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 #ifdef CONFIG_NET_CLS_POLICE
 			tcf_police_release(f->police);
@@ -203,7 +204,7 @@
 
 			f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]);
 			cl = tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid);
-			cl = cls_set_class(&f->res.class, cl);
+			cl = cls_set_class(tp, &f->res.class, cl);
 			if (cl)
 				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 		}
@@ -211,8 +212,9 @@
 		if (tb[TCA_FW_POLICE-1]) {
 			struct tcf_police *police = tcf_police_locate(tb[TCA_FW_POLICE-1], tca[TCA_RATE-1]);
 
+			tcf_tree_lock(tp);
 			police = xchg(&f->police, police);
-			synchronize_bh();
+			tcf_tree_unlock(tp);
 
 			tcf_police_release(police);
 		}
@@ -229,8 +231,9 @@
 			return -ENOBUFS;
 		memset(head, 0, sizeof(*head));
 
+		tcf_tree_lock(tp);
 		tp->root = head;
-		synchronize_bh();
+		tcf_tree_unlock(tp);
 	}
 
 	f = kmalloc(sizeof(struct fw_filter), GFP_KERNEL);
@@ -245,7 +248,7 @@
 		if (RTA_PAYLOAD(tb[TCA_FW_CLASSID-1]) != 4)
 			goto errout;
 		f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]);
-		cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
+		cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
 	}
 
 #ifdef CONFIG_NET_CLS_POLICE
@@ -254,8 +257,9 @@
 #endif
 
 	f->next = head->ht[fw_hash(handle)];
-	wmb();
+	tcf_tree_lock(tp);
 	head->ht[fw_hash(handle)] = f;
+	tcf_tree_unlock(tp);
 
 	*arg = (unsigned long)f;
 	return 0;
@@ -335,7 +339,8 @@
 	rta->rta_len = skb->tail - b;
 #ifdef CONFIG_NET_CLS_POLICE
 	if (f->police) {
-		RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &f->police->stats);
+		if (qdisc_copy_stats(skb, &f->police->stats))
+			goto rtattr_failure;
 	}
 #endif
 	return skb->len;
diff -u --recursive --new-file v2.3.5/linux/net/sched/cls_route.c linux/net/sched/cls_route.c
--- v2.3.5/linux/net/sched/cls_route.c	Thu Mar 25 09:23:34 1999
+++ linux/net/sched/cls_route.c	Wed Jun  9 14:45:37 1999
@@ -83,11 +83,11 @@
 	return id&0xF;
 }
 
-static void route4_reset_fastmap(struct route4_head *head, u32 id)
+static void route4_reset_fastmap(struct device *dev, struct route4_head *head, u32 id)
 {
-	start_bh_atomic();
+	spin_lock_bh(&dev->queue_lock);
 	memset(head->fastmap, 0, sizeof(head->fastmap));
-	end_bh_atomic();
+	spin_unlock_bh(&dev->queue_lock);
 }
 
 static void __inline__
@@ -297,7 +297,7 @@
 					unsigned long cl;
 
 					b->ht[h2] = f->next;
-					if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+					if ((cl = __cls_set_class(&f->res.class, 0)) != 0)
 						tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 #ifdef CONFIG_NET_CLS_POLICE
 					tcf_police_release(f->police);
@@ -329,12 +329,13 @@
 		if (*fp == f) {
 			unsigned long cl;
 
+			tcf_tree_lock(tp);
 			*fp = f->next;
-			synchronize_bh();
+			tcf_tree_unlock(tp);
 
-			route4_reset_fastmap(head, f->id);
+			route4_reset_fastmap(tp->q->dev, head, f->id);
 
-			if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+			if ((cl = cls_set_class(tp, &f->res.class, 0)) != 0)
 				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 
 #ifdef CONFIG_NET_CLS_POLICE
@@ -349,8 +350,9 @@
 					return 0;
 
 			/* OK, session has no flows */
+			tcf_tree_lock(tp);
 			head->table[to_hash(h)] = NULL;
-			synchronize_bh();
+			tcf_tree_unlock(tp);
 
 			kfree(b);
 			return 0;
@@ -387,7 +389,7 @@
 			unsigned long cl;
 
 			f->res.classid = *(u32*)RTA_DATA(tb[TCA_ROUTE4_CLASSID-1]);
-			cl = cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
+			cl = cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
 			if (cl)
 				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 		}
@@ -395,8 +397,9 @@
 		if (tb[TCA_ROUTE4_POLICE-1]) {
 			struct tcf_police *police = tcf_police_locate(tb[TCA_ROUTE4_POLICE-1], tca[TCA_RATE-1]);
 
+			tcf_tree_lock(tp);
 			police = xchg(&f->police, police);
-			synchronize_bh();
+			tcf_tree_unlock(tp);
 
 			tcf_police_release(police);
 		}
@@ -412,8 +415,9 @@
 			return -ENOBUFS;
 		memset(head, 0, sizeof(struct route4_head));
 
+		tcf_tree_lock(tp);
 		tp->root = head;
-		synchronize_bh();
+		tcf_tree_unlock(tp);
 	}
 
 	f = kmalloc(sizeof(struct route4_filter), GFP_KERNEL);
@@ -475,8 +479,9 @@
 			goto errout;
 		memset(b, 0, sizeof(*b));
 
+		tcf_tree_lock(tp);
 		head->table[h1] = b;
-		synchronize_bh();
+		tcf_tree_unlock(tp);
 	}
 	f->bkt = b;
 
@@ -489,17 +494,18 @@
 			goto errout;
 	}
 
-	cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
+	cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
 #ifdef CONFIG_NET_CLS_POLICE
 	if (tb[TCA_ROUTE4_POLICE-1])
 		f->police = tcf_police_locate(tb[TCA_ROUTE4_POLICE-1], tca[TCA_RATE-1]);
 #endif
 
 	f->next = f1;
-	wmb();
+	tcf_tree_lock(tp);
 	*ins_f = f;
+	tcf_tree_unlock(tp);
 
-	route4_reset_fastmap(head, f->id);
+	route4_reset_fastmap(tp->q->dev, head, f->id);
 	*arg = (unsigned long)f;
 	return 0;
 
@@ -589,7 +595,8 @@
 	rta->rta_len = skb->tail - b;
 #ifdef CONFIG_NET_CLS_POLICE
 	if (f->police) {
-		RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &f->police->stats);
+		if (qdisc_copy_stats(skb, &f->police->stats))
+			goto rtattr_failure;
 	}
 #endif
 	return skb->len;
diff -u --recursive --new-file v2.3.5/linux/net/sched/cls_rsvp.h linux/net/sched/cls_rsvp.h
--- v2.3.5/linux/net/sched/cls_rsvp.h	Thu Apr 22 19:45:20 1999
+++ linux/net/sched/cls_rsvp.h	Wed Jun  9 14:45:37 1999
@@ -282,7 +282,7 @@
 					unsigned long cl;
 
 					s->ht[h2] = f->next;
-					if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+					if ((cl = __cls_set_class(&f->res.class, 0)) != 0)
 						tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 #ifdef CONFIG_NET_CLS_POLICE
 					tcf_police_release(f->police);
@@ -310,10 +310,11 @@
 			unsigned long cl;
 
 
+			tcf_tree_lock(tp);
 			*fp = f->next;
-			synchronize_bh();
+			tcf_tree_unlock(tp);
 
-			if ((cl = cls_set_class(&f->res.class, 0)) != 0)
+			if ((cl = cls_set_class(tp, &f->res.class, 0)) != 0)
 				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 
 #ifdef CONFIG_NET_CLS_POLICE
@@ -332,8 +333,9 @@
 			for (sp = &((struct rsvp_head*)tp->root)->ht[h&0xFF];
 			     *sp; sp = &(*sp)->next) {
 				if (*sp == s) {
+					tcf_tree_lock(tp);
 					*sp = s->next;
-					synchronize_bh();
+					tcf_tree_unlock(tp);
 
 					kfree(s);
 					return 0;
@@ -446,7 +448,7 @@
 			unsigned long cl;
 
 			f->res.classid = *(u32*)RTA_DATA(tb[TCA_RSVP_CLASSID-1]);
-			cl = cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
+			cl = cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
 			if (cl)
 				tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 		}
@@ -454,8 +456,9 @@
 		if (tb[TCA_RSVP_POLICE-1]) {
 			struct tcf_police *police = tcf_police_locate(tb[TCA_RSVP_POLICE-1], tca[TCA_RATE-1]);
 
+			tcf_tree_lock(tp);
 			police = xchg(&f->police, police);
-			synchronize_bh();
+			tcf_tree_unlock(tp);
 
 			tcf_police_release(police);
 		}
@@ -536,7 +539,7 @@
 
 			f->sess = s;
 			if (f->tunnelhdr == 0)
-				cls_set_class(&f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
+				cls_set_class(tp, &f->res.class, tp->q->ops->cl_ops->bind_tcf(tp->q, base, f->res.classid));
 #ifdef CONFIG_NET_CLS_POLICE
 			if (tb[TCA_RSVP_POLICE-1])
 				f->police = tcf_police_locate(tb[TCA_RSVP_POLICE-1], tca[TCA_RATE-1]);
@@ -659,7 +662,8 @@
 	rta->rta_len = skb->tail - b;
 #ifdef CONFIG_NET_CLS_POLICE
 	if (f->police) {
-		RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &f->police->stats);
+		if (qdisc_copy_stats(skb, &f->police->stats))
+			goto rtattr_failure;
 	}
 #endif
 	return skb->len;
diff -u --recursive --new-file v2.3.5/linux/net/sched/cls_u32.c linux/net/sched/cls_u32.c
--- v2.3.5/linux/net/sched/cls_u32.c	Thu Mar 25 09:23:34 1999
+++ linux/net/sched/cls_u32.c	Wed Jun  9 14:45:37 1999
@@ -307,7 +307,7 @@
 {
 	unsigned long cl;
 
-	if ((cl = cls_set_class(&n->res.class, 0)) != 0)
+	if ((cl = __cls_set_class(&n->res.class, 0)) != 0)
 		tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
 #ifdef CONFIG_NET_CLS_POLICE
 	tcf_police_release(n->police);
@@ -326,8 +326,9 @@
 	if (ht) {
 		for (kp = &ht->ht[TC_U32_HASH(key->handle)]; *kp; kp = &(*kp)->next) {
 			if (*kp == key) {
+				tcf_tree_lock(tp);
 				*kp = key->next;
-				synchronize_bh();
+				tcf_tree_unlock(tp);
 
 				u32_destroy_key(tp, key);
 				return 0;
@@ -346,7 +347,6 @@
 	for (h=0; h<=ht->divisor; h++) {
 		while ((n = ht->ht[h]) != NULL) {
 			ht->ht[h] = n->next;
-			synchronize_bh();
 
 			u32_destroy_key(tp, n);
 		}
@@ -465,8 +465,9 @@
 			ht_down->refcnt++;
 		}
 
+		sch_tree_lock(q);
 		ht_down = xchg(&n->ht_down, ht_down);
-		synchronize_bh();
+		sch_tree_unlock(q);
 
 		if (ht_down)
 			ht_down->refcnt--;
@@ -475,7 +476,9 @@
 		unsigned long cl;
 
 		n->res.classid = *(u32*)RTA_DATA(tb[TCA_U32_CLASSID-1]);
-		cl = cls_set_class(&n->res.class, q->ops->cl_ops->bind_tcf(q, base, n->res.classid));
+		sch_tree_lock(q);
+		cl = __cls_set_class(&n->res.class, q->ops->cl_ops->bind_tcf(q, base, n->res.classid));
+		sch_tree_unlock(q);
 		if (cl)
 			q->ops->cl_ops->unbind_tcf(q, cl);
 	}
@@ -483,8 +486,9 @@
 	if (tb[TCA_U32_POLICE-1]) {
 		struct tcf_police *police = tcf_police_locate(tb[TCA_U32_POLICE-1], est);
 
+		sch_tree_lock(q);
 		police = xchg(&n->police, police);
-		synchronize_bh();
+		sch_tree_lock(q);
 
 		tcf_police_release(police);
 	}
@@ -682,7 +686,8 @@
 	rta->rta_len = skb->tail - b;
 #ifdef CONFIG_NET_CLS_POLICE
 	if (TC_U32_KEY(n->handle) && n->police) {
-		RTA_PUT(skb, TCA_STATS, sizeof(struct tc_stats), &n->police->stats);
+		if (qdisc_copy_stats(skb, &n->police->stats))
+			goto rtattr_failure;
 	}
 #endif
 	return skb->len;
diff -u --recursive --new-file v2.3.5/linux/net/sched/estimator.c linux/net/sched/estimator.c
--- v2.3.5/linux/net/sched/estimator.c	Thu Mar 25 09:23:34 1999
+++ linux/net/sched/estimator.c	Wed Jun  9 14:45:37 1999
@@ -97,29 +97,38 @@
 
 static struct qdisc_estimator_head elist[EST_MAX_INTERVAL+1];
 
+/* Estimator array lock */
+static rwlock_t est_lock = RW_LOCK_UNLOCKED;
+
 static void est_timer(unsigned long arg)
 {
 	int idx = (int)arg;
 	struct qdisc_estimator *e;
 
+	read_lock(&est_lock);
 	for (e = elist[idx].list; e; e = e->next) {
-		u64 nbytes = e->stats->bytes;
-		u32 npackets = e->stats->packets;
+		struct tc_stats *st = e->stats;
+		u64 nbytes;
+		u32 npackets;
 		u32 rate;
-		
+
+		spin_lock(st->lock);
+		nbytes = st->bytes;
+		npackets = st->packets;
 		rate = (nbytes - e->last_bytes)<<(7 - idx);
 		e->last_bytes = nbytes;
 		e->avbps += ((long)rate - (long)e->avbps) >> e->ewma_log;
-		e->stats->bps = (e->avbps+0xF)>>5;
+		st->bps = (e->avbps+0xF)>>5;
 
 		rate = (npackets - e->last_packets)<<(12 - idx);
 		e->last_packets = npackets;
 		e->avpps += ((long)rate - (long)e->avpps) >> e->ewma_log;
 		e->stats->pps = (e->avpps+0x1FF)>>10;
+		spin_unlock(st->lock);
 	}
 
-	elist[idx].timer.expires = jiffies + ((HZ/4)<<idx);
-	add_timer(&elist[idx].timer);
+	mod_timer(&elist[idx].timer, jiffies + ((HZ/4)<<idx));
+	read_unlock(&est_lock);
 }
 
 int qdisc_new_estimator(struct tc_stats *stats, struct rtattr *opt)
@@ -154,7 +163,9 @@
 		elist[est->interval].timer.function = est_timer;
 		add_timer(&elist[est->interval].timer);
 	}
+	write_lock_bh(&est_lock);
 	elist[est->interval].list = est;
+	write_unlock_bh(&est_lock);
 	return 0;
 }
 
@@ -172,8 +183,9 @@
 				continue;
 			}
 
+			write_lock_bh(&est_lock);
 			*pest = est->next;
-			synchronize_bh();
+			write_unlock_bh(&est_lock);
 
 			kfree(est);
 			killed++;
diff -u --recursive --new-file v2.3.5/linux/net/sched/police.c linux/net/sched/police.c
--- v2.3.5/linux/net/sched/police.c	Sun Mar 21 07:22:00 1999
+++ linux/net/sched/police.c	Wed Jun  9 14:45:37 1999
@@ -38,6 +38,10 @@
 
 static u32 idx_gen;
 static struct tcf_police *tcf_police_ht[16];
+/* Policer hash table lock */
+static rwlock_t police_lock = RW_LOCK_UNLOCKED;
+
+/* Each policer is serialized by its individual spinlock */
 
 static __inline__ unsigned tcf_police_hash(u32 index)
 {
@@ -48,11 +52,13 @@
 {
 	struct tcf_police *p;
 
+	read_lock(&police_lock);
 	for (p = tcf_police_ht[tcf_police_hash(index)]; p; p = p->next) {
 		if (p->index == index)
-			return p;
+			break;
 	}
-	return NULL;
+	read_unlock(&police_lock);
+	return p;
 }
 
 static __inline__ u32 tcf_police_new_index(void)
@@ -73,7 +79,9 @@
 	
 	for (p1p = &tcf_police_ht[h]; *p1p; p1p = &(*p1p)->next) {
 		if (*p1p == p) {
+			write_lock_bh(&police_lock);
 			*p1p = p->next;
+			write_unlock_bh(&police_lock);
 #ifdef CONFIG_NET_ESTIMATOR
 			qdisc_kill_estimator(&p->stats);
 #endif
@@ -114,6 +122,8 @@
 
 	memset(p, 0, sizeof(*p));
 	p->refcnt = 1;
+	spin_lock_init(&p->lock);
+	p->stats.lock = &p->lock;
 	if (parm->rate.rate) {
 		if ((p->R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1])) == NULL)
 			goto failure;
@@ -144,8 +154,10 @@
 		qdisc_new_estimator(&p->stats, est);
 #endif
 	h = tcf_police_hash(p->index);
+	write_lock_bh(&police_lock);
 	p->next = tcf_police_ht[h];
 	tcf_police_ht[h] = p;
+	write_unlock_bh(&police_lock);
 	return p;
 
 failure:
@@ -161,19 +173,24 @@
 	long toks;
 	long ptoks = 0;
 
+	spin_lock(&p->lock);
+
 	p->stats.bytes += skb->len;
 	p->stats.packets++;
 
 #ifdef CONFIG_NET_ESTIMATOR
 	if (p->ewma_rate && p->stats.bps >= p->ewma_rate) {
 		p->stats.overlimits++;
+		spin_unlock(&p->lock);
 		return p->action;
 	}
 #endif
 
 	if (skb->len <= p->mtu) {
-		if (p->R_tab == NULL)
+		if (p->R_tab == NULL) {
+			spin_unlock(&p->lock);
 			return p->result;
+		}
 
 		PSCHED_GET_TIME(now);
 
@@ -194,11 +211,13 @@
 			p->t_c = now;
 			p->toks = toks;
 			p->ptoks = ptoks;
+			spin_unlock(&p->lock);
 			return p->result;
 		}
 	}
 
 	p->stats.overlimits++;
+	spin_unlock(&p->lock);
 	return p->action;
 }
 
diff -u --recursive --new-file v2.3.5/linux/net/sched/sch_api.c linux/net/sched/sch_api.c
--- v2.3.5/linux/net/sched/sch_api.c	Mon May 31 22:28:07 1999
+++ linux/net/sched/sch_api.c	Wed Jun  9 14:45:37 1999
@@ -124,6 +124,10 @@
    changes qdisc parameters.
  */
 
+/* Protects list of registered TC modules. It is pure SMP lock. */
+static rwlock_t qdisc_mod_lock = RW_LOCK_UNLOCKED;
+
+
 /************************************************
  *	Queueing disciplines manipulation.	*
  ************************************************/
@@ -139,9 +143,13 @@
 {
 	struct Qdisc_ops *q, **qp;
 
-	for (qp = &qdisc_base; (q=*qp)!=NULL; qp = &q->next)
-		if (strcmp(qops->id, q->id) == 0)
+	write_lock(&qdisc_mod_lock);
+	for (qp = &qdisc_base; (q=*qp)!=NULL; qp = &q->next) {
+		if (strcmp(qops->id, q->id) == 0) {
+			write_unlock(&qdisc_mod_lock);
 			return -EEXIST;
+		}
+	}
 
 	if (qops->enqueue == NULL)
 		qops->enqueue = noop_qdisc_ops.enqueue;
@@ -152,20 +160,26 @@
 
 	qops->next = NULL;
 	*qp = qops;
+	write_unlock(&qdisc_mod_lock);
 	return 0;
 }
 
 int unregister_qdisc(struct Qdisc_ops *qops)
 {
 	struct Qdisc_ops *q, **qp;
+	int err = -ENOENT;
+
+	write_lock(&qdisc_mod_lock);
 	for (qp = &qdisc_base; (q=*qp)!=NULL; qp = &q->next)
 		if (q == qops)
 			break;
-	if (!q)
-		return -ENOENT;
-	*qp = q->next;
-	q->next = NULL;
-	return 0;
+	if (q) {
+		*qp = q->next;
+		q->next = NULL;
+		err = 0;
+	}
+	write_unlock(&qdisc_mod_lock);
+	return err;
 }
 
 /* We know handle. Find qdisc among all qdisc's attached to device
@@ -203,15 +217,17 @@
 
 struct Qdisc_ops *qdisc_lookup_ops(struct rtattr *kind)
 {
-	struct Qdisc_ops *q;
+	struct Qdisc_ops *q = NULL;
 
 	if (kind) {
+		read_lock(&qdisc_mod_lock);
 		for (q = qdisc_base; q; q = q->next) {
 			if (rtattr_strcmp(kind, q->id) == 0)
-				return q;
+				break;
 		}
+		read_unlock(&qdisc_mod_lock);
 	}
-	return NULL;
+	return q;
 }
 
 static struct qdisc_rate_table *qdisc_rtab_list;
@@ -284,7 +300,8 @@
 	if (dev->flags & IFF_UP)
 		dev_deactivate(dev);
 
-	start_bh_atomic();
+	write_lock(&qdisc_tree_lock);
+	spin_lock_bh(&dev->queue_lock);
 	oqdisc = dev->qdisc_sleeping;
 
 	/* Prune old scheduler */
@@ -296,7 +313,8 @@
 		qdisc = &noop_qdisc;
 	dev->qdisc_sleeping = qdisc;
 	dev->qdisc = &noop_qdisc;
-	end_bh_atomic();
+	spin_unlock_bh(&dev->queue_lock);
+	write_unlock(&qdisc_tree_lock);
 
 	if (dev->flags & IFF_UP)
 		dev_activate(dev);
@@ -376,7 +394,7 @@
 		goto err_out;
 
 	/* Grrr... Resolve race condition with module unload */
-	
+
 	err = -EINVAL;
 	if (ops != qdisc_lookup_ops(kind))
 		goto err_out;
@@ -389,6 +407,7 @@
 	sch->dequeue = ops->dequeue;
 	sch->dev = dev;
 	atomic_set(&sch->refcnt, 1);
+	sch->stats.lock = &dev->queue_lock;
 	if (handle == 0) {
 		handle = qdisc_alloc_handle(dev);
 		err = -ENOMEM;
@@ -398,8 +417,10 @@
 	sch->handle = handle;
 
 	if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS-1])) == 0) {
+		write_lock(&qdisc_tree_lock);
 		sch->next = dev->qdisc_list;
 		dev->qdisc_list = sch;
+		write_unlock(&qdisc_tree_lock);
 #ifdef CONFIG_NET_ESTIMATOR
 		if (tca[TCA_RATE-1])
 			qdisc_new_estimator(&sch->stats, tca[TCA_RATE-1]);
@@ -521,7 +542,9 @@
 			return err;
 		if (q) {
 			qdisc_notify(skb, n, clid, q, NULL);
+			spin_lock_bh(&dev->queue_lock);
 			qdisc_destroy(q);
+			spin_unlock_bh(&dev->queue_lock);
 		}
 	} else {
 		qdisc_notify(skb, n, clid, NULL, q);
@@ -637,17 +660,36 @@
 		struct Qdisc *old_q = NULL;
 		err = qdisc_graft(dev, p, clid, q, &old_q);
 		if (err) {
-			if (q)
+			if (q) {
+				spin_lock_bh(&dev->queue_lock);
 				qdisc_destroy(q);
+				spin_unlock_bh(&dev->queue_lock);
+			}
 			return err;
 		}
 		qdisc_notify(skb, n, clid, old_q, q);
-		if (old_q)
+		if (old_q) {
+			spin_lock_bh(&dev->queue_lock);
 			qdisc_destroy(old_q);
+			spin_unlock_bh(&dev->queue_lock);
+		}
 	}
 	return 0;
 }
 
+int qdisc_copy_stats(struct sk_buff *skb, struct tc_stats *st)
+{
+	spin_lock_bh(st->lock);
+	RTA_PUT(skb, TCA_STATS, (char*)&st->lock - (char*)st, st);
+	spin_unlock_bh(st->lock);
+	return 0;
+
+rtattr_failure:
+	spin_unlock_bh(st->lock);
+	return -1;
+}
+
+
 static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
 			 u32 pid, u32 seq, unsigned flags, int event)
 {
@@ -667,7 +709,8 @@
 	if (q->ops->dump && q->ops->dump(q, skb) < 0)
 		goto rtattr_failure;
 	q->stats.qlen = q->q.qlen;
-	RTA_PUT(skb, TCA_STATS, sizeof(q->stats), &q->stats);
+	if (qdisc_copy_stats(skb, &q->stats))
+		goto rtattr_failure;
 	nlh->nlmsg_len = skb->tail - b;
 	return skb->len;
 
@@ -713,24 +756,28 @@
 
 	s_idx = cb->args[0];
 	s_q_idx = q_idx = cb->args[1];
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
 		if (idx < s_idx)
 			continue;
 		if (idx > s_idx)
 			s_q_idx = 0;
+		read_lock(&qdisc_tree_lock);
 		for (q = dev->qdisc_list, q_idx = 0; q;
 		     q = q->next, q_idx++) {
 			if (q_idx < s_q_idx)
 				continue;
 			if (tc_fill_qdisc(skb, q, 0, NETLINK_CB(cb->skb).pid,
-					  cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0)
+					  cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) {
+				read_unlock(&qdisc_tree_lock);
 				goto done;
+			}
 		}
+		read_unlock(&qdisc_tree_lock);
 	}
 
 done:
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 
 	cb->args[0] = idx;
 	cb->args[1] = q_idx;
@@ -936,6 +983,7 @@
 
 	s_t = cb->args[0];
 
+	read_lock(&qdisc_tree_lock);
 	for (q=dev->qdisc_list, t=0; q; q = q->next, t++) {
 		if (t < s_t) continue;
 		if (!q->ops->cl_ops) continue;
@@ -954,6 +1002,7 @@
 		if (arg.w.stop)
 			break;
 	}
+	read_unlock(&qdisc_tree_lock);
 
 	cb->args[0] = t;
 
diff -u --recursive --new-file v2.3.5/linux/net/sched/sch_cbq.c linux/net/sched/sch_cbq.c
--- v2.3.5/linux/net/sched/sch_cbq.c	Sat Apr 24 17:51:48 1999
+++ linux/net/sched/sch_cbq.c	Wed Jun  9 14:45:37 1999
@@ -1417,6 +1417,7 @@
 	q->link.ewma_log = TC_CBQ_DEF_EWMA;
 	q->link.avpkt = q->link.allot/2;
 	q->link.minidle = -0x7FFFFFFF;
+	q->link.stats.lock = &sch->dev->queue_lock;
 
 	init_timer(&q->wd_timer);
 	q->wd_timer.data = (unsigned long)sch;
@@ -1558,6 +1559,16 @@
 	return 0;
 }
 
+int cbq_copy_xstats(struct sk_buff *skb, struct tc_cbq_xstats *st)
+{
+	RTA_PUT(skb, TCA_STATS, sizeof(*st), st);
+	return 0;
+
+rtattr_failure:
+	return -1;
+}
+
+
 static int cbq_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
 	struct cbq_sched_data *q = (struct cbq_sched_data*)sch->data;
@@ -1569,8 +1580,13 @@
 	if (cbq_dump_attr(skb, &q->link) < 0)
 		goto rtattr_failure;
 	rta->rta_len = skb->tail - b;
+	spin_lock_bh(&sch->dev->queue_lock);
 	q->link.xstats.avgidle = q->link.avgidle;
-	RTA_PUT(skb, TCA_XSTATS, sizeof(q->link.xstats), &q->link.xstats);
+	if (cbq_copy_xstats(skb, &q->link.xstats)) {
+		spin_unlock_bh(&sch->dev->queue_lock);
+		goto rtattr_failure;
+	}
+	spin_unlock_bh(&sch->dev->queue_lock);
 	return skb->len;
 
 rtattr_failure:
@@ -1600,12 +1616,19 @@
 		goto rtattr_failure;
 	rta->rta_len = skb->tail - b;
 	cl->stats.qlen = cl->q->q.qlen;
-	RTA_PUT(skb, TCA_STATS, sizeof(cl->stats), &cl->stats);
+	if (qdisc_copy_stats(skb, &cl->stats))
+		goto rtattr_failure;
+	spin_lock_bh(&sch->dev->queue_lock);
 	cl->xstats.avgidle = cl->avgidle;
 	cl->xstats.undertime = 0;
 	if (!PSCHED_IS_PASTPERFECT(cl->undertime))
 		cl->xstats.undertime = PSCHED_TDIFF(cl->undertime, q->now);
-	RTA_PUT(skb, TCA_XSTATS, sizeof(cl->xstats), &cl->xstats);
+	q->link.xstats.avgidle = q->link.avgidle;
+	if (cbq_copy_xstats(skb, &cl->xstats)) {
+		spin_unlock_bh(&sch->dev->queue_lock);
+		goto rtattr_failure;
+	}
+	spin_unlock_bh(&sch->dev->queue_lock);
 
 	return skb->len;
 
@@ -1631,8 +1654,11 @@
 				new->reshape_fail = cbq_reshape_fail;
 #endif
 		}
-		if ((*old = xchg(&cl->q, new)) != NULL)
-			qdisc_reset(*old);
+		sch_tree_lock(sch);
+		*old = cl->q;
+		cl->q = new;
+		qdisc_reset(*old);
+		sch_tree_unlock(sch);
 
 		return 0;
 	}
@@ -1710,16 +1736,16 @@
 	struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data;
 	struct cbq_class *cl = (struct cbq_class*)arg;
 
-	start_bh_atomic();
 	if (--cl->refcnt == 0) {
 #ifdef CONFIG_NET_CLS_POLICE
+		spin_lock_bh(&sch->dev->queue_lock);
 		if (q->rx_class == cl)
 			q->rx_class = NULL;
+		spin_unlock_bh(&sch->dev->queue_lock);
 #endif
+
 		cbq_destroy_class(cl);
 	}
-	end_bh_atomic();
-	return;
 }
 
 static int
@@ -1780,7 +1806,7 @@
 		}
 
 		/* Change class parameters */
-		start_bh_atomic();
+		sch_tree_lock(sch);
 
 		if (cl->next_alive != NULL)
 			cbq_deactivate_class(cl);
@@ -1812,7 +1838,7 @@
 		if (cl->q->q.qlen)
 			cbq_activate_class(cl);
 
-		end_bh_atomic();
+		sch_tree_lock(sch);
 
 #ifdef CONFIG_NET_ESTIMATOR
 		if (tca[TCA_RATE-1]) {
@@ -1878,8 +1904,9 @@
 	cl->allot = parent->allot;
 	cl->quantum = cl->allot;
 	cl->weight = cl->R_tab->rate.rate;
+	cl->stats.lock = &sch->dev->queue_lock;
 
-	start_bh_atomic();
+	sch_tree_lock(sch);
 	cbq_link_class(cl);
 	cl->borrow = cl->tparent;
 	if (cl->tparent != &q->link)
@@ -1903,7 +1930,7 @@
 #endif
 	if (tb[TCA_CBQ_FOPT-1])
 		cbq_set_fopt(cl, RTA_DATA(tb[TCA_CBQ_FOPT-1]));
-	end_bh_atomic();
+	sch_tree_unlock(sch);
 
 #ifdef CONFIG_NET_ESTIMATOR
 	if (tca[TCA_RATE-1])
@@ -1926,7 +1953,7 @@
 	if (cl->filters || cl->children || cl == &q->link)
 		return -EBUSY;
 
-	start_bh_atomic();
+	sch_tree_lock(sch);
 
 	if (cl->next_alive)
 		cbq_deactivate_class(cl);
@@ -1948,11 +1975,10 @@
 	cbq_sync_defmap(cl);
 
 	cbq_rmprio(q, cl);
+	sch_tree_unlock(sch);
 
 	if (--cl->refcnt == 0)
 		cbq_destroy_class(cl);
-
-	end_bh_atomic();
 
 	return 0;
 }
diff -u --recursive --new-file v2.3.5/linux/net/sched/sch_csz.c linux/net/sched/sch_csz.c
--- v2.3.5/linux/net/sched/sch_csz.c	Sun Mar 21 07:22:00 1999
+++ linux/net/sched/sch_csz.c	Wed Jun  9 14:45:37 1999
@@ -885,7 +885,7 @@
 
 		a = &q->flow[cl];
 
-		start_bh_atomic();	
+		spin_lock_bh(&sch->dev->queue_lock);
 #if 0
 		a->rate_log = copt->rate_log;
 #endif
@@ -899,7 +899,7 @@
 		if (tb[TCA_CSZ_RTAB-1])
 			memcpy(a->L_tab, RTA_DATA(tb[TCA_CSZ_RTAB-1]), 1024);
 
-		end_bh_atomic();
+		spin_unlock_bh(&sch->dev->queue_lock);
 		return 0;
 	}
 	/* NI */
@@ -920,14 +920,14 @@
 
 	a = &q->flow[cl];
 
-	start_bh_atomic();
+	spin_lock_bh(&sch->dev->queue_lock);
 	a->fprev->fnext = a->fnext;
 	a->fnext->fprev = a->fprev;
 	a->sprev->snext = a->snext;
 	a->snext->sprev = a->sprev;
 	a->start = a->finish = 0;
 	kfree(xchg(&q->flow[cl].L_tab, NULL));
-	end_bh_atomic();
+	spin_unlock_bh(&sch->dev->queue_lock);
 
 	return 0;
 }
diff -u --recursive --new-file v2.3.5/linux/net/sched/sch_generic.c linux/net/sched/sch_generic.c
--- v2.3.5/linux/net/sched/sch_generic.c	Mon May 31 22:28:07 1999
+++ linux/net/sched/sch_generic.c	Wed Jun  9 14:45:37 1999
@@ -34,7 +34,45 @@
 
 /* Main transmission queue. */
 
-struct Qdisc_head qdisc_head = { &qdisc_head };
+struct Qdisc_head qdisc_head = { &qdisc_head, &qdisc_head };
+spinlock_t qdisc_runqueue_lock = SPIN_LOCK_UNLOCKED;
+
+/* Main qdisc structure lock. 
+
+   However, modifications
+   to data, participating in scheduling must be additionally
+   protected with dev->queue_lock spinlock.
+
+   The idea is the following:
+   - enqueue, dequeue are serialized via top level device
+     spinlock dev->queue_lock.
+   - tree walking is protected by read_lock(qdisc_tree_lock)
+     and this lock is used only in process context.
+   - updates to tree are made only under rtnl semaphore,
+     hence this lock may be made without local bh disabling.
+
+   qdisc_tree_lock must be grabbed BEFORE dev->queue_lock!
+ */
+rwlock_t qdisc_tree_lock = RW_LOCK_UNLOCKED;
+
+/* Anti deadlock rules:
+
+   qdisc_runqueue_lock protects main transmission list qdisc_head.
+   Run list is accessed only under this spinlock.
+
+   dev->queue_lock serializes queue accesses for this device
+   AND dev->qdisc pointer itself.
+
+   dev->xmit_lock serializes accesses to device driver.
+
+   dev->queue_lock and dev->xmit_lock are mutually exclusive,
+   if one is grabbed, another must be free.
+
+   qdisc_runqueue_lock may be requested under dev->queue_lock,
+   but neither dev->queue_lock nor dev->xmit_lock may be requested
+   under qdisc_runqueue_lock.
+ */
+
 
 /* Kick device.
    Note, that this procedure can be called by a watchdog timer, so that
@@ -44,7 +82,7 @@
             >0  - queue is not empty, but throttled.
 	    <0  - queue is not empty. Device is throttled, if dev->tbusy != 0.
 
-   NOTE: Called only from NET BH
+   NOTE: Called under dev->queue_lock with locally disabled BH.
 */
 
 int qdisc_restart(struct device *dev)
@@ -53,27 +91,97 @@
 	struct sk_buff *skb;
 
 	if ((skb = q->dequeue(q)) != NULL) {
+		/* Dequeue packet and release queue */
+		spin_unlock(&dev->queue_lock);
+
 		if (netdev_nit)
 			dev_queue_xmit_nit(skb, dev);
 
-		if (dev->hard_start_xmit(skb, dev) == 0) {
-			q->tx_last = jiffies;
-			return -1;
+		if (spin_trylock(&dev->xmit_lock)) {
+			/* Remember that the driver is grabbed by us. */
+			dev->xmit_lock_owner = smp_processor_id();
+			if (dev->hard_start_xmit(skb, dev) == 0) {
+				dev->xmit_lock_owner = -1;
+				spin_unlock(&dev->xmit_lock);
+
+				spin_lock(&dev->queue_lock);
+				dev->qdisc->tx_last = jiffies;
+				return -1;
+			}
+			/* Release the driver */
+			dev->xmit_lock_owner = -1;
+			spin_unlock(&dev->xmit_lock);
+		} else {
+			/* So, someone grabbed the driver. */
+
+			/* It may be transient configuration error,
+			   when hard_start_xmit() recurses. We detect
+			   it by checking xmit owner and drop the
+			   packet when deadloop is detected.
+			 */
+			if (dev->xmit_lock_owner == smp_processor_id()) {
+				kfree_skb(skb);
+				if (net_ratelimit())
+					printk(KERN_DEBUG "Dead loop on virtual %s, fix it urgently!\n", dev->name);
+				spin_lock(&dev->queue_lock);
+				return -1;
+			}
+
+			/* Otherwise, packet is requeued
+			   and will be sent by the next net_bh run.
+			 */
+			mark_bh(NET_BH);
 		}
 
 		/* Device kicked us out :(
 		   This is possible in three cases:
 
+		   0. driver is locked
 		   1. fastroute is enabled
 		   2. device cannot determine busy state
 		      before start of transmission (f.e. dialout)
 		   3. device is buggy (ppp)
 		 */
 
+		spin_lock(&dev->queue_lock);
+		q = dev->qdisc;
 		q->ops->requeue(skb, q);
 		return -1;
 	}
-	return q->q.qlen;
+	return dev->qdisc->q.qlen;
+}
+
+static __inline__ void
+qdisc_stop_run(struct Qdisc *q)
+{
+	q->h.forw->back = q->h.back;
+	q->h.back->forw = q->h.forw;
+	q->h.forw = NULL;
+}
+
+extern __inline__ void
+qdisc_continue_run(struct Qdisc *q)
+{
+	if (!qdisc_on_runqueue(q) && q->dev) {
+		q->h.forw = &qdisc_head;
+		q->h.back = qdisc_head.back;
+		qdisc_head.back->forw = &q->h;
+		qdisc_head.back = &q->h;
+	}
+}
+
+static __inline__ int
+qdisc_init_run(struct Qdisc_head *lh)
+{
+	if (qdisc_head.forw != &qdisc_head) {
+		*lh = qdisc_head;
+		lh->forw->back = lh;
+		lh->back->forw = lh;
+		qdisc_head.forw = &qdisc_head;
+		qdisc_head.back = &qdisc_head;
+		return 1;
+	}
+	return 0;
 }
 
 /* Scan transmission queue and kick devices.
@@ -84,63 +192,90 @@
    I have no idea how to solve it using only "anonymous" Linux mark_bh().
 
    To change queue from device interrupt? Ough... only not this...
+
+   This function is called only from net_bh.
  */
 
 void qdisc_run_queues(void)
 {
-	struct Qdisc_head **hp, *h;
+	struct Qdisc_head lh, *h;
 
-	hp = &qdisc_head.forw;
-	while ((h = *hp) != &qdisc_head) {
-		int res = -1;
+	spin_lock(&qdisc_runqueue_lock);
+	if (!qdisc_init_run(&lh))
+		goto out;
+
+	while ((h = lh.forw) != &lh) {
+		int res;
+		struct device *dev;
 		struct Qdisc *q = (struct Qdisc*)h;
-		struct device *dev = q->dev;
 
-		spin_lock_bh(&dev->xmit_lock);
-		while (!dev->tbusy && (res = qdisc_restart(dev)) < 0)
-			/* NOTHING */;
-		spin_unlock_bh(&dev->xmit_lock);
-
-		/* An explanation is necessary here.
-		   qdisc_restart called dev->hard_start_xmit,
-		   if device is virtual, it could trigger one more
-		   dev_queue_xmit and a new device could appear
-		   in the active chain. In this case we cannot unlink
-		   the empty queue, because we lost the back pointer.
-		   No problem, we will unlink it during the next round.
-		 */
+		qdisc_stop_run(q);
+
+		dev = q->dev;
+		spin_unlock(&qdisc_runqueue_lock);
 
-		if (res == 0 && *hp == h) {
-			*hp = h->forw;
-			h->forw = NULL;
-			continue;
+		res = -1;
+		if (spin_trylock(&dev->queue_lock)) {
+			while (!dev->tbusy && (res = qdisc_restart(dev)) < 0)
+				/* NOTHING */;
+			spin_unlock(&dev->queue_lock);
 		}
-		hp = &h->forw;
+
+		spin_lock(&qdisc_runqueue_lock);
+		/* If qdisc is not empty add it to the tail of list */
+		if (res)
+			qdisc_continue_run(q);
 	}
+out:
+	spin_unlock(&qdisc_runqueue_lock);
 }
 
-/* Periodic watchdoc timer to recover from hard/soft device bugs. */
+/* Periodic watchdog timer to recover from hard/soft device bugs. */
 
 static void dev_do_watchdog(unsigned long dummy);
 
 static struct timer_list dev_watchdog =
 	{ NULL, NULL, 0L, 0L, &dev_do_watchdog };
 
+/*   This function is called only from timer */
+
 static void dev_do_watchdog(unsigned long dummy)
 {
-	struct Qdisc_head *h;
+	struct Qdisc_head lh, *h;
+
+	if (!spin_trylock(&qdisc_runqueue_lock)) {
+		/* No hurry with watchdog. */
+		mod_timer(&dev_watchdog, jiffies + HZ/10);
+		return;
+	}
+
+	if (!qdisc_init_run(&lh))
+		goto out;
 
-	for (h = qdisc_head.forw; h != &qdisc_head; h = h->forw) {
+	while ((h = lh.forw) != &lh) {
+		struct device *dev;
 		struct Qdisc *q = (struct Qdisc*)h;
-		struct device *dev = q->dev;
 
-		spin_lock_bh(&dev->xmit_lock);
-		if (dev->tbusy && jiffies - q->tx_last > q->tx_timeo)
-			qdisc_restart(dev);
-		spin_unlock_bh(&dev->xmit_lock);
+		qdisc_stop_run(q);
+
+		dev = q->dev;
+		spin_unlock(&qdisc_runqueue_lock);
+
+		if (spin_trylock(&dev->queue_lock)) {
+			q = dev->qdisc;
+			if (dev->tbusy && jiffies - q->tx_last > q->tx_timeo)
+				qdisc_restart(dev);
+			spin_unlock(&dev->queue_lock);
+		}
+
+		spin_lock(&qdisc_runqueue_lock);
+
+		qdisc_continue_run(dev->qdisc);
 	}
-	dev_watchdog.expires = jiffies + 5*HZ;
-	add_timer(&dev_watchdog);
+
+out:
+	mod_timer(&dev_watchdog, jiffies + 5*HZ);
+	spin_unlock(&qdisc_runqueue_lock);
 }
 
 
@@ -211,7 +346,7 @@
 {
         { NULL }, 
 	NULL,
-	NULL,
+	noop_dequeue,
 	TCQ_F_BUILTIN,
 	&noqueue_qdisc_ops,
 };
@@ -327,6 +462,7 @@
 	sch->enqueue = ops->enqueue;
 	sch->dequeue = ops->dequeue;
 	sch->dev = dev;
+	sch->stats.lock = &dev->queue_lock;
 	atomic_set(&sch->refcnt, 1);
 	if (!ops->init || ops->init(sch, NULL) == 0)
 		return sch;
@@ -335,42 +471,45 @@
 	return NULL;
 }
 
+/* Under dev->queue_lock and BH! */
+
 void qdisc_reset(struct Qdisc *qdisc)
 {
 	struct Qdisc_ops *ops = qdisc->ops;
-	start_bh_atomic();
 	if (ops->reset)
 		ops->reset(qdisc);
-	end_bh_atomic();
 }
 
+/* Under dev->queue_lock and BH! */
+
 void qdisc_destroy(struct Qdisc *qdisc)
 {
 	struct Qdisc_ops *ops = qdisc->ops;
+	struct device *dev;
 
 	if (!atomic_dec_and_test(&qdisc->refcnt))
 		return;
 
+	dev = qdisc->dev;
+
 #ifdef CONFIG_NET_SCHED
-	if (qdisc->dev) {
+	if (dev) {
 		struct Qdisc *q, **qp;
-		for (qp = &qdisc->dev->qdisc_list; (q=*qp) != NULL; qp = &q->next)
+		for (qp = &qdisc->dev->qdisc_list; (q=*qp) != NULL; qp = &q->next) {
 			if (q == qdisc) {
 				*qp = q->next;
-				q->next = NULL;
 				break;
 			}
+		}
 	}
 #ifdef CONFIG_NET_ESTIMATOR
 	qdisc_kill_estimator(&qdisc->stats);
 #endif
 #endif
-	start_bh_atomic();
 	if (ops->reset)
 		ops->reset(qdisc);
 	if (ops->destroy)
 		ops->destroy(qdisc);
-	end_bh_atomic();
 	if (!(qdisc->flags&TCQ_F_BUILTIN))
 		kfree(qdisc);
 }
@@ -385,19 +524,23 @@
 	 */
 
 	if (dev->qdisc_sleeping == &noop_qdisc) {
+		struct Qdisc *qdisc;
 		if (dev->tx_queue_len) {
-			struct Qdisc *qdisc;
 			qdisc = qdisc_create_dflt(dev, &pfifo_fast_ops);
 			if (qdisc == NULL) {
 				printk(KERN_INFO "%s: activation failed\n", dev->name);
 				return;
 			}
-			dev->qdisc_sleeping = qdisc;
-		} else
-			dev->qdisc_sleeping = &noqueue_qdisc;
+		} else {
+			qdisc =  &noqueue_qdisc;
+		}
+		write_lock(&qdisc_tree_lock);
+		dev->qdisc_sleeping = qdisc;
+		write_unlock(&qdisc_tree_lock);
 	}
 
-	start_bh_atomic();
+	spin_lock_bh(&dev->queue_lock);
+	spin_lock(&qdisc_runqueue_lock);
 	if ((dev->qdisc = dev->qdisc_sleeping) != &noqueue_qdisc) {
 		dev->qdisc->tx_timeo = 5*HZ;
 		dev->qdisc->tx_last = jiffies - dev->qdisc->tx_timeo;
@@ -405,51 +548,50 @@
 			dev_watchdog.expires = jiffies + 5*HZ;
 		add_timer(&dev_watchdog);
 	}
-	end_bh_atomic();
+	spin_unlock(&qdisc_runqueue_lock);
+	spin_unlock_bh(&dev->queue_lock);
 }
 
 void dev_deactivate(struct device *dev)
 {
 	struct Qdisc *qdisc;
 
-	start_bh_atomic();
-
-	qdisc = xchg(&dev->qdisc, &noop_qdisc);
+	spin_lock_bh(&dev->queue_lock);
+	qdisc = dev->qdisc;
+	dev->qdisc = &noop_qdisc;
 
 	qdisc_reset(qdisc);
 
-	if (qdisc->h.forw) {
-		struct Qdisc_head **hp, *h;
-
-		for (hp = &qdisc_head.forw; (h = *hp) != &qdisc_head; hp = &h->forw) {
-			if (h == &qdisc->h) {
-				*hp = h->forw;
-				break;
-			}
-		}
-	}
-
-	end_bh_atomic();
+	spin_lock(&qdisc_runqueue_lock);
+	if (qdisc_on_runqueue(qdisc))
+		qdisc_stop_run(qdisc);
+	spin_unlock(&qdisc_runqueue_lock);
+	spin_unlock_bh(&dev->queue_lock);
 }
 
 void dev_init_scheduler(struct device *dev)
 {
+	write_lock(&qdisc_tree_lock);
+	spin_lock_bh(&dev->queue_lock);
 	dev->qdisc = &noop_qdisc;
+	spin_unlock_bh(&dev->queue_lock);
 	dev->qdisc_sleeping = &noop_qdisc;
 	dev->qdisc_list = NULL;
+	write_unlock(&qdisc_tree_lock);
 }
 
 void dev_shutdown(struct device *dev)
 {
 	struct Qdisc *qdisc;
 
-	start_bh_atomic();
+	write_lock(&qdisc_tree_lock);
+	spin_lock_bh(&dev->queue_lock);
 	qdisc = dev->qdisc_sleeping;
 	dev->qdisc = &noop_qdisc;
 	dev->qdisc_sleeping = &noop_qdisc;
 	qdisc_destroy(qdisc);
 	BUG_TRAP(dev->qdisc_list == NULL);
 	dev->qdisc_list = NULL;
-	end_bh_atomic();
+	spin_unlock_bh(&dev->queue_lock);
+	write_unlock(&qdisc_tree_lock);
 }
-
diff -u --recursive --new-file v2.3.5/linux/net/sched/sch_prio.c linux/net/sched/sch_prio.c
--- v2.3.5/linux/net/sched/sch_prio.c	Thu Mar 25 09:23:34 1999
+++ linux/net/sched/sch_prio.c	Wed Jun  9 14:45:37 1999
@@ -178,7 +178,7 @@
 			return -EINVAL;
 	}
 
-	start_bh_atomic();
+	sch_tree_lock(sch);
 	q->bands = qopt->bands;
 	memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1);
 
@@ -187,7 +187,7 @@
 		if (child != &noop_qdisc)
 			qdisc_destroy(child);
 	}
-	end_bh_atomic();
+	sch_tree_unlock(sch);
 
 	for (i=0; i<=TC_PRIO_MAX; i++) {
 		int band = q->prio2band[i];
@@ -195,11 +195,12 @@
 			struct Qdisc *child;
 			child = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
 			if (child) {
+				sch_tree_lock(sch);
 				child = xchg(&q->queues[band], child);
-				synchronize_bh();
 
 				if (child != &noop_qdisc)
 					qdisc_destroy(child);
+				sch_tree_unlock(sch);
 			}
 		}
 	}
@@ -265,7 +266,11 @@
 	if (new == NULL)
 		new = &noop_qdisc;
 
-	*old = xchg(&q->queues[band], new);
+	sch_tree_lock(sch);
+	*old = q->queues[band];
+	q->queues[band] = new;
+	qdisc_reset(*old);
+	sch_tree_unlock(sch);
 
 	return 0;
 }
diff -u --recursive --new-file v2.3.5/linux/net/sched/sch_sfq.c linux/net/sched/sch_sfq.c
--- v2.3.5/linux/net/sched/sch_sfq.c	Sat Apr 24 17:51:48 1999
+++ linux/net/sched/sch_sfq.c	Wed Jun  9 14:45:37 1999
@@ -387,7 +387,7 @@
 	if (opt->rta_len < RTA_LENGTH(sizeof(*ctl)))
 		return -EINVAL;
 
-	start_bh_atomic();
+	sch_tree_lock(sch);
 	q->quantum = ctl->quantum ? : psched_mtu(sch->dev);
 	q->perturb_period = ctl->perturb_period*HZ;
 
@@ -396,7 +396,7 @@
 		q->perturb_timer.expires = jiffies + q->perturb_period;
 		add_timer(&q->perturb_timer);
 	}
-	end_bh_atomic();
+	sch_tree_unlock(sch);
 	return 0;
 }
 
diff -u --recursive --new-file v2.3.5/linux/net/sched/sch_tbf.c linux/net/sched/sch_tbf.c
--- v2.3.5/linux/net/sched/sch_tbf.c	Sun Mar 21 07:22:00 1999
+++ linux/net/sched/sch_tbf.c	Wed Jun  9 14:45:37 1999
@@ -308,7 +308,7 @@
 	if (rtab->data[max_size>>qopt->rate.cell_log] > qopt->buffer)
 		goto done;
 
-	start_bh_atomic();
+	sch_tree_lock(sch);
 	q->limit = qopt->limit;
 	q->mtu = qopt->mtu;
 	q->max_size = max_size;
@@ -317,7 +317,7 @@
 	q->ptokens = q->mtu;
 	rtab = xchg(&q->R_tab, rtab);
 	ptab = xchg(&q->P_tab, ptab);
-	end_bh_atomic();
+	sch_tree_unlock(sch);
 	err = 0;
 done:
 	if (rtab)
diff -u --recursive --new-file v2.3.5/linux/net/sched/sch_teql.c linux/net/sched/sch_teql.c
--- v2.3.5/linux/net/sched/sch_teql.c	Sun Mar 21 07:22:00 1999
+++ linux/net/sched/sch_teql.c	Wed Jun  9 14:45:37 1999
@@ -125,9 +125,11 @@
 	if (skb == NULL) {
 		struct device *m = dat->m->dev.qdisc->dev;
 		if (m) {
-			m->tbusy = 0;
 			dat->m->slaves = sch;
+			spin_lock(&m->queue_lock);
+			m->tbusy = 0;
 			qdisc_restart(m);
+			spin_unlock(&m->queue_lock);
 		}
 	}
 	sch->q.qlen = dat->q.qlen + dat->m->dev.qdisc->q.qlen;
@@ -167,7 +169,9 @@
 					master->slaves = NEXT_SLAVE(q);
 					if (q == master->slaves) {
 						master->slaves = NULL;
+						spin_lock_bh(&master->dev.queue_lock);
 						qdisc_reset(master->dev.qdisc);
+						spin_unlock_bh(&master->dev.queue_lock);
 					}
 				}
 				skb_queue_purge(&dat->q);
@@ -190,6 +194,9 @@
 	if (dev->hard_header_len > m->dev.hard_header_len)
 		return -EINVAL;
 
+	if (&m->dev == dev)
+		return -ELOOP;
+
 	q->m = m;
 
 	skb_queue_head_init(&q->q);
@@ -244,7 +251,11 @@
 			return -ENOBUFS;
 	}
 	if (neigh_event_send(n, skb_res) == 0) {
-		if (dev->hard_header(skb, dev, ntohs(skb->protocol), n->ha, NULL, skb->len) < 0) {
+		int err;
+		read_lock(&n->lock);
+		err = dev->hard_header(skb, dev, ntohs(skb->protocol), n->ha, NULL, skb->len);
+		read_unlock(&n->lock);
+		if (err < 0) {
 			neigh_release(n);
 			return -EINVAL;
 		}
@@ -295,19 +306,24 @@
 			continue;
 		}
 
-		if (q->h.forw == NULL) {
-			q->h.forw = qdisc_head.forw;
-			qdisc_head.forw = &q->h;
-		}
+		if (!qdisc_on_runqueue(q))
+			qdisc_run(q);
 
 		switch (teql_resolve(skb, skb_res, slave)) {
 		case 0:
-			if (slave->hard_start_xmit(skb, slave) == 0) {
-				master->slaves = NEXT_SLAVE(q);
-				dev->tbusy = 0;
-				master->stats.tx_packets++;
-				master->stats.tx_bytes += len;
+			if (spin_trylock(&slave->xmit_lock)) {
+				slave->xmit_lock_owner = smp_processor_id();
+				if (slave->hard_start_xmit(skb, slave) == 0) {
+					slave->xmit_lock_owner = -1;
+					spin_unlock(&slave->xmit_lock);
+					master->slaves = NEXT_SLAVE(q);
+					dev->tbusy = 0;
+					master->stats.tx_packets++;
+					master->stats.tx_bytes += len;
 					return 0;
+				}
+				slave->xmit_lock_owner = -1;
+				spin_unlock(&slave->xmit_lock);
 			}
 			if (dev->tbusy)
 				busy = 1;
diff -u --recursive --new-file v2.3.5/linux/net/socket.c linux/net/socket.c
--- v2.3.5/linux/net/socket.c	Mon May 31 22:28:07 1999
+++ linux/net/socket.c	Tue Jun  8 10:47:58 1999
@@ -204,7 +204,7 @@
 			return -ENFILE;
 		}
 
-		file->f_dentry = d_alloc_root(inode, NULL);
+		file->f_dentry = d_alloc_root(inode);
 		if (!file->f_dentry) {
 			put_filp(file);
 			put_unused_fd(fd);
diff -u --recursive --new-file v2.3.5/linux/net/sunrpc/xprt.c linux/net/sunrpc/xprt.c
--- v2.3.5/linux/net/sunrpc/xprt.c	Tue May  4 10:29:07 1999
+++ linux/net/sunrpc/xprt.c	Tue Jun  8 17:58:03 1999
@@ -42,6 +42,7 @@
 #define __KERNEL_SYSCALLS__
 
 #include <linux/version.h>
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/malloc.h>
 #include <linux/sched.h>
@@ -56,6 +57,8 @@
 #include <linux/file.h>
 
 #include <net/sock.h>
+#include <net/checksum.h>
+#include <net/udp.h>
 
 #include <asm/uaccess.h>
 
@@ -356,6 +359,7 @@
 	sk->user_data    = NULL;
 #endif
 	sk->data_ready   = xprt->old_data_ready;
+	sk->no_check 	 = 0;
 	sk->state_change = xprt->old_state_change;
 	sk->write_space  = xprt->old_write_space;
 
@@ -563,18 +567,61 @@
 	return;
 }
 
-/*
- * Input handler for RPC replies. Called from a bottom half and hence
+/* We have set things up such that we perform the checksum of the UDP
+ * packet in parallel with the copies into the RPC client iovec.  -DaveM
+ */
+static int csum_partial_copy_to_page_cache(struct iovec *iov,
+					   struct sk_buff *skb,
+					   int copied)
+{
+	__u8 *pkt_data = skb->data + sizeof(struct udphdr);
+	__u8 *cur_ptr = iov->iov_base;
+	__kernel_size_t cur_len = iov->iov_len;
+	unsigned int csum = skb->csum;
+	int need_csum = (skb->ip_summed != CHECKSUM_UNNECESSARY);
+	int slack = skb->len - copied - sizeof(struct udphdr);
+
+	if (need_csum)
+		csum = csum_partial(skb->h.raw, sizeof(struct udphdr), csum);
+	while (copied > 0) {
+		if (cur_len) {
+			int to_move = cur_len;
+			if (to_move > copied)
+				to_move = copied;
+			if (need_csum)
+				csum = csum_partial_copy_nocheck(pkt_data, cur_ptr,
+								 to_move, csum);
+			else
+				memcpy(cur_ptr, pkt_data, to_move);
+			pkt_data += to_move;
+			copied -= to_move;
+			cur_ptr += to_move;
+			cur_len -= to_move;
+		}
+		if (cur_len <= 0) {
+			iov++;
+			cur_len = iov->iov_len;
+			cur_ptr = iov->iov_base;
+		}
+	}
+	if (need_csum) {
+		if (slack > 0)
+			csum = csum_partial(pkt_data, slack, csum);
+		if ((unsigned short)csum_fold(csum))
+			return -1;
+	}
+	return 0;
+}
+
+/* Input handler for RPC replies. Called from a bottom half and hence
  * atomic.
  */
 static inline void
 udp_data_ready(struct sock *sk, int len)
 {
-	struct rpc_task	*task;
 	struct rpc_xprt	*xprt;
 	struct rpc_rqst *rovr;
 	struct sk_buff	*skb;
-	struct iovec	iov[MAX_IOVEC];
 	int		err, repsize, copied;
 
 	dprintk("RPC:      udp_data_ready...\n");
@@ -584,28 +631,31 @@
 
 	if ((skb = skb_recv_datagram(sk, 0, 1, &err)) == NULL)
 		return;
-	repsize = skb->len - 8;	/* don't account for UDP header */
 
+	repsize = skb->len - sizeof(struct udphdr);
 	if (repsize < 4) {
 		printk("RPC: impossible RPC reply size %d!\n", repsize);
 		goto dropit;
 	}
 
 	/* Look up the request corresponding to the given XID */
-	if (!(rovr = xprt_lookup_rqst(xprt, *(u32 *) (skb->h.raw + 8))))
+	if (!(rovr = xprt_lookup_rqst(xprt,
+				      *(u32 *) (skb->h.raw + sizeof(struct udphdr)))))
 		goto dropit;
-	task = rovr->rq_task;
 
-	dprintk("RPC: %4d received reply\n", task->tk_pid);
-	xprt_pktdump("packet data:", (u32 *) (skb->h.raw+8), repsize);
+	dprintk("RPC: %4d received reply\n", rovr->rq_task->tk_pid);
+	xprt_pktdump("packet data:",
+		     (u32 *) (skb->h.raw + sizeof(struct udphdr)), repsize);
 
 	if ((copied = rovr->rq_rlen) > repsize)
 		copied = repsize;
 
-	/* Okay, we have it. Copy datagram... */
-	memcpy(iov, rovr->rq_rvec, rovr->rq_rnr * sizeof(iov[0]));
-	/* This needs to stay tied with the usermode skb_copy_dagram... */
-	memcpy_tokerneliovec(iov, skb->data+8, copied);
+	/* Suck it into the iovec, verify checksum if not done by hw. */
+	if (csum_partial_copy_to_page_cache(rovr->rq_rvec, skb, copied))
+		goto dropit;
+
+	/* Something worked... */
+	dst_confirm(skb->dst);
 
 	xprt_complete_rqst(xprt, rovr, copied);
 
@@ -1341,6 +1391,7 @@
 	xprt->old_write_space = inet->write_space;
 	if (proto == IPPROTO_UDP) {
 		inet->data_ready = udp_data_ready;
+		inet->no_check = UDP_CSUM_NORCV;
 	} else {
 		inet->data_ready = tcp_data_ready;
 		inet->state_change = tcp_state_change;
diff -u --recursive --new-file v2.3.5/linux/net/wanrouter/wanmain.c linux/net/wanrouter/wanmain.c
--- v2.3.5/linux/net/wanrouter/wanmain.c	Wed Dec 30 15:07:00 1998
+++ linux/net/wanrouter/wanmain.c	Wed Jun  2 14:40:22 1999
@@ -30,6 +30,7 @@
 * Dec 22, 1998  Arnaldo Melo    vmalloc/vfree used in device_setup to allocate
 *                               kernel memory and copy configuration data to
 *                               kernel space (for big firmwares)
+* May 19, 1999  Arnaldo Melo    __initfunc in wanrouter_init
 *****************************************************************************/
 
 #include <linux/config.h>
@@ -104,17 +105,18 @@
 #endif
 
 #ifndef MODULE
-
-int wanrouter_init(void)
+__initfunc(int wanrouter_init(void))
 {
 	int err;
-	extern void wanpipe_init(void);
+	extern int wanpipe_init(void),
+		   cyclomx_init(void);
 
 	printk(KERN_INFO "%s v%u.%u %s\n",
 		fullname, ROUTER_VERSION, ROUTER_RELEASE, copyright);
 	err = wanrouter_proc_init();
 	if (err)
-		printk(KERN_ERR "%s: can't create entry in proc filesystem!\n", modname);		
+		printk(KERN_ERR "%s: can't create entry in proc filesystem!\n",
+				modname);		
 
 	/*
 	 *	Initialise compiled in boards
@@ -123,6 +125,9 @@
 #ifdef CONFIG_VENDOR_SANGOMA
 	wanpipe_init();
 #endif	
+#ifdef CONFIG_CYCLADES_SYNC
+	cyclomx_init();
+#endif
 	return err;
 }
 
@@ -187,7 +192,6 @@
  *	Context:	process
  */
 
-
 int register_wan_device(wan_device_t* wandev)
 {
 	int err, namelen;
@@ -207,7 +211,6 @@
 	printk(KERN_INFO "%s: registering WAN device %s\n",
 		modname, wandev->name);
 #endif
-
 	/*
 	 *	Register /proc directory entry 
 	 */
diff -u --recursive --new-file v2.3.5/linux/net/x25/af_x25.c linux/net/x25/af_x25.c
--- v2.3.5/linux/net/x25/af_x25.c	Wed Jun  2 14:44:39 1999
+++ linux/net/x25/af_x25.c	Wed Jun  9 14:45:37 1999
@@ -1336,7 +1336,7 @@
 	/*
 	 *	Register any pre existing devices.
 	 */
-	read_lock_bh(&dev_base_lock);
+	read_lock(&dev_base_lock);
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25
 #if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
@@ -1345,7 +1345,7 @@
 			))
 			x25_link_device_up(dev);
 	}
-	read_unlock_bh(&dev_base_lock);
+	read_unlock(&dev_base_lock);
 
 	return 0;
 }
