diff -u --recursive --new-file v2.1.119/linux/CREDITS linux/CREDITS
--- v2.1.119/linux/CREDITS	Thu Aug 27 19:56:28 1998
+++ linux/CREDITS	Tue Sep  1 10:50:11 1998
@@ -212,12 +212,14 @@
 S: 10 St Barnabas Road, Cambridge CB1 2BY
 S: United Kingdom
 
-N: Thomas Bogendoerfer
+N: Thomas Bogendörfer
 E: tsbogend@alpha.franken.de
-D: PCnet32 driver
+D: PCnet32 driver, SONIC driver, JAZZ_ESP driver
+D: newport abscon driver, g364 framebuffer driver
 D: strace for Linux/Alpha
-S: Baumgartenweg 5
-S: 91452 Wilhermsdorf
+D: Linux/MIPS hacker
+S: Schafhofstr. 40
+S: 90556 Cadolzburg
 S: Germany
 
 N: Bill Bogstad
@@ -1233,12 +1235,12 @@
 S: Netherlands
 
 N: David S. Miller
-E: davem@caip.rutgers.edu
-D: Sparc hacker
-D: New Linux-Activists maintainer
+E: davem@dm.cobaltmicro.com
+D: Sparc and blue box hacker
+D: Vger Linux mailing list co-maintainer
 D: Linux Emacs elf/qmagic support + other libc/gcc things
 D: Yee bore de yee bore! ;-)
-S: 111 Alta Tierra Court
+S: 331 Santa Rosa Drive
 S: Los Gatos, California 95032
 S: USA
 
@@ -1445,14 +1447,11 @@
 
 N: Eric S. Raymond
 E: esr@thyrsus.com
-W: http://www.ccil.org/~esr/home.html
-D: ncurses library co-maintainer
+W: http://www.tuxedo.org/~esr/
 D: terminfo master file maintainer
-D: Distributions HOWTO editor
-D: Instigator, FHS standard
-D: Keeper of the Jargon File and curator of the Retrocomputing Museum
-D: Author, Emacs VC and GUD modes
-S: 22 South Warren Avenue
+D: Editor: Installation HOWTO, Distributions HOWTO, XFree86 HOWTO
+D: Author: fetchmail, Emacs VC mode, Emacs GUD mode
+S: 6 Karen Drive
 S: Malvern, Pennsylvania 19355
 S: USA
 
@@ -1473,8 +1472,8 @@
 D: Several AX.25 hacks
 
 N: Francois-Rene Rideau
-E: rideau@ens.fr
-W: http://www.eleves.ens.fr:8080/home/rideau/
+E: fare@tunes.org
+W: http://www.tunes.org/~fare
 D: petty kernel janitor (byteorder, ufs)
 S: 6, rue Augustin Thierry
 S: 75019 Paris
@@ -1512,10 +1511,12 @@
 
 N: Stephen Rothwell
 E: Stephen.Rothwell@canb.auug.org.au
+W: http://www.canb.auug.org.au/~sfr
+P: 1024/BD8C7805 CD A4 9D 01 10 6E 7E 3B  91 88 FA D9 C8 40 AA 02
 D: Boot/setup/build work for setup > 2K
 D: Author, APM driver
-S: 59 Bugden Avenue
-S: Gowrie ACT 2904
+S: 66 Maltby Circuit
+S: Wanniassa ACT 2903
 S: Australia
 
 N: Gerard Roudier
diff -u --recursive --new-file v2.1.119/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.119/linux/Documentation/Configure.help	Thu Aug 20 17:05:15 1998
+++ linux/Documentation/Configure.help	Fri Sep  4 15:32:26 1998
@@ -6478,6 +6478,18 @@
   If you don't know whether you need it, then you don't need it: say
   N.
 
+QNX filesystem support (EXPERIMENTAL)
+CONFIG_QNX4FS_FS  
+  This is the filesystem used by QNX 4. Say Y if you intend to mount
+  QNX hard disks and floppies.
+
+  This filesystem support is also available as a module ( = code which
+  can be inserted in and removed from the running kernel whenever you
+  want). The module is called qnx4.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt. 
+
+  If unsure, say N.
+
 Kernel automounter support
 CONFIG_AUTOFS_FS
   The automounter is a tool to automatically mount remote filesystems
@@ -8259,6 +8271,24 @@
   operation, and are not currently included.  These files can be
   obtained from Turtle Beach.  See Documentation/sound/MultiSound for
   information on how to obtain this.
+
+MSND Pinnacle Non-PnP Mode
+CONFIG_MSNDPIN_NONPNP
+  The Pinnacle and Fiji card resources can be configured either with
+  PnP, or through a configuration port.  For the Pinnacle,
+  configuration in non-PnP mode allows use of the IDE and joystick
+  peripherals on the card as well, since they do not show up when the
+  card is in PnP mode.  Specifying zero for any resource of a device
+  will disable the device.  If you are running the card in PnP mode,
+  you must say N here and use isapnptools to configure the card's
+  resources.
+
+MSND Pinnacle Config Port
+CONFIG_MSNDPIN_CFG
+  This is the port which the Pinnacle and Fiji uses to configure
+  the card's resources when not in PnP mode.  If your card is in
+  PnP mode, then be sure to say N to the previous option,
+  CONFIG_MSNDPIN_NONPNP.
 
 /dev/dsp and /dev/audio support
 CONFIG_SOUND_AUDIO
diff -u --recursive --new-file v2.1.119/linux/Documentation/filesystems/umsdos.txt linux/Documentation/filesystems/umsdos.txt
--- v2.1.119/linux/Documentation/filesystems/umsdos.txt	Sat May  2 14:19:51 1998
+++ linux/Documentation/filesystems/umsdos.txt	Wed Sep  2 16:12:37 1998
@@ -3,7 +3,7 @@
 fs/umsdos/README-WIP.txt for more information on current status. Thanks.
 
 ----------------------------------------------------------------------------
-Very short explanation for the impatient!!!
+Very short explanation for the impatient!
 
 Umsdos is a file system driver that run on top the MSDOS fs driver.
 It is written by Jacques Gelinas (jacques@solucorp.qc.ca)
@@ -24,9 +24,7 @@
 made from those comments is available from
 sunsite.unc.edu:/pub/Linux/system/Filesystems/umsdos.
 
-Mostly...
-
-You mount a DOS partition like this
+You mount a DOS partition like this:
 
 mount -t umsdos /dev/hda3 /mnt
          ^
@@ -41,7 +39,7 @@
 happens. This is why all mount options are passed to the msdos fs driver.
 
 Umsdos uses a special DOS file --linux-.--- to store the information
-which can't be handle by the normal MsDOS file system. This is the trick.
+which can't be handled by the normal MS-DOS filesystem. This is the trick.
 
 --linux-.--- is optional. There is one per directory.
 
@@ -65,12 +63,12 @@
 
 So in our example, after mounting mnt, we do
 
-umssync .
+	umssync .
 
 This will promote this directory (a recursive option is available) to full
-umsdos capabilities (long name ...). A ls -l before and after won't show
-much difference however. The files which were there are still there. But now
-you can do all this:
+umsdos capabilities (long name, etc.).  However, an "ls -l" before and after
+won't show much difference.  The files which were there are still there, but
+now you can do all this:
 
 	chmod 644 *
 	chown you.your_group *
@@ -94,7 +92,7 @@
 
 	(You put one for each umsdos mount point in the fstab)
 
-This will insure nice operation. A umsdos.fsck is in the making,
+This will ensure nice operation.  A umsdos.fsck is in the making,
 so you will be allowed to manage umsdos partitions in the same way
 other filesystems are, using the generic fsck front end.
 
diff -u --recursive --new-file v2.1.119/linux/Documentation/networking/wavelan.txt linux/Documentation/networking/wavelan.txt
--- v2.1.119/linux/Documentation/networking/wavelan.txt	Sat May  2 14:19:51 1998
+++ linux/Documentation/networking/wavelan.txt	Fri Aug 28 10:45:29 1998
@@ -1,5 +1,8 @@
 Sun Jul  2 01:38:33 EST 1995
 
+See also: http://www-uk.hpl.hp.com/people/jt/Linux/Wavelan.html
+
+
 1. At present the driver autoprobes for a WaveLAN card only at I/O address 
    0x390. The version of the card that I use (NCR) supports four I/O addresses
    (selectable via a pair of DIP switches).  If you want the driver to
diff -u --recursive --new-file v2.1.119/linux/Documentation/sound/AudioExcelDSP16 linux/Documentation/sound/AudioExcelDSP16
--- v2.1.119/linux/Documentation/sound/AudioExcelDSP16	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sound/AudioExcelDSP16	Fri Aug 28 10:45:45 1998
@@ -0,0 +1,101 @@
+Driver
+------
+
+Informations about Audio Excel DSP 16 driver can be found in the source
+file lowlevel/aedsp16.c
+Please, read the head of the source before using it. It contain useful
+informations.
+
+Configuration
+-------------
+
+The Audio Excel configuration, is now done with the standard Linux setup.
+You have to configure the sound card (Sound Blaster or Microsoft Sound System)
+and, if you want it, the Roland MPU-401 (do not use the Sound Blaster MPU-401,
+SB-MPU401) in the main driver menu. Activate the lowlevel drivers then select
+the Audio Excel hardware that you want to initialize. Check the IRQ/DMA/MIRQ
+of the Audio Excel initialization: it must be the same as the SBPRO (or MSS)
+setup. If the parameters are different, correct it.
+I you own a Gallant's audio card based on SC-6600, activate the SC-6600 support.
+If you want to change the configuration of the sound board, be sure to
+check off all the configuration items before re-configure it.
+
+Module parameters
+-----------------
+To use this driver as a module, you must configure some module parameters, to
+set up I/O addresses, IRQ lines and DMA channels. Some parameters are
+mandatory while some others are optional. Here a list of parameters you can
+use with this module:
+
+Name		Description
+====		===========
+MANDATORY
+io		I/O base address (0x220 or 0x240)
+irq		irq line (5, 7, 9, 10 or 11)
+dma		dma channel (0, 1 or 3)
+
+OPTIONAL
+mss_base	I/O base address for activate MSS mode (default SBPRO)
+		(0x530 or 0xE80)
+mpu_base	I/O base address for activate MPU-401 mode
+		(0x300, 0x310, 0x320 or 0x330)
+mpu_irq		MPU-401 irq line (5, 7, 9, 10 or 0)
+
+The /etc/conf.modules will have lines like this:
+
+options opl3 io=0x388
+options ad1848 io=0x530 irq=11 dma=3
+options aedsp16 io=0x220 irq=11 dma=3 mss_base=0x530
+
+Where the aedsp16 options are the options for this driver while opl3 and
+ad1848 are the corresponding options for the MSS and OPL3 modules.
+
+Loading MSS and OPL3 needs to pre load the aedsp16 module to set up correctly
+the sound card. Installation dependancies must be written in the conf.modules
+file:
+
+pre-install ad1848 modprobe aedsp16
+pre-install opl3 modprobe aedsp16
+
+Then you must load the sound modules stack in this order:
+sound -> aedsp16 -> [ ad1848, opl3 ]
+
+With the above configuration, loading ad1848 or opl3 modules, will
+automatically load all the sound stack.
+
+Sound cards supported
+---------------------
+This driver supports the SC-6000 and SC-6600 based Gallant's sound card.
+It don't support the Audio Excel DSP 16 III (try the SC-6600 code).
+I'm working on the III version of the card: if someone have useful
+informations about it, please let me know.
+For all the non-supported audio cards, you have to boot MS-DOS (or WIN95)
+activating the audio card with the MS-DOS device driver, then you have to
+<ctrl>-<alt>-<del> and boot Linux.
+Follow these steps:
+
+1) Compile Linux kernel with standard sound driver, using the emulation
+   you want, with the parameters of your audio card,
+   e.g. Microsoft Sound System irq10 dma3
+2) Install your new kernel as the default boot kernel.
+3) Boot MS-DOS and configure the audio card with the boot time device
+   driver, for MSS irq10 dma3 in our example.
+4) <ctrl>-<alt>-<del> and boot Linux. This will mantain the DOS configuration
+   and will boot the new kernel with sound driver. The sound driver will find
+   the audio card and will recognize and attach it.
+
+Reports on User successes
+-------------------------
+
+> Date: Mon, 29 Jul 1996 08:35:40 +0100
+> From: Mr S J Greenaway <sjg95@unixfe.rl.ac.uk>
+> To: riccardo@cdc8g5.cdc.polimi.it (Riccardo Facchetti)
+> Subject: Re: Audio Excel DSP 16 initialization code
+>
+> Just to let you know got my Audio Excel (emulating a MSS) working
+> with my original SB16, thanks for the driver!
+
+
+Last revised: 20 August 1998
+Riccardo Facchetti
+fizban@tin.it
diff -u --recursive --new-file v2.1.119/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.119/linux/MAINTAINERS	Wed Aug 26 11:37:33 1998
+++ linux/MAINTAINERS	Sat Aug 29 10:08:23 1998
@@ -504,7 +504,7 @@
 S:	Maintained
 
 PCNET32 NETWORK DRIVER
-P:	Thomas Bogendoerfer
+P:	Thomas Bogendörfer
 M:	tsbogend@alpha.franken.de
 L:	linux-net@vger.rutgers.edu
 S:	Maintained
@@ -561,6 +561,12 @@
 P:	Linus Torvalds
 M:	torvalds@transmeta.com
 L:	linux-smp@vger.rutgers.edu
+S:	Maintained
+
+SONIC NETWORK DRIVER
+P:	Thomas Bogendoerfer
+M:	tsbogend@alpha.franken.de
+L:	linux-net@vger.rutgers.edu
 S:	Maintained
 
 SOUND
diff -u --recursive --new-file v2.1.119/linux/Makefile linux/Makefile
--- v2.1.119/linux/Makefile	Thu Aug 27 19:56:28 1998
+++ linux/Makefile	Thu Aug 27 14:24:46 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 119
+SUBLEVEL = 120
 
 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.1.119/linux/arch/alpha/kernel/core_lca.c linux/arch/alpha/kernel/core_lca.c
--- v2.1.119/linux/arch/alpha/kernel/core_lca.c	Tue Aug 18 22:02:02 1998
+++ linux/arch/alpha/kernel/core_lca.c	Fri Sep  4 12:10:46 1998
@@ -470,7 +470,7 @@
 	case MCHK_K_OS_BUGCHECK: reason = "callsys in kernel mode"; break;
 	case MCHK_K_DCPERR:	reason = "d-cache parity error"; break;
 	case MCHK_K_ICPERR:	reason = "i-cache parity error"; break;
-	case MCHK_K_SIO_SERR:	reason = "SIO SERR occurred on on PCI bus"; break;
+	case MCHK_K_SIO_SERR:	reason = "SIO SERR occurred on PCI bus"; break;
 	case MCHK_K_SIO_IOCHK:	reason = "SIO IOCHK occurred on ISA bus"; break;
 	case MCHK_K_DCSR:	reason = "MCHK_K_DCSR"; break;
 	case MCHK_K_UNKNOWN:
diff -u --recursive --new-file v2.1.119/linux/arch/alpha/kernel/smc37c669.c linux/arch/alpha/kernel/smc37c669.c
--- v2.1.119/linux/arch/alpha/kernel/smc37c669.c	Tue Jul 21 00:15:29 1998
+++ linux/arch/alpha/kernel/smc37c669.c	Fri Sep  4 12:10:46 1998
@@ -1709,7 +1709,7 @@
 **	the configuration is also updated.  If the device function
 **	is currently disabled, only the local shadow copy is 
 **	updated and the actual device function will be updated
-**	if/when is is enabled.
+**	if/when it is enabled.
 **
 **--
 */
diff -u --recursive --new-file v2.1.119/linux/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- v2.1.119/linux/arch/i386/boot/setup.S	Wed Jun 24 22:54:03 1998
+++ linux/arch/i386/boot/setup.S	Tue Sep  1 10:18:42 1998
@@ -18,7 +18,7 @@
 ! March 1993/June 1994 (Christoph.Niemann@linux.org)
 !
 ! add APM BIOS checking by Stephen Rothwell, May 1994
-! (Stephen.Rothwell@pd.necisa.oz.au)
+! (Stephen.Rothwell@canb.auug.org.au)
 !
 ! High load stuff, initrd support and position independency
 ! by Hans Lermen & Werner Almesberger, February 1996
diff -u --recursive --new-file v2.1.119/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.1.119/linux/arch/i386/kernel/entry.S	Thu Aug 20 17:05:15 1998
+++ linux/arch/i386/kernel/entry.S	Wed Sep  2 09:32:18 1998
@@ -105,23 +105,21 @@
 	popl %eax;	\
 1:	popl %ds;	\
 2:	popl %es;	\
-3:	addl $4,%esp;	\
-4:	iret;		\
+	addl $4,%esp;	\
+3:	iret;		\
 .section fixup,"ax";	\
-5:	pushl $0;	\
-	popl %ds;	\
+4:	movl $0,(%esp);	\
+	jmp 1b;		\
+5:	movl $0,(%esp);	\
 	jmp 2b;		\
-6:	pushl $0;	\
-	popl %es;	\
-	jmp 3b;		\
-7:	pushl $11;	\
+6:	pushl $11;	\
 	call do_exit;	\
 .previous;		\
 .section __ex_table,"a";\
 	.align 4;	\
-	.long 1b,5b;	\
-	.long 2b,6b;	\
-	.long 4b,7b;	\
+	.long 1b,4b;	\
+	.long 2b,5b;	\
+	.long 3b,6b;	\
 .previous
 
 #define GET_CURRENT(reg) \
diff -u --recursive --new-file v2.1.119/linux/arch/i386/kernel/io_apic.c linux/arch/i386/kernel/io_apic.c
--- v2.1.119/linux/arch/i386/kernel/io_apic.c	Tue Aug 18 22:02:02 1998
+++ linux/arch/i386/kernel/io_apic.c	Mon Aug 31 16:03:17 1998
@@ -921,9 +921,10 @@
 static inline void self_IPI(unsigned int irq)
 {
 	irq_desc_t *desc = irq_desc + irq;
+	unsigned int status = desc->status;
 
-	if (desc->events && !desc->ipi) {
-		desc->ipi = 1;
+	if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) {
+		desc->status = status | IRQ_REPLAY;
 		send_IPI(APIC_DEST_SELF, IO_APIC_VECTOR(irq));
 	}
 }
@@ -960,6 +961,7 @@
 {
 	irq_desc_t *desc = irq_desc + irq;
 	struct irqaction * action;
+	unsigned int status;
 
 	spin_lock(&irq_controller_lock);
 
@@ -968,19 +970,19 @@
 	 * and do not need to be masked.
 	 */
 	ack_APIC_irq();
-	desc->ipi = 0;
-	desc->events = 1;
+	status = desc->status & ~IRQ_REPLAY;
+	status |= IRQ_PENDING;
 
 	/*
 	 * If the IRQ is disabled for whatever reason, we cannot
 	 * use the action we have.
 	 */
 	action = NULL;
-	if (!(desc->status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
+	if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
 		action = desc->action;
-		desc->status = IRQ_INPROGRESS;
-		desc->events = 0;
+		status &= ~IRQ_PENDING;
 	}
+	desc->status = status | IRQ_INPROGRESS;
 	spin_unlock(&irq_controller_lock);
 
 	/*
@@ -996,18 +998,15 @@
 	 * pending events.
 	 */
 	for (;;) {
-		int pending;
-
 		handle_IRQ_event(irq, regs);
 
 		spin_lock(&irq_controller_lock);
-		pending = desc->events;
-		desc->events = 0;
-		if (!pending)
+		if (!(desc->status & IRQ_PENDING))
 			break;
+		desc->status &= ~IRQ_PENDING;
 		spin_unlock(&irq_controller_lock);
 	}
-	desc->status &= IRQ_DISABLED;
+	desc->status &= ~IRQ_INPROGRESS;
 	spin_unlock(&irq_controller_lock);
 
 	irq_exit(cpu, irq);
@@ -1018,6 +1017,7 @@
 {
 	irq_desc_t *desc = irq_desc + irq;
 	struct irqaction * action;
+	unsigned int status;
 
 	spin_lock(&irq_controller_lock);
 	/*
@@ -1029,18 +1029,17 @@
 	 * So this all has to be within the spinlock.
 	 */
 	mask_IO_APIC_irq(irq);
-
-	desc->ipi = 0;
+	status = desc->status & ~IRQ_REPLAY;
 
 	/*
 	 * If the IRQ is disabled for whatever reason, we must
 	 * not enter the IRQ action.
 	 */
 	action = NULL;
-	if (!(desc->status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
+	if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
 		action = desc->action;
-		desc->status = IRQ_INPROGRESS;
 	}
+	desc->status = status | IRQ_INPROGRESS;
 
 	ack_APIC_irq();
 	spin_unlock(&irq_controller_lock);
@@ -1055,7 +1054,7 @@
 
 	spin_lock(&irq_controller_lock);
 	desc->status &= ~IRQ_INPROGRESS;
-	if (!desc->status)
+	if (!(desc->status & IRQ_DISABLED))
 		unmask_IO_APIC_irq(irq);
 	spin_unlock(&irq_controller_lock);
 
@@ -1160,6 +1159,31 @@
 	}
 }
 
+/*
+ *
+ * IRQ's that are handled by the old PIC in all cases:
+ * - IRQ2 is the cascade IRQ, and cannot be a io-apic IRQ.
+ *   Linux doesn't really care, as it's not actually used
+ *   for any interrupt handling anyway.
+ * - IRQ13 is the FPU error IRQ, and may be connected
+ *   directly from the FPU to the old PIC. Linux doesn't
+ *   really care, because Linux doesn't want to use IRQ13
+ *   anyway (exception 16 is the proper FPU error signal)
+ * - IRQ9 is broken on PIIX4 motherboards:
+ *
+ *		"IRQ9 cannot be re-assigned"
+ *
+ *		IRQ9 is not available to assign to
+ *		ISA add-in cards because it is
+ *		dedicated to the power
+ *		management function of the PIIX4
+ *		controller on the motherboard.
+ *		This is true for other motherboards
+ *		which use the 82371AB PIIX4
+ *		component.
+ */
+#define PIC_IRQS	((1<<2)|(1<<9)|(1<<13))
+
 void __init setup_IO_APIC(void)
 {
 	init_sym_mode();
@@ -1177,7 +1201,7 @@
 		pirqs_enabled)
 	{
 		printk("ENABLING IO-APIC IRQs\n");
-		io_apic_irqs = ~((1<<2)|(1<<9)|(1<<13));
+		io_apic_irqs = ~PIC_IRQS;
 	} else {
 		if (ioapic_blacklisted())
 			printk(" blacklisted board, DISABLING IO-APIC IRQs\n");
diff -u --recursive --new-file v2.1.119/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v2.1.119/linux/arch/i386/kernel/irq.c	Thu Aug 27 19:56:28 1998
+++ linux/arch/i386/kernel/irq.c	Wed Sep  2 16:18:32 1998
@@ -105,8 +105,8 @@
 };
 
 irq_desc_t irq_desc[NR_IRQS] = {
-	[0 ... 15] = { 0, 0, 0, &i8259A_irq_type, },	/* default to standard ISA IRQs */
-	[16 ... 63] = { 0, 0, 0, &no_irq_type, },	/* 'high' PCI IRQs filled in on demand */
+	[0 ... 15] = { 0, &i8259A_irq_type, },	/* default to standard ISA IRQs */
+	[16 ... 63] = { 0, &no_irq_type, },	/* 'high' PCI IRQs filled in on demand */
 };
 
 int irq_vector[NR_IRQS] = { IRQ0_TRAP_VECTOR , 0 };
@@ -663,7 +663,8 @@
 
 	if (handle_IRQ_event(irq, regs)) {
 		spin_lock(&irq_controller_lock);
-		if (!(irq_desc[irq].status &= IRQ_DISABLED))
+		irq_desc[irq].status &= ~IRQ_INPROGRESS;
+		if (!(irq_desc[irq].status & IRQ_DISABLED))
 			enable_8259A_irq(irq);
 		spin_unlock(&irq_controller_lock);
 	}
@@ -683,10 +684,6 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&irq_controller_lock, flags);
-	/*
-	 * At this point we may actually have a pending interrupt being active
-	 * on another CPU. So don't touch the IRQ_INPROGRESS bit..
-	 */
 	irq_desc[irq].status |= IRQ_DISABLED;
 	irq_desc[irq].handler->disable(irq);
 	spin_unlock_irqrestore(&irq_controller_lock, flags);
@@ -793,6 +790,7 @@
 	*p = new;
 
 	if (!shared) {
+		irq_desc[irq].status = 0;
 #ifdef __SMP__
 		if (IO_APIC_IRQ(irq)) {
 			/*
@@ -803,11 +801,10 @@
 			if (irq < 16) {
 				disable_8259A_irq(irq);
 				if (i8259A_irq_pending(irq))
-					irq_desc[irq].events = 1;
+					irq_desc[irq].status = IRQ_PENDING;
 			}
 		}
 #endif
-		irq_desc[irq].status = 0;
 		irq_desc[irq].handler->enable(irq);
 	}
 	spin_unlock_irqrestore(&irq_controller_lock,flags);
@@ -863,8 +860,10 @@
 		/* Found it - now free it */
 		*p = action->next;
 		kfree(action);
-		if (!irq_desc[irq].action)
+		if (!irq_desc[irq].action) {
+			irq_desc[irq].status |= IRQ_DISABLED;
 			irq_desc[irq].handler->disable(irq);
+		}
 		goto out;
 	}
 	printk("Trying to free free IRQ%d\n",irq);
@@ -880,9 +879,9 @@
  * with "IRQ_INPROGRESS" asserted and the interrupt
  * disabled.
  */
-unsigned long probe_irq_on (void)
+unsigned long probe_irq_on(void)
 {
-	unsigned int i, irqs = 0;
+	unsigned int i;
 	unsigned long delay;
 
 	/*
@@ -891,51 +890,68 @@
 	spin_lock_irq(&irq_controller_lock);
 	for (i = NR_IRQS-1; i > 0; i--) {
 		if (!irq_desc[i].action) {
-			irq_desc[i].status = 0;
+			unsigned int status = irq_desc[i].status | IRQ_AUTODETECT;
+			irq_desc[i].status = status & ~(IRQ_INPROGRESS | IRQ_PENDING);
 			irq_desc[i].handler->enable(i);
-			irqs |= (1 << i);
 		}
 	}
 	spin_unlock_irq(&irq_controller_lock);
 
 	/*
-	 * wait for spurious interrupts to increase counters
+	 * Wait for spurious interrupts to trigger
 	 */
 	for (delay = jiffies + HZ/10; delay > jiffies; )
 		/* about 100ms delay */ synchronize_irq();
 
 	/*
-	 * now filter out any obviously spurious interrupts
+	 * Now filter out any obviously spurious interrupts
 	 */
 	spin_lock_irq(&irq_controller_lock);
 	for (i=0; i<NR_IRQS; i++) {
-		if (irq_desc[i].status & IRQ_INPROGRESS)
-			irqs &= ~(1UL << i);
+		unsigned int status = irq_desc[i].status;
+
+		if (!(status & IRQ_AUTODETECT))
+			continue;
+		
+		/* It triggered already - consider it spurious. */
+		if (status & IRQ_INPROGRESS) {
+			irq_desc[i].status = status & ~IRQ_AUTODETECT;
+			irq_desc[i].handler->disable(i);
+		}
 	}
 	spin_unlock_irq(&irq_controller_lock);
 
-	return irqs;
+	return 0x12345678;
 }
 
-int probe_irq_off (unsigned long irqs)
+int probe_irq_off(unsigned long unused)
 {
-	int i, irq_found = -1;
+	int i, irq_found, nr_irqs;
+
+	if (unused != 0x12345678)
+		printk("Bad IRQ probe from %lx\n", (&unused)[-1]);
 
+	nr_irqs = 0;
+	irq_found = 0;
 	spin_lock_irq(&irq_controller_lock);
 	for (i=0; i<NR_IRQS; i++) {
-		if ((irqs & 1) && (irq_desc[i].status & IRQ_INPROGRESS)) {
-			if (irq_found != -1) {
-				irq_found = -irq_found;
-				goto out;
-			}
-			irq_found = i;
+		unsigned int status = irq_desc[i].status;
+
+		if (!(status & IRQ_AUTODETECT))
+			continue;
+
+		if (status & IRQ_INPROGRESS) {
+			if (!nr_irqs)
+				irq_found = i;
+			nr_irqs++;
 		}
-		irqs >>= 1;
+		irq_desc[i].status = status & ~IRQ_AUTODETECT;
+		irq_desc[i].handler->disable(i);
 	}
-	if (irq_found == -1)
-		irq_found = 0;
-out:
 	spin_unlock_irq(&irq_controller_lock);
+
+	if (nr_irqs > 1)
+		irq_found = -irq_found;
 	return irq_found;
 }
 
@@ -948,10 +964,9 @@
 	outb_p(LATCH & 0xff , 0x40);	/* LSB */
 	outb(LATCH >> 8 , 0x40);	/* MSB */
 
-	for (i=0; i<NR_IRQS; i++) {
-		irq_desc[i].events = 0;
-		irq_desc[i].status = 0;
-	}
+	for (i=0; i<NR_IRQS; i++)
+		irq_desc[i].status = IRQ_DISABLED;
+
 	/*
 	 * 16 old-style INTA-cycle interrupt gates:
 	 */
diff -u --recursive --new-file v2.1.119/linux/arch/i386/kernel/irq.h linux/arch/i386/kernel/irq.h
--- v2.1.119/linux/arch/i386/kernel/irq.h	Thu Aug  6 14:06:28 1998
+++ linux/arch/i386/kernel/irq.h	Wed Sep  2 13:36:23 1998
@@ -16,12 +16,13 @@
 
 
 /*
- * Status: reason for being disabled: somebody has
- * done a "disable_irq()" or we must not re-enter the
- * already executing irq..
+ * IRQ line status.
  */
-#define IRQ_INPROGRESS	1
-#define IRQ_DISABLED	2
+#define IRQ_INPROGRESS	1	/* IRQ handler active - do not enter! */
+#define IRQ_DISABLED	2	/* IRQ disabled - do not enter! */
+#define IRQ_PENDING	4	/* IRQ pending - replay on enable */
+#define IRQ_REPLAY	8	/* IRQ has been replayed but not acked yet */
+#define IRQ_AUTODETECT	16	/* IRQ is being autodetected */
 
 /*
  * This is the "IRQ descriptor", which contains various information
@@ -32,8 +33,6 @@
  */
 typedef struct {
 	unsigned int status;			/* IRQ status - IRQ_INPROGRESS, IRQ_DISABLED */
-	unsigned int events;			/* Do we have any pending events? */
-	unsigned int ipi;			/* Have we sent off the pending IPI? */
 	struct hw_interrupt_type *handler;	/* handle/enable/disable functions */
 	struct irqaction *action;		/* IRQ action list */
 	unsigned int unused[3];
diff -u --recursive --new-file v2.1.119/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v2.1.119/linux/arch/i386/kernel/process.c	Wed Aug 26 11:37:33 1998
+++ linux/arch/i386/kernel/process.c	Fri Sep  4 23:44:44 1998
@@ -488,21 +488,19 @@
 
 void release_segments(struct mm_struct *mm)
 {
-	void * ldt = mm->segments;
-	int nr;
-
 	/* forget local segments */
-	__asm__ __volatile__("movl %w0,%%fs ; movl %w0,%%gs ; lldt %w0"
+	__asm__ __volatile__("movl %w0,%%fs ; movl %w0,%%gs"
 		: /* no outputs */
 		: "r" (0));
-	current->tss.ldt = 0;
-	/*
-	 * Set the GDT entry back to the default.
-	 */
-	nr = current->tarray_ptr - &task[0];
-	set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY, &default_ldt, 1);
+	if (mm->segments) {
+		void * ldt = mm->segments;
+
+		/*
+		 * Get the LDT entry from init_task.
+		 */
+		current->tss.ldt = _LDT(0);
+		load_ldt(0);
 
-	if (ldt) {
 		mm->segments = NULL;
 		vfree(ldt);
 	}
@@ -557,23 +555,22 @@
 	void * old_ldt = old_mm->segments, * ldt = old_ldt;
 	int ldt_size = LDT_ENTRIES;
 
-	p->tss.ldt = _LDT(nr);
+	/* "default_ldt" - use the one from init_task */
+	p->tss.ldt = _LDT(0);
 	if (old_ldt) {
 		if (new_mm) {
 			ldt = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
 			new_mm->segments = ldt;
 			if (!ldt) {
 				printk(KERN_WARNING "ldt allocation failed\n");
-				goto no_ldt;
+				return;
 			}
 			memcpy(ldt, old_ldt, LDT_ENTRIES*LDT_ENTRY_SIZE);
 		}
-	} else {
-	no_ldt:
-		ldt = &default_ldt;
-		ldt_size = 1;
+		p->tss.ldt = _LDT(nr);
+		set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY, ldt, ldt_size);
+		return;
 	}
-	set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY, ldt, ldt_size);
 }
 
 /*
diff -u --recursive --new-file v2.1.119/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.1.119/linux/arch/i386/kernel/setup.c	Tue Aug 18 22:02:02 1998
+++ linux/arch/i386/kernel/setup.c	Fri Sep  4 19:16:46 1998
@@ -439,8 +439,8 @@
 	    NULL, NULL, NULL, NULL }},
 	{ X86_VENDOR_INTEL,	6,
 	  { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)", 
-	    NULL, "Pentium II (Deschutes)", NULL, NULL, NULL, NULL, NULL, NULL,
-	    NULL, NULL, NULL, NULL }},
+	    NULL, "Pentium II (Deschutes)", "Celeron (Mendocino)", 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",
 	    "486 DX/4", "486 DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT",
@@ -467,6 +467,7 @@
 	char *p = NULL;
 
 	c->loops_per_sec = loops_per_sec;
+	c->x86_cache_size = -1;
 
 	get_cpu_vendor(c);
 
@@ -479,13 +480,64 @@
 		return;
 	}
 
-	if (c->x86_model < 16)
-		for (i=0; i<sizeof(cpu_models)/sizeof(struct cpu_model_info); i++)
-			if (cpu_models[i].vendor == c->x86_vendor &&
-			    cpu_models[i].x86 == c->x86) {
+	for (i = 0; i < sizeof(cpu_models)/sizeof(struct cpu_model_info); i++) {
+		if (c->cpuid_level > 1) {
+			/* supports eax=2  call */
+			int edx, cache_size, dummy;
+			
+			cpuid(2, &dummy, &dummy, &dummy, &edx);
+
+			/* We need only the LSB */
+			edx &= 0xff;
+
+			switch (edx) {
+				case 0x40:
+					cache_size = 0;
+
+				case 0x41:
+					cache_size = 128;
+					break;
+
+				case 0x42:
+					cache_size = 256;
+					break;
+
+				case 0x43:
+					cache_size = 512;
+					break;
+
+				case 0x44:
+					cache_size = 1024;
+					break;
+
+				case 0x45:
+					cache_size = 2048;
+					break;
+
+				default:
+					cache_size = 0;
+					break;
+			}
+
+			c->x86_cache_size = cache_size; 
+		}
+
+		if (cpu_models[i].vendor == c->x86_vendor &&
+		    cpu_models[i].x86 == c->x86) {
+			if (c->x86_model <= 16)
 				p = cpu_models[i].model_names[c->x86_model];
-				break;
+
+			/* Names for the Pentium II processors */
+			if ((cpu_models[i].vendor == X86_VENDOR_INTEL)
+			    && (cpu_models[i].x86 == 6) 
+			    && (c->x86_model == 5)
+			    && (c->x86_cache_size == 0)) {
+				p = "Celeron";
 			}
+		}
+			
+	}
+
 	if (p) {
 		strcpy(c->x86_model_id, p);
 		return;
@@ -548,14 +600,17 @@
 		if (!(cpu_present_map & (1<<n)))
 			continue;
 #endif
-		p += sprintf(p, "processor\t: %d\n"
+		p += sprintf(p,"processor\t: %d\n"
+			       "vendor_id\t: %s\n"
 			       "cpu family\t: %c\n"
-			       "model\t\t: %s\n"
-			       "vendor_id\t: %s\n",
+			       "model\t\t: %d\n"
+			       "model name\t: %s\n",
 			       n,
+			       c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown",
 			       c->x86 + '0',
-			       c->x86_model_id[0] ? c->x86_model_id : "unknown",
-			       c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown");
+			       c->x86_model,
+			       c->x86_model_id[0] ? c->x86_model_id : "unknown");
+		
 		if (c->x86_mask) {
 			if (c->x86_vendor == X86_VENDOR_CYRIX)
 				p += sprintf(p, "stepping\t: %s\n", Cx86_step);
@@ -564,6 +619,10 @@
 		} else
 			p += sprintf(p, "stepping\t: unknown\n");
 
+		/* Cache size */
+		if (c->x86_cache_size >= 0)
+			p += sprintf(p, "cache size\t: %d KB\n", c->x86_cache_size);
+		
 		/* Modify the capabilities according to chip type */
 		if (c->x86_mask) {
 			if (c->x86_vendor == X86_VENDOR_CYRIX) {
diff -u --recursive --new-file v2.1.119/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v2.1.119/linux/arch/i386/kernel/signal.c	Wed Aug 26 11:37:33 1998
+++ linux/arch/i386/kernel/signal.c	Tue Sep  1 11:57:14 1998
@@ -22,6 +22,17 @@
 #include <asm/ucontext.h>
 #include <asm/uaccess.h>
 
+void checksignals(void)
+{
+	sigset_t *blocked = &current->blocked;
+	unsigned long mask = blocked->sig[0] | sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT);
+	mask &= blocked->sig[1];
+	if (~mask) {
+		printk("Bad signal mask\n");
+		*(int *) 0 = 0;
+	}
+}
+
 #define DEBUG_SIG 0
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
diff -u --recursive --new-file v2.1.119/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.1.119/linux/arch/i386/kernel/smp.c	Thu Aug 27 19:56:28 1998
+++ linux/arch/i386/kernel/smp.c	Sun Aug 30 13:49:20 1998
@@ -371,7 +371,7 @@
 					/*
 					 * we use the first one only currently
 					 */
-					if (!ioapics)
+					if (ioapics == 1)
 						mp_ioapic_addr = m->mpc_apicaddr;
 				}
 				mpt+=sizeof(*m);
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/amiga/config.c linux/arch/m68k/amiga/config.c
--- v2.1.119/linux/arch/m68k/amiga/config.c	Mon Aug  3 12:45:44 1998
+++ linux/arch/m68k/amiga/config.c	Wed Sep  2 09:39:18 1998
@@ -20,6 +20,7 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/init.h>
+#include <linux/zorro.h>
 
 #include <asm/bootinfo.h>
 #include <asm/setup.h>
@@ -29,7 +30,7 @@
 #include <asm/amigaints.h>
 #include <asm/irq.h>
 #include <asm/machdep.h>
-#include <linux/zorro.h>
+#include <asm/io.h>
 
 unsigned long amiga_model;
 unsigned long amiga_eclock;
@@ -421,10 +422,6 @@
    */
   if (AMIGAHW_PRESENT(MAGIC_REKICK))
 	  *(unsigned char *)ZTWO_VADDR(0xde0002) |= 0x80;
-
-#ifdef CONFIG_ZORRO
-  zorro_init();
-#endif
 }
 
 static unsigned short jiffy_ticks;
@@ -689,8 +686,8 @@
 
 static void amiga_reset (void)
 {
-  unsigned long jmp_addr040 = VTOP(&&jmp_addr_label040);
-  unsigned long jmp_addr = VTOP(&&jmp_addr_label);
+  unsigned long jmp_addr040 = virt_to_phys(&&jmp_addr_label040);
+  unsigned long jmp_addr = virt_to_phys(&&jmp_addr_label);
 
   cli();
   if (CPU_IS_040_OR_060)
@@ -785,7 +782,7 @@
     savekmsg = (struct savekmsg *)amiga_chip_alloc(SAVEKMSG_MAXMEM);
     savekmsg->magic1 = SAVEKMSG_MAGIC1;
     savekmsg->magic2 = SAVEKMSG_MAGIC2;
-    savekmsg->magicptr = VTOP(savekmsg);
+    savekmsg->magicptr = virt_to_phys(savekmsg);
     savekmsg->size = 0;
 }
 
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/atari/atasound.c linux/arch/m68k/atari/atasound.c
--- v2.1.119/linux/arch/m68k/atari/atasound.c	Tue Jun 23 10:01:20 1998
+++ linux/arch/m68k/atari/atasound.c	Wed Sep  2 09:39:18 1998
@@ -61,17 +61,21 @@
 	save_flags(flags);
 	cli();
 
-	/* Convert from frequency value to PSG period value (base
-	   frequency 125 kHz).  */
-	period = PSG_FREQ / hz;
-
-	if (period > 0xfff) period = 0xfff;
 
 	/* Disable generator A in mixer control.  */
 	sound_ym.rd_data_reg_sel = 7;
 	tmp = sound_ym.rd_data_reg_sel;
 	tmp |= 011;
 	sound_ym.wd_data = tmp;
+
+	if (hz) {
+	    /* Convert from frequency value to PSG period value (base
+	       frequency 125 kHz).  */
+		
+	    period = PSG_FREQ / hz;
+
+	    if (period > 0xfff) period = 0xfff;
+
 	/* Set generator A frequency to hz.  */
 	sound_ym.rd_data_reg_sel = 0;
 	sound_ym.wd_data = period & 0xff;
@@ -101,6 +105,6 @@
 	sound_ym.rd_data_reg_sel = 7;
 	tmp &= ~1;
 	sound_ym.wd_data = tmp;
-
+	}
 	restore_flags(flags);
 }
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/atari/config.c linux/arch/m68k/atari/config.c
--- v2.1.119/linux/arch/m68k/atari/config.c	Mon Aug  3 12:45:44 1998
+++ linux/arch/m68k/atari/config.c	Wed Sep  2 09:39:18 1998
@@ -39,6 +39,7 @@
 #include <asm/system.h>
 #include <asm/machdep.h>
 #include <asm/hwtest.h>
+#include <asm/io.h>
 
 u_long atari_mch_cookie;
 u_long atari_mch_type = 0;
@@ -616,7 +617,7 @@
 	 : : : "d0" );
     
     if (CPU_IS_040_OR_060) {
-        unsigned long jmp_addr040 = VTOP(&&jmp_addr_label040);
+        unsigned long jmp_addr040 = virt_to_phys(&&jmp_addr_label040);
 	if (CPU_IS_060) {
 	    /* 68060: clear PCR to turn off superscalar operation */
 	    __asm__ __volatile__
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/atari/joystick.c linux/arch/m68k/atari/joystick.c
--- v2.1.119/linux/arch/m68k/atari/joystick.c	Tue Jun 23 10:01:20 1998
+++ linux/arch/m68k/atari/joystick.c	Wed Sep  2 09:39:18 1998
@@ -126,6 +126,7 @@
 	NULL,		/* joystick_ioctl */
 	NULL,		/* joystick_mmap */
 	open_joystick,
+	NULL,		/* flush */
 	release_joystick
 };
 
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/atari/stram.c linux/arch/m68k/atari/stram.c
--- v2.1.119/linux/arch/m68k/atari/stram.c	Wed Aug 26 11:37:33 1998
+++ linux/arch/m68k/atari/stram.c	Wed Sep  2 09:39:18 1998
@@ -25,6 +25,7 @@
 #include <asm/pgtable.h>
 #include <asm/atarihw.h>
 #include <asm/atari_stram.h>
+#include <asm/io.h>
 
 
 #ifdef CONFIG_STRAM_SWAP
@@ -121,9 +122,6 @@
 #define ALIGN_IF_SWAP(x)	(x)
 #endif
 
-/* map entry for reserved swap page (used as buffer) */
-#define SWP_RSVD			0x80
-
 /* get index of swap page at address 'addr' */
 #define SWAP_NR(addr)		(((unsigned long)(addr)-swap_start) >> PAGE_SHIFT)
 
@@ -232,10 +230,10 @@
 		       long entry, unsigned long page, int isswap );
 static int unswap_process( struct mm_struct * mm, unsigned long entry,
 			   unsigned long page, int isswap );
-static int unswap_by_move( unsigned char *map, unsigned long max, unsigned
-			   long start, unsigned long n_pages );
-static int unswap_by_read( unsigned char *map, unsigned long max, unsigned
-			   long start, unsigned long n_pages );
+static int unswap_by_move(unsigned short *, unsigned long, unsigned long,
+			  unsigned long);
+static int unswap_by_read(unsigned short *, unsigned long, unsigned long,
+			  unsigned long);
 static void *get_stram_region( unsigned long n_pages );
 static void free_stram_region( unsigned long offset, unsigned long n_pages
 			       );
@@ -275,7 +273,7 @@
 
 	/* determine whether kernel code resides in ST-RAM (then ST-RAM is the
 	 * first memory block at virtual 0x0) */
-	stram_start = PTOV( 0 );
+	stram_start = phys_to_virt( 0 );
 	kernel_in_stram = (stram_start == 0);
 
 	for( i = 0; i < m68k_num_memory; ++i ) {
@@ -356,11 +354,13 @@
 		DPRINTK( "atari_stram_reserve_pages: swapping enabled; "
 				 "swap=%08lx-%08lx\n", swap_start, swap_end );
 		
-		/* reserve some amount of memory for maintainance of swapping itself:
-		 * 1 page for the lockmap, and one page for each 4096 (PAGE_SIZE) swap
-		 * pages. (1 byte for each page) */
+		/* reserve some amount of memory for maintainance of
+		 * swapping itself: 1 page for the lockmap, and one page
+		 * for each 2048 (PAGE_SIZE/2) swap pages. (2 bytes for
+		 * each page) */
 		swap_data = start_mem;
-		start_mem += PAGE_ALIGN(SWAP_NR(swap_end)) + PAGE_SIZE;
+		start_mem += (((SWAP_NR(swap_end) + PAGE_SIZE/2 - 1)
+			       >> (PAGE_SHIFT-1)) + 1) << PAGE_SHIFT;
 		/* correct swap_start if necessary */
 		if (swap_start == swap_data)
 			swap_start = start_mem;
@@ -621,7 +621,7 @@
 	p->swap_file    = &fake_dentry[0];
 	p->swap_device  = 0;
 	p->swap_lockmap = (unsigned char *)(swap_data);
-	p->swap_map	    = (unsigned char *)(swap_data + PAGE_SIZE);
+	p->swap_map	= (unsigned short *)(swap_data + PAGE_SIZE);
 	p->cluster_nr   = 0;
 	p->next         = -1;
 	p->prio         = 0x7ff0;	/* a rather high priority, but not the higest
@@ -638,7 +638,7 @@
 	memset( p->swap_lockmap, 0, PAGE_SIZE );
 
 	/* initialize swap_map: set regions that are already allocated or belong
-	 * to kernel data space to SWP_RSVD, otherwise to free */
+	 * to kernel data space to SWAP_MAP_BAD, otherwise to free */
 	j = 0; /* # of free pages */
 	k = 0; /* # of already allocated pages (from pre-mem_init stram_alloc()) */
 	p->lowest_bit = 0;
@@ -646,11 +646,11 @@
 	for( i = 1, addr = (unsigned long)SWAP_ADDR(1); i < p->max;
 		 i++, addr += PAGE_SIZE ) {
 		if (in_some_region( addr )) {
-			p->swap_map[i] = SWP_RSVD;
+			p->swap_map[i] = SWAP_MAP_BAD;
 			++k;
 		}
 		else if (kernel_in_stram && addr < start_mem ) {
-			p->swap_map[i] = SWP_RSVD;
+			p->swap_map[i] = SWAP_MAP_BAD;
 		}
 		else {
 			p->swap_map[i] = 0;
@@ -660,7 +660,7 @@
 		}
 	}
 	/* first page always reserved (and doesn't really belong to swap space) */
-	p->swap_map[0] = SWP_RSVD;
+	p->swap_map[0] = SWAP_MAP_BAD;
 
 	/* now swapping to this device ok */
 	p->pages = j + k;
@@ -846,8 +846,8 @@
 }
 
 
-static int unswap_by_move( unsigned char *map, unsigned long max,
-						   unsigned long start, unsigned long n_pages )
+static int unswap_by_move(unsigned short *map, unsigned long max,
+			  unsigned long start, unsigned long n_pages)
 {
 	struct task_struct *p;
 	unsigned long entry, rover = (start == 1) ? n_pages+1 : 1;
@@ -859,11 +859,11 @@
 	/* can free the allocated pages by moving them to other swap pages */
 	for( i = start; i < start+n_pages; ++i ) {
 		if (!map[i]) {
-			map[i] = SWP_RSVD;
+			map[i] = SWAP_MAP_BAD;
 			DPRINTK( "unswap: page %lu was free\n", i );
 			continue;
 		}
-		else if (map[i] == SWP_RSVD) {
+		else if (map[i] == SWAP_MAP_BAD) {
 			printk( KERN_ERR "get_stram_region: page %lu already "
 					"reserved??\n", i );
 		}
@@ -923,7 +923,7 @@
 
 		DPRINTK( "unswap: map[i=%lu]=%u map[j=%lu]=%u nr_swap=%u\n",
 				 i, map[i], j, map[j], nr_swap_pages );
-		map[i] = SWP_RSVD;
+		map[i] = SWAP_MAP_BAD;
 		if (stram_swap_info->lowest_bit == i)
 			stram_swap_info->lowest_bit++;
 		if (stram_swap_info->highest_bit == i)
@@ -933,8 +933,8 @@
 	return( 0 );
 }
 
-static int unswap_by_read( unsigned char *map, unsigned long max,
-						   unsigned long start, unsigned long n_pages )
+static int unswap_by_read(unsigned short *map, unsigned long max,
+			  unsigned long start, unsigned long n_pages)
 {
 	struct task_struct *p;
 	unsigned long entry, page = 0;
@@ -944,7 +944,7 @@
 			 start, start+n_pages-1 );
 
 	for( i = start; i < start+n_pages; ++i ) {
-		if (map[i] == SWP_RSVD) {
+		if (map[i] == SWAP_MAP_BAD) {
 			printk( KERN_ERR "get_stram_region: page %lu already "
 					"reserved??\n", i );
 			continue;
@@ -977,7 +977,7 @@
 				 * we freed the last reference to an overflowed entry,
 				 * or the system has lost track of the use counts.
 				 */
-				if (map[i] && map[i] != SWP_RSVD-1)
+				if (map[i] && map[i] != SWAP_MAP_MAX)
 					printk( KERN_ERR "get_stram_region: swap entry %08lx "
 							"not used by any process\n", entry );
 				/* quit while loop and overwrite bad map entry */
@@ -990,7 +990,7 @@
 
 		DPRINTK( "unswap: map[i=%lu]=%u nr_swap=%u\n",
 				 i, map[i], nr_swap_pages );
-		map[i] = SWP_RSVD;
+		map[i] = SWAP_MAP_BAD;
 		if (stram_swap_info->lowest_bit == i)
 			stram_swap_info->lowest_bit++;
 		if (stram_swap_info->highest_bit == i)
@@ -1008,7 +1008,7 @@
  */
 static void *get_stram_region( unsigned long n_pages )
 {
-	unsigned char *map = stram_swap_info->swap_map;
+	unsigned short *map = stram_swap_info->swap_map;
 	unsigned long max = stram_swap_info->max;
 	unsigned long start, total_free, region_free;
 	int err;
@@ -1046,7 +1046,7 @@
  */
 static void free_stram_region( unsigned long offset, unsigned long n_pages )
 {
-	unsigned char *map = stram_swap_info->swap_map;
+	unsigned short *map = stram_swap_info->swap_map;
 
 	DPRINTK( "free_stram_region(offset=%lu,n_pages=%lu)\n", offset, n_pages );
 
@@ -1057,7 +1057,7 @@
 
 	/* un-reserve the freed pages */
 	for( ; n_pages > 0; ++offset, --n_pages ) {
-		if (map[offset] != SWP_RSVD)
+		if (map[offset] != SWAP_MAP_BAD)
 			printk( KERN_ERR "free_stram_region: Swap page %lu was not "
 					"reserved\n", offset );
 		map[offset] = 0;
@@ -1092,11 +1092,11 @@
 }
 
 
-static unsigned long find_free_region( unsigned long n_pages,
-									   unsigned long *total_free,
-									   unsigned long *region_free )
+static unsigned long find_free_region(unsigned long n_pages,
+				      unsigned long *total_free,
+				      unsigned long *region_free)
 {
-	unsigned char *map = stram_swap_info->swap_map;
+	unsigned short *map = stram_swap_info->swap_map;
 	unsigned long max = stram_swap_info->max;
 	unsigned long head, tail, max_start;
 	long nfree, max_free;
@@ -1111,7 +1111,7 @@
 	/* increment tail until final window size reached, and count free pages */
 	nfree = 0;
 	for( tail = head; tail-head < n_pages && tail < max-n_pages; ++tail ) {
-		if (map[tail] == SWP_RSVD) {
+		if (map[tail] == SWAP_MAP_BAD) {
 			head = tail+1;
 			goto start_over;
 		}
@@ -1134,7 +1134,7 @@
 	 * possible are free */
 	while( tail < max ) {
 		nfree -= (map[head++] == 0);
-		if (map[tail] == SWP_RSVD) {
+		if (map[tail] == SWAP_MAP_BAD) {
 			head = tail+1;
 			goto start_over;
 		}
@@ -1413,8 +1413,8 @@
 	int len = 0;
 	BLOCK *p;
 #ifdef CONFIG_STRAM_SWAP
-    int i;
-	unsigned char *map = stram_swap_info->swap_map;
+	int i;
+	unsigned short *map = stram_swap_info->swap_map;
 	unsigned long max = stram_swap_info->max;
 	unsigned free = 0, used = 0, rsvd = 0;
 #endif
@@ -1424,7 +1424,7 @@
 		for( i = 1; i < max; ++i ) {
 			if (!map[i])
 				++free;
-			else if (map[i] == SWP_RSVD)
+			else if (map[i] == SWAP_MAP_BAD)
 				++rsvd;
 			else
 				++used;
@@ -1466,7 +1466,8 @@
 		if (len + 50 >= PAGE_SIZE)
 			break;
 		PRINT_PROC( "0x%08lx-0x%08lx: %s (",
-					VTOP(p->start), VTOP(p->start+p->size-1), p->owner );
+					virt_to_phys(p->start),
+					virt_to_phys(p->start+p->size-1), p->owner );
 		if (p->flags & BLOCK_STATIC)
 			PRINT_PROC( "static)\n" );
 		else if (p->flags & BLOCK_GFP)
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/config.in linux/arch/m68k/config.in
--- v2.1.119/linux/arch/m68k/config.in	Tue Aug 18 22:02:02 1998
+++ linux/arch/m68k/config.in	Wed Sep  2 09:39:18 1998
@@ -63,9 +63,10 @@
 if [ "$CONFIG_VME" = "y" -a "$CONFIG_M68060" = "y" ]; then
   define_bool CONFIG_060_WRITETHROUGH y
 fi
-bool 'Advanced processor options' CONFIG_ADVANCED_CPU
-if [ "$CONFIG_ADVANCED_CPU" = "y" ]; then
+bool 'Advanced configuration options' CONFIG_ADVANCED
+if [ "$CONFIG_ADVANCED" = "y" ]; then
   bool 'Use read-modify-write instructions' CONFIG_RMW_INSNS
+  bool 'Use one physical chunk of memory only' CONFIG_SINGLE_MEMORY_CHUNK
   if [ "$CONFIG_M68060" = "y" -a "$CONFIG_VME" = "n" ]; then
     bool 'Use write-through caching for 68060 supervisor accesses' CONFIG_060_WRITETHROUGH
   fi
@@ -235,23 +236,14 @@
     tristate 'PAMsNet support' CONFIG_ATARI_PAMSNET
   fi
 fi
+if [ "$CONFIG_HP300" = "y" ]; then
+  bool 'HP on-board LANCE support' CONFIG_HPLANCE
 fi
-endmenu
-
 fi
+endmenu
 
-source fs/Config.in
-
-if [ "$CONFIG_VME" = "n" ]; then
-  define_bool CONFIG_FB y
-  mainmenu_option next_comment
-  comment 'Console drivers'
-  source drivers/video/Config.in
-  endmenu
 fi
 
-source fs/nls/Config.in
-
 mainmenu_option next_comment
 comment 'Character devices'
  
@@ -277,7 +269,7 @@
   tristate 'Atari mouse support' CONFIG_ATARIMOUSE
 fi
 if [ "$CONFIG_MAC" = "y" ]; then
-   bool 'Mac ADB mouse support' CONFIG_MACMOUSE
+   bool 'Mac ADB mouse support' CONFIG_ADBMOUSE
 fi
 if [ "$CONFIG_ATARI" = "y" ]; then
   tristate 'Atari MFP serial support' CONFIG_ATARI_MFPSER
@@ -344,6 +336,11 @@
 if [ "$CONFIG_ATARI" = "y" ]; then
   bool 'Enhanced Real Time Clock Support' CONFIG_RTC
 fi
+bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS
+if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then
+  int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256
+fi
+
 endmenu
 
 mainmenu_option next_comment
@@ -354,6 +351,22 @@
   dep_tristate 'Amiga or Atari DMA sound support' CONFIG_DMASOUND $CONFIG_SOUND
 fi
 endmenu
+
+source fs/Config.in
+
+if [ "$CONFIG_VME" = "n" ]; then
+  if [ "$CONFIG_HP300" = "y" ]; then
+    bool 'Frame buffer support' CONFIG_FB
+  else
+    define_bool CONFIG_FB y
+  fi
+  mainmenu_option next_comment
+  comment 'Console drivers'
+  source drivers/video/Config.in
+  endmenu
+fi
+
+source fs/nls/Config.in
 
 mainmenu_option next_comment
 comment 'Kernel hacking'
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/defconfig linux/arch/m68k/defconfig
--- v2.1.119/linux/arch/m68k/defconfig	Tue Aug 18 22:02:02 1998
+++ linux/arch/m68k/defconfig	Wed Sep  2 09:39:18 1998
@@ -30,7 +30,8 @@
 # CONFIG_M68060 is not set
 # CONFIG_OPTIMIZE_040 is not set
 # CONFIG_OPTIMIZE_060 is not set
-# CONFIG_ADVANCED_CPU is not set
+# CONFIG_ADVANCED is not set
+# CONFIG_SINGLE_MEMORY_CHUNK is not set
 # CONFIG_RMW_INSNS is not set
 
 #
@@ -135,6 +136,7 @@
 # CONFIG_A4000T_SCSI is not set
 # CONFIG_A4091_SCSI is not set
 # CONFIG_WARPENGINE_SCSI is not set
+# CONFIG_BLZ603EPLUS_SCSI is not set
 CONFIG_ATARI_SCSI=y
 # CONFIG_ATARI_SCSI_TOSHIBA_DELAY is not set
 # CONFIG_ATARI_SCSI_RESET_BOOT is not set
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/hp300/Makefile linux/arch/m68k/hp300/Makefile
--- v2.1.119/linux/arch/m68k/hp300/Makefile	Tue Jun 23 10:01:20 1998
+++ linux/arch/m68k/hp300/Makefile	Wed Sep  2 09:39:18 1998
@@ -8,6 +8,10 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := hp300.o
-O_OBJS	 := ksyms.o config.o ints.o time.o reboot.o hil.o
+O_OBJS	 := ksyms.o config.o ints.o time.o reboot.o
+
+ifdef CONFIG_VT
+O_OBJS += hil.o
+endif
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/hp300/config.c linux/arch/m68k/hp300/config.c
--- v2.1.119/linux/arch/m68k/hp300/config.c	Thu Jul 16 18:09:24 1998
+++ linux/arch/m68k/hp300/config.c	Wed Sep  2 09:39:18 1998
@@ -24,7 +24,17 @@
 #include "time.h"
 
 extern void hp300_reset(void);
-extern void hp300_hil_init(void);
+extern void (*hp300_default_handler[])(int, void *, struct pt_regs *);
+extern int hp300_get_irq_list(char *buf);
+
+#ifdef CONFIG_VT
+extern int hp300_keyb_init(void);
+#else
+/* Dummy function for when there is no keyboard. */
+__initfunc(int hp300_keyb_init(void))
+{
+}
+#endif
 
 #ifdef CONFIG_HEARTBEAT
 static void hp300_pulse(int x)
@@ -45,20 +55,30 @@
 {
 }
 
+/* for "kbd-reset" cmdline param */
+__initfunc(void hp300_kbd_reset_setup(char *str, int i))
+{
+}
+
+static void hp300_get_model(char *model)
+{
+  strcpy(model, "HP9000/300");
+}
+
 __initfunc(void config_hp300(void))
 {
   mach_sched_init      = hp300_sched_init;
-  mach_keyb_init       = hp300_hil_init;
+  mach_keyb_init       = hp300_keyb_init;
   mach_kbdrate         = hp300_kbdrate;
   mach_kbd_leds        = hp300_kbd_leds;
   mach_init_IRQ        = hp300_init_IRQ;
   mach_request_irq     = hp300_request_irq;
   mach_free_irq        = hp300_free_irq;
-#if 0
+  kbd_reset_setup      = hp300_kbd_reset_setup;
   mach_get_model       = hp300_get_model;
   mach_get_irq_list    = hp300_get_irq_list;
-#endif
   mach_gettimeoffset   = hp300_gettimeoffset;
+  mach_default_handler = &hp300_default_handler;
 #if 0
   mach_gettod          = hp300_gettod;
 #endif
@@ -70,9 +90,4 @@
   conswitchp	       = &dummy_con;
 #endif
   mach_max_dma_address = 0xffffffff;
-}
-
-/* for "kbd-reset" cmdline param */
-__initfunc(void kbd_reset_setup(char *str, int *ints))
-{
 }
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/hp300/hil.c linux/arch/m68k/hp300/hil.c
--- v2.1.119/linux/arch/m68k/hp300/hil.c	Tue Jun 23 10:01:20 1998
+++ linux/arch/m68k/hp300/hil.c	Wed Sep  2 09:39:18 1998
@@ -54,63 +54,155 @@
 
 #define HIL_IRQ			1
 
-static u_short hp_plain_map[NR_KEYS] __initdata = {
-	0xf200, 0xf01b, 0xf20e, 0xf700, 0xf700, 0xf700, 0xf702, 0xf036,
-	0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf008, 0xf009,
-	0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69,
-	0xfb62, 0xfb76, 0xf063, 0xfb78, 0xfb7a, 0xf702, 0xfb61, 0xfb73,
-	0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b,
-	0xfb68, 0xfb67, 0xfb66, 0xfb64, 0xfb73, 0xfb61, 0xfb63, 0xfb76,
-	0xfb75, 0xfb79, 0xfb74, 0xfb72, 0xfb65, 0xfb77, 0xfb71, 0xf200,
-	0xf037, 0xf036, 0xf035, 0xf034, 0xf033, 0xf032, 0xf031, 0xf104,
-	0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf200, 0xf200, 0xf114,
-	0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
-	0xf600, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf038, 0xf039, 0xf030, 0xf200, 0xf200, 0xf008, 0xf200, 0xf200,
-	0xfb69, 0xfb6f, 0xfb70, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
-	0xfb6a, 0xfb6b, 0xfb6c, 0xf305, 0xf306, 0xf00d, 0xf302, 0xf303,
-	0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xfb6e, 0xf020, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
-};
-
-static u_short hp_shift_map[NR_KEYS] __initdata = {
-	0xf200, 0xf01b, 0xf20e, 0xf700, 0xf700, 0xf700, 0xf002, 0xf036,
-	0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf008, 0xf009,
-	0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69,
-	0xfb62, 0xfb76, 0xf063, 0xfb78, 0xfb7a, 0xf702, 0xfb61, 0xfb73,
-	0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b,
-	0xfb68, 0xfb67, 0xfb66, 0xfb64, 0xfb73, 0xfb61, 0xfb63, 0xfb76,
-	0xfb75, 0xfb79, 0xfb74, 0xfb72, 0xfb65, 0xfb77, 0xfb71, 0xf200,
-	0xf037, 0xf036, 0xf035, 0xf034, 0xf033, 0xf032, 0xf031, 0xf104,
-	0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf200, 0xf200, 0xf114,
-	0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
-	0xf600, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf038, 0xf039, 0xf030, 0xf200, 0xf200, 0xf008, 0xf200, 0xf200,
-	0xfb69, 0xfb6f, 0xfb70, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
-	0xfb6a, 0xfb6b, 0xfb6c, 0xf305, 0xf306, 0xf00d, 0xf302, 0xf303,
-	0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xfb6e, 0xf020, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
-};
-
-static u_short hp_ctrl_map[NR_KEYS] __initdata = {
-	0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e,
-	0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200,
-	0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009,
-	0xf00f, 0xf010, 0xf003, 0xf01d, 0xf201, 0xf702, 0xf001, 0xf013,
-	0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200,
-	0xf007, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016,
-	0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf07f, 0xf700, 0xf200,
-	0xf703, 0xf000, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104,
-	0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf200, 0xf200, 0xf114,
-	0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
-	0xf600, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf200, 0xf1ff, 0xf202, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
-	0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
-	0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
+#define plain_map		hp_plain_map
+#define shift_map		hp_shift_map
+#define altgr_map		hp_altgr_map
+#define ctrl_map		hp_ctrl_map
+#define shift_ctrl_map		hp_shift_ctrl_map
+#define alt_map			hp_alt_map
+#define ctrl_alt_map		hp_ctrl_alt_map
+
+u_short plain_map[NR_KEYS] = {
+	0xf200,	0xf200,	0xf703,	0xf703,	0xf700,	0xf700,	0xf702,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xfb62,	0xfb76,	0xfb63,	0xfb78,	0xfb7a,	0xf200,	0xf200,	0xf01b,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xfb68,	0xfb67,	0xfb66,	0xfb64,	0xfb73,	0xfb61,	0xf200,	0xf207,
+	0xfb75,	0xfb79,	0xfb74,	0xfb72,	0xfb65,	0xfb77,	0xfb71,	0xf009,
+	0xf037,	0xf036,	0xf035,	0xf034,	0xf033,	0xf032,	0xf031,	0xf060,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf103,	0xf102,	0xf101,	0xf100,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf104,	0xf105,	0xf106,	0xf107,	0xf200,	0xf200,	0xf200,
+	0xf038,	0xf039,	0xf030,	0xf02d,	0xf03d,	0xf008,	0xf200,	0xf200,
+	0xfb69,	0xfb6f,	0xfb70,	0xf05b,	0xf05d,	0xf05c,	0xf200,	0xf200,
+	0xfb6a,	0xfb6b,	0xfb6c,	0xf03b,	0xf027,	0xf201,	0xf200,	0xf200,
+	0xfb6d,	0xf02c,	0xf02e,	0xf02f,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xfb6e,	0xf020,	0xf200,	0xf200,	0xf601,	0xf600,	0xf603,	0xf602,
 };
 
+u_short shift_map[NR_KEYS] = {
+	0xf200,	0xf200,	0xf703,	0xf703,	0xf700,	0xf700,	0xf702,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xfb42,	0xfb56,	0xfb43,	0xfb58,	0xfb5a,	0xf200,	0xf200,	0xf07f,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xfb48,	0xfb47,	0xfb46,	0xfb44,	0xfb53,	0xfb41,	0xf200,	0xf207,
+	0xfb55,	0xfb59,	0xfb54,	0xfb52,	0xfb45,	0xfb57,	0xfb51,	0xf009,
+	0xf026,	0xf05e,	0xf025,	0xf024,	0xf023,	0xf040,	0xf021,	0xf07e,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf103,	0xf102,	0xf101,	0xf100,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf104,	0xf105,	0xf106,	0xf107,	0xf200,	0xf200,	0xf200,
+	0xf02a,	0xf028,	0xf029,	0xf05f,	0xf02b,	0xf200,	0xf200,	0xf200,
+	0xfb49,	0xfb4f,	0xfb50,	0xf07b,	0xf07d,	0xf07c,	0xf200,	0xf200,
+	0xfb4a,	0xfb4b,	0xfb4c,	0xf03a,	0xf022,	0xf201,	0xf200,	0xf200,
+	0xfb4d,	0xf03c,	0xf03e,	0xf03f,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xfb4e,	0xf020,	0xf200,	0xf200,	0xf601,	0xf600,	0xf603,	0xf602,
+};
+
+u_short altgr_map[NR_KEYS] = {
+	0xf200,	0xf200,	0xf703,	0xf703,	0xf700,	0xf700,	0xf702,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xfb62,	0xfb76,	0xfb63,	0xfb78,	0xfb7a,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xfb68,	0xfb67,	0xfb66,	0xfb64,	0xfb73,	0xfb61,	0xf200,	0xf207,
+	0xfb75,	0xfb79,	0xfb74,	0xfb72,	0xfb65,	0xfb77,	0xfb71,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf040,	0xf021,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf103,	0xf102,	0xf101,	0xf100,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf104,	0xf105,	0xf106,	0xf107,	0xf200,	0xf200,	0xf200,
+	0xf02a,	0xf05b,	0xf05d,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xfb69,	0xfb6f,	0xfb70,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xfb6a,	0xfb6b,	0xfb6c,	0xf200,	0xf200,	0xf201,	0xf200,	0xf200,
+	0xfb6d,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xfb6e,	0xf200,	0xf200,	0xf200,	0xf601,	0xf600,	0xf603,	0xf602,
+};
+
+u_short ctrl_map[NR_KEYS] = {
+	0xf200,	0xf200,	0xf703,	0xf703,	0xf700,	0xf700,	0xf702,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf002,	0xf016,	0xf003,	0xf018,	0xf01a,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf008,	0xf007,	0xf006,	0xf004,	0xf013,	0xf001,	0xf200,	0xf207,
+	0xf015,	0xf019,	0xf014,	0xf012,	0xf005,	0xf017,	0xf011,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf000,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf503,	0xf502,	0xf501,	0xf500,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf504,	0xf505,	0xf506,	0xf507,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf009,	0xf00f,	0xf010,	0xf200,	0xf200,	0xf01c,	0xf200,	0xf200,
+	0xf00a,	0xf00b,	0xf00c,	0xf200,	0xf007,	0xf201,	0xf200,	0xf200,
+	0xf00d,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf00e,	0xf200,	0xf200,	0xf200,	0xf601,	0xf600,	0xf603,	0xf602,
+};
+
+u_short shift_ctrl_map[NR_KEYS] = {
+	0xf200,	0xf200,	0xf703,	0xf703,	0xf700,	0xf700,	0xf702,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf002,	0xf016,	0xf003,	0xf018,	0xf01a,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf008,	0xf007,	0xf006,	0xf004,	0xf013,	0xf001,	0xf200,	0xf207,
+	0xf015,	0xf019,	0xf014,	0xf012,	0xf005,	0xf017,	0xf011,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf103,	0xf102,	0xf101,	0xf100,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf104,	0xf105,	0xf106,	0xf107,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf009,	0xf00f,	0xf010,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf00a,	0xf00b,	0xf00c,	0xf200,	0xf200,	0xf201,	0xf200,	0xf200,
+	0xf00d,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf00e,	0xf200,	0xf200,	0xf200,	0xf601,	0xf600,	0xf603,	0xf602,
+};
+
+u_short alt_map[NR_KEYS] = {
+	0xf200,	0xf200,	0xf703,	0xf703,	0xf700,	0xf700,	0xf702,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf862,	0xf876,	0xf863,	0xf878,	0xf87a,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf868,	0xf867,	0xf866,	0xf864,	0xf873,	0xf861,	0xf200,	0xf207,
+	0xf875,	0xf879,	0xf874,	0xf872,	0xf865,	0xf877,	0xf871,	0xf809,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf860,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf103,	0xf102,	0xf101,	0xf100,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf104,	0xf105,	0xf106,	0xf107,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf869,	0xf86f,	0xf870,	0xf200,	0xf200,	0xf85c,	0xf200,	0xf200,
+	0xf86a,	0xf86b,	0xf86c,	0xf83b,	0xf827,	0xf201,	0xf200,	0xf200,
+	0xf86d,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf86e,	0xf200,	0xf200,	0xf200,	0xf601,	0xf600,	0xf603,	0xf602,
+};
+
+u_short ctrl_alt_map[NR_KEYS] = {
+	0xf200,	0xf200,	0xf703,	0xf703,	0xf700,	0xf700,	0xf702,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf802,	0xf816,	0xf803,	0xf818,	0xf81a,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf808,	0xf807,	0xf806,	0xf804,	0xf813,	0xf801,	0xf200,	0xf207,
+	0xf815,	0xf819,	0xf814,	0xf812,	0xf805,	0xf817,	0xf811,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf103,	0xf102,	0xf101,	0xf100,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf104,	0xf105,	0xf106,	0xf107,	0xf200,	0xf200,	0xf200,
+	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf809,	0xf80f,	0xf810,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf80a,	0xf80b,	0xf80c,	0xf200,	0xf200,	0xf201,	0xf200,	0xf200,
+	0xf80d,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,	0xf200,
+	0xf80e,	0xf200,	0xf200,	0xf200,	0xf601,	0xf600,	0xf603,	0xf602,
+};
+
+#undef plain_map
+#undef ctrl_alt_map
+#undef shift_map
+#undef altgr_map
+#undef ctrl_map
+#undef shift_ctrl_map
+#undef alt_map
+
 struct {
   unsigned char s, c;
   int valid;
@@ -211,7 +303,7 @@
  * Initialise HIL. 
  */
 
-__initfunc(void hp300_hil_init(void))
+__initfunc(int hp300_keyb_init(void))
 {
   unsigned char s, c, kbid;
   unsigned int n = 0;
@@ -221,7 +313,7 @@
   memcpy(key_maps[4], hp_ctrl_map, sizeof(plain_map));
 
   if (!hwreg_present((void *)(HILBASE + HIL_DATA)))
-    return;		/* maybe this can happen */
+    return 1;		/* maybe this can happen */
 
   request_irq(HIL_IRQ, hil_interrupt, 0, "HIL", NULL);
 
@@ -231,20 +323,21 @@
   /* Look for keyboards */
   hil_do(HIL_READKBDSADR, NULL, 0);
   while (!hil_last.valid) {
-    if (n++ > 1000) {
+    if (n++ > 100000) {
       printk("HIL: timed out, assuming no keyboard present.\n");
-      return;
+      return 1;
     }
     mb();
   }
   hil_getlast(s, c);
   if (c == 0) {
     printk("HIL: no keyboard present.\n");
-    return;
+    return 1;
   }
   for (kbid = 0; (kbid < 8) && ((c & (1<<kbid)) == 0); kbid++);
   printk("HIL: keyboard found at id %d\n", kbid);
   /* set it to raw mode */
   c = 0;
   hil_do(HIL_WRITEKBDSADR, &c, 1);
+  return 0;
 }
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/hp300/hp300map.map linux/arch/m68k/hp300/hp300map.map
--- v2.1.119/linux/arch/m68k/hp300/hp300map.map	Wed Dec 31 16:00:00 1969
+++ linux/arch/m68k/hp300/hp300map.map	Wed Sep  2 09:39:18 1998
@@ -0,0 +1,252 @@
+# HP300 kernel keymap. This uses 7 modifier combinations.
+keymaps 0-2,4-5,8,12
+# Change the above line into
+#	keymaps 0-2,4-6,8,12
+# in case you want the entries
+#	altgr   control keycode  83 = Boot            
+#	altgr   control keycode 111 = Boot            
+# below.
+#
+# In fact AltGr is used very little, and one more keymap can
+# be saved by mapping AltGr to Alt (and adapting a few entries):
+# keycode 100 = Alt
+#
+keycode   1 = 
+keycode   2 = Alt
+keycode   3 = Alt
+keycode   4 = Shift
+keycode   5 = Shift
+keycode   6 = Control
+keycode   7 = 
+keycode   8 = 
+keycode   9 = 
+keycode  10 = 
+keycode  11 = 
+keycode  12 = 
+keycode  13 = 
+keycode  14 =
+keycode  15 = 
+keycode  16 =
+keycode  17 =
+keycode  18 =
+keycode  19 =
+keycode  20 =
+keycode  21 =
+keycode  22 =
+keycode  23 =
+keycode  24 = b               
+keycode  25 = v               
+keycode  26 = c
+keycode  27 = x
+keycode  28 = z
+keycode  29 = 
+keycode  30 =
+keycode  31 = Escape		Delete
+keycode  32 =
+keycode  33 =
+keycode  34 =
+keycode  35 =
+keycode  36 =
+keycode  37 =
+keycode  38 =
+keycode  39 =
+keycode  40 = h
+keycode  41 = g
+keycode  42 = f
+keycode  43 = d
+keycode  44 = s
+keycode  45 = a               
+keycode  46 =
+keycode  47 = Caps_Lock
+keycode  48 = u
+keycode  49 = y
+keycode  50 = t
+keycode  51 = r
+keycode  52 = e
+keycode  53 = w
+keycode  54 = q
+keycode  55 = Tab		Tab             
+	alt     keycode    55 = Meta_Tab        
+keycode  56 = seven		ampersand
+keycode  57 = six		asciicircum
+keycode  58 = five		percent
+keycode  59 = four		dollar
+keycode  60 = three		numbersign
+keycode  61 = two		at		at
+keycode  62 = one		exclam		exclam
+keycode  63 = grave		asciitilde      
+	control keycode    63 = nul             
+	alt     keycode    63 = Meta_grave      
+keycode  64 =        
+keycode  65 = 
+keycode  66 = 
+keycode  67 = 
+keycode  68 = 
+keycode  69 = 
+keycode  70 = 
+keycode  71 = 
+keycode  72 =
+keycode  73 = F4       
+	control keycode	   73 = Console_4
+keycode  74 = F3
+	control keycode	   74 = Console_3
+keycode  75 = F2
+	control keycode	   75 = Console_2
+keycode  76 = F1
+	control keycode	   76 = Console_1
+keycode  77 =
+keycode  78 =
+keycode  79 =
+keycode  80 =
+keycode  81 = F5
+	control keycode	   81 = Console_5
+keycode  82 = F6
+	control keycode	   82 = Console_6
+keycode  83 = F7
+	control keycode	   83 = Console_7
+keycode  84 = F8
+	control keycode	   84 = Console_8
+keycode  85 =
+keycode  86 = 
+keycode  87 = 
+keycode  88 = eight		asterisk	asterisk
+keycode  89 = nine		parenleft	bracketleft
+keycode  90 = zero		parenright	bracketright
+keycode  91 = minus		underscore
+keycode  92 = equal		plus
+keycode  93 = BackSpace
+keycode  94 =
+keycode  95 =
+keycode  96 = i
+keycode  97 = o
+keycode  98 = p
+keycode  99 = bracketleft	braceleft
+keycode 100 = bracketright	braceright
+keycode 101 = backslash		bar             
+	control keycode   101 = Control_backslash
+	alt     keycode   101 = Meta_backslash  
+keycode 102 = 
+keycode 103 = 
+keycode 104 = j
+keycode 105 = k
+keycode 106 = l
+keycode 107 = semicolon		colon
+	alt     keycode   107 = Meta_semicolon  
+keycode 108 = apostrophe	quotedbl        
+	control keycode   108 = Control_g       
+	alt     keycode   108 = Meta_apostrophe 
+keycode 109 = Return
+keycode 110 = 
+keycode 111 = 
+keycode 112 = m
+keycode 113 = comma		less
+keycode 114 = period		greater
+keycode 115 = slash		question
+keycode 116 = 
+keycode 117 = 
+keycode 118 = 
+keycode 119 = 
+keycode 120 = n
+keycode 121 = space		space
+keycode 122 =
+keycode 123 =
+keycode 124 = Left
+keycode 125 = Down
+keycode 126 = Up
+keycode 127 = Right
+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.1.119/linux/arch/m68k/hp300/ints.c linux/arch/m68k/hp300/ints.c
--- v2.1.119/linux/arch/m68k/hp300/ints.c	Wed Jul  1 19:38:53 1998
+++ linux/arch/m68k/hp300/ints.c	Wed Sep  2 09:39:18 1998
@@ -3,12 +3,13 @@
  *
  *  Copyright (C) 1998 Philip Blundell <philb@gnu.org>
  *
- *  This file contains the HP300-specific interrupt handling.  There
- *  isn't much here -- we only use the autovector interrupts and at the
- *  moment everything difficult is handled by the generic code.
+ *  This file contains the HP300-specific interrupt handling.
+ *  We only use the autovector interrupts, and therefore we need to
+ *  maintain lists of devices sharing each ipl.
+ *  [ipl list code added by Peter Maydell <pmaydell@chiark.greenend.org.uk> 06/1998]
  */
 
-#include <asm/ptrace.h>
+#include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/init.h>
 #include <linux/sched.h>
@@ -19,29 +20,140 @@
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/traps.h>
+#include <asm/ptrace.h>
+#include <asm/spinlock.h>
 #include "ints.h"
 
-static void hp300_nmi_handler(int irq, void *dev_id, struct pt_regs *fp)
+/* Each ipl has a linked list of interrupt service routines.
+ * Service routines are added via hp300_request_irq() and removed
+ * via hp300_free_irq(). The device driver should set IRQ_FLG_FAST
+ * if it needs to be serviced early (eg FIFOless UARTs); this will
+ * cause it to be added at the front of the queue rather than 
+ * the back.
+ * Currently IRQ_FLG_SLOW and flags=0 are treated identically; if
+ * we needed three levels of priority we could distinguish them
+ * but this strikes me as mildly ugly...
+ */
+
+/* we start with no entries in any list */
+static irq_node_t *hp300_irq_list[HP300_NUM_IRQS] = { [0 ... HP300_NUM_IRQS-1] = NULL };
+
+static spinlock_t irqlist_lock;
+
+/* This handler receives all interrupts, dispatching them to the registered handlers */
+static void hp300_int_handler(int irq, void *dev_id, struct pt_regs *fp)
 {
-  extern void hp300_reset(void);
-  printk("RESET pressed - self destruct sequence initiated.\n");
-  hp300_reset();
+        irq_node_t *t;
+        /* We just give every handler on the chain an opportunity to handle
+         * the interrupt, in priority order.
+         */
+        for(t = hp300_irq_list[irq]; t; t=t->next)
+                t->handler(irq, t->dev_id, fp);
+        /* We could put in some accounting routines, checks for stray interrupts,
+         * etc, in here. Note that currently we can't tell whether or not
+         * a handler handles the interrupt, though. 
+         */
 }
 
+void (*hp300_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = {
+	hp300_int_handler, hp300_int_handler, hp300_int_handler, hp300_int_handler,
+	hp300_int_handler, hp300_int_handler, hp300_int_handler, NULL
+};
+
+/* dev_id had better be unique to each handler because it's the only way we have
+ * to distinguish handlers when removing them...
+ *
+ * It would be pretty easy to support IRQ_FLG_LOCK (handler is not replacable)
+ * and IRQ_FLG_REPLACE (handler replaces existing one with this dev_id)
+ * if we wanted to. IRQ_FLG_FAST is needed for devices where interrupt latency
+ * matters (eg the dreaded FIFOless UART...)
+ */
 int hp300_request_irq(unsigned int irq,
-		void (*handler) (int, void *, struct pt_regs *),
-		unsigned long flags, const char *devname, void *dev_id)
+                      void (*handler) (int, void *, struct pt_regs *),
+                      unsigned long flags, const char *devname, void *dev_id)
 {
-  return sys_request_irq(irq, handler, flags, devname, dev_id);
+        irq_node_t *t, *n = new_irq_node();
+        
+        if (!n)                                   /* oops, no free nodes */
+                return -ENOMEM;
+
+	spin_lock_irqsave(&irqlist_lock, flags);
+
+        if (!hp300_irq_list[irq]) {
+                /* no list yet */
+                hp300_irq_list[irq] = n;
+                n->next = NULL;
+        } else if (flags & IRQ_FLG_FAST) {
+                /* insert at head of list */
+                n->next = hp300_irq_list[irq];
+                hp300_irq_list[irq] = n;
+        } else {
+                /* insert at end of list */
+                for(t = hp300_irq_list[irq]; t->next; t = t->next)
+                        /* do nothing */;
+                n->next = NULL;
+                t->next = n;
+        }
+
+        /* Fill in n appropriately */
+        n->handler = handler;
+        n->flags = flags;
+        n->dev_id = dev_id;
+        n->devname = devname;
+	spin_unlock_irqrestore(&irqlist_lock, flags);
+	return 0;
 }
 
 void hp300_free_irq(unsigned int irq, void *dev_id)
 {
-  sys_free_irq(irq, dev_id);
+        irq_node_t *t;
+        unsigned long flags;
+
+        spin_lock_irqsave(&irqlist_lock, flags);
+        
+        t = hp300_irq_list[irq];
+        if (!t)                                   /* no handlers at all for that IRQ */
+        {
+                printk(KERN_ERR "hp300_free_irq: attempt to remove nonexistent handler for IRQ %d\n", irq);
+                spin_unlock_irqrestore(&irqlist_lock, flags);
+		return;
+        }
+        
+        if (t->dev_id == dev_id)
+        {                                         /* removing first handler on chain */
+                t->flags = IRQ_FLG_STD;           /* we probably don't really need these */
+                t->dev_id = NULL;
+                t->devname = NULL;
+                t->handler = NULL;                /* frees this irq_node_t */
+                hp300_irq_list[irq] = t->next;
+		spin_unlock_irqrestore(&irqlist_lock, flags);
+		return;
+        }
+        
+        /* OK, must be removing from middle of the chain */
+        
+        for (t = hp300_irq_list[irq]; t->next && t->next->dev_id != dev_id; t = t->next)
+                /* do nothing */;
+        if (!t->next)
+        {
+                printk(KERN_ERR "hp300_free_irq: attempt to remove nonexistent handler for IRQ %d\n", irq);
+		spin_unlock_irqrestore(&irqlist_lock, flags);
+		return;
+        }
+        /* remove the entry after t: */
+        t->next->flags = IRQ_FLG_STD;
+        t->next->dev_id = t->next->devname = t->next->handler = NULL;
+        t->next = t->next->next;
+        
+	spin_unlock_irqrestore(&irqlist_lock, flags);
+}
+
+int hp300_get_irq_list(char *buf)
+{
+	return 0;
 }
 
 __initfunc(void hp300_init_IRQ(void))
 {
-  /* IPL6 - NMI (keyboard reset) */
-  sys_request_irq(7, hp300_nmi_handler, IRQ_FLG_STD, "NMI", hp300_nmi_handler);
+	spin_lock_init(&irqlist_lock);
 }
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/hp300/ints.h linux/arch/m68k/hp300/ints.h
--- v2.1.119/linux/arch/m68k/hp300/ints.h	Tue Jun 23 10:01:20 1998
+++ linux/arch/m68k/hp300/ints.h	Wed Sep  2 09:39:18 1998
@@ -4,3 +4,6 @@
 extern int hp300_request_irq(unsigned int irq,
 		void (*handler) (int, void *, struct pt_regs *),
 		unsigned long flags, const char *devname, void *dev_id);
+
+/* number of interrupts, includes 0 (what's that?) */
+#define HP300_NUM_IRQS 8
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/hp300/time.c linux/arch/m68k/hp300/time.c
--- v2.1.119/linux/arch/m68k/hp300/time.c	Wed Jul  1 19:38:53 1998
+++ linux/arch/m68k/hp300/time.c	Wed Sep  2 09:39:18 1998
@@ -31,10 +31,10 @@
 #define	CLKMSB2		0x9
 #define	CLKMSB3		0xD
 
-unsigned long hp300_gettimeoffset (void)
-{
-	return 0L;
-}
+/* This is for machines which generate the exact clock. */
+#define USECS_PER_JIFFY (1000000/HZ)
+
+#define INTVAL ((10000 / 4) - 1)
 
 static void hp300_tick(int irq, void *dev_id, struct pt_regs *regs)
 {
@@ -45,18 +45,31 @@
   vector(irq, NULL, regs);
 }
 
-__initfunc(void hp300_sched_init(void (*vector)(int, void *, struct pt_regs *)))
+unsigned long hp300_gettimeoffset(void)
 {
-  unsigned int intval = (10000 / 4) - 1;
+  /* Read current timer 1 value */
+  unsigned char lsb, msb1, msb2;
+  unsigned short ticks;
+
+  msb1 = readb(CLOCKBASE + 5);
+  lsb = readb(CLOCKBASE + 7);
+  msb2 = readb(CLOCKBASE + 5);
+  if (msb1 != msb2)
+    /* A carry happened while we were reading.  Read it again */
+    lsb = readb(CLOCKBASE + 7);
+  ticks = INTVAL - ((msb2 << 8) | lsb);
+  return (USECS_PER_JIFFY * ticks) / INTVAL;
+}
 
+__initfunc(void hp300_sched_init(void (*vector)(int, void *, struct pt_regs *)))
+{
   writeb(0x1, CLOCKBASE + CLKCR2);		/* select CR1 */
   writeb(0x1, CLOCKBASE + CLKCR1);		/* reset */
 
-  asm volatile(" movpw %0,%1@(5)" : : "d" (intval), "a" (CLOCKBASE));
+  asm volatile(" movpw %0,%1@(5)" : : "d" (INTVAL), "a" (CLOCKBASE));
 
   sys_request_irq(6, hp300_tick, IRQ_FLG_STD, "timer tick", vector);
 
   writeb(0x1, CLOCKBASE + CLKCR2);		/* select CR1 */
   writeb(0x40, CLOCKBASE + CLKCR1);		/* enable irq */
 }
-
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/kernel/entry.S linux/arch/m68k/kernel/entry.S
--- v2.1.119/linux/arch/m68k/kernel/entry.S	Tue Aug 18 22:02:03 1998
+++ linux/arch/m68k/kernel/entry.S	Wed Sep  2 09:39:18 1998
@@ -418,7 +418,7 @@
 	.long SYMBOL_NAME(sys_lseek)
 	.long SYMBOL_NAME(sys_getpid)		/* 20 */
 	.long SYMBOL_NAME(sys_mount)
-	.long SYMBOL_NAME(sys_umount)
+	.long SYMBOL_NAME(sys_oldumount)
 	.long SYMBOL_NAME(sys_setuid)
 	.long SYMBOL_NAME(sys_getuid)
 	.long SYMBOL_NAME(sys_stime)		/* 25 */
@@ -448,7 +448,7 @@
 	.long SYMBOL_NAME(sys_geteuid)
 	.long SYMBOL_NAME(sys_getegid)		/* 50 */
 	.long SYMBOL_NAME(sys_acct)
-	.long SYMBOL_NAME(sys_ni_syscall)				/* old phys syscall holder */
+	.long SYMBOL_NAME(sys_umount)					/* recycled never used phys() */
 	.long SYMBOL_NAME(sys_ni_syscall)				/* old lock syscall holder */
 	.long SYMBOL_NAME(sys_ioctl)
 	.long SYMBOL_NAME(sys_fcntl)		/* 55 */
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/kernel/m68k_defs.h linux/arch/m68k/kernel/m68k_defs.h
--- v2.1.119/linux/arch/m68k/kernel/m68k_defs.h	Tue Aug 18 22:02:03 1998
+++ linux/arch/m68k/kernel/m68k_defs.h	Wed Sep  2 09:39:18 1998
@@ -3,6 +3,6 @@
  */
 
 #define TS_MAGICKEY	0x5a5a5a5a
-#define TS_TSS 478
-#define TS_ESP0 498
-#define TS_FPU 502
+#define TS_TSS 482
+#define TS_ESP0 502
+#define TS_FPU 506
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/kernel/process.c linux/arch/m68k/kernel/process.c
--- v2.1.119/linux/arch/m68k/kernel/process.c	Mon Aug  3 12:45:44 1998
+++ linux/arch/m68k/kernel/process.c	Wed Sep  2 09:39:18 1998
@@ -134,29 +134,20 @@
 
 asmlinkage int m68k_fork(struct pt_regs *regs)
 {
-	int ret;
-
-	lock_kernel();
-	ret = do_fork(SIGCHLD, rdusp(), regs);
-	unlock_kernel();
-	return ret;
+	return do_fork(SIGCHLD, rdusp(), regs);
 }
 
 asmlinkage int m68k_clone(struct pt_regs *regs)
 {
 	unsigned long clone_flags;
 	unsigned long newsp;
-	int ret;
 
-	lock_kernel();
 	/* syscall2 puts clone_flags in d1 and usp in d2 */
 	clone_flags = regs->d1;
 	newsp = regs->d2;
 	if (!newsp)
-	  newsp  = rdusp();
-	ret = do_fork(clone_flags, newsp, regs);
-	unlock_kernel();
-	return ret;
+		newsp = rdusp();
+	return do_fork(clone_flags, newsp, regs);
 }
 
 int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/kernel/setup.c linux/arch/m68k/kernel/setup.c
--- v2.1.119/linux/arch/m68k/kernel/setup.c	Mon Aug  3 12:45:44 1998
+++ linux/arch/m68k/kernel/setup.c	Wed Sep  2 09:39:18 1998
@@ -23,6 +23,7 @@
 #include <asm/bootinfo.h>
 #include <asm/setup.h>
 #include <asm/irq.h>
+#include <asm/io.h>
 #include <asm/machdep.h>
 #ifdef CONFIG_AMIGA
 #include <asm/amigahw.h>
@@ -123,7 +124,7 @@
 		/* Already set up by head.S */
 		break;
 
-	    case BI_MEMCHUNK:
+ 	    case BI_MEMCHUNK:
 		if (m68k_num_memory < NUM_MEMINFO) {
 		    m68k_memory[m68k_num_memory].addr = data[0];
 		    m68k_memory[m68k_num_memory].size = data[1];
@@ -157,6 +158,14 @@
 		   record->tag);
 	record = (struct bi_record *)((u_long)record+record->size);
     }
+
+#ifdef CONFIG_SINGLE_MEMORY_CHUNK
+    if (m68k_num_memory > 1) {
+	printk("Ignoring last %i chunks of physical memory\n",
+	       (m68k_num_memory - 1));
+	m68k_num_memory = 1;
+    }
+#endif
 }
 
 __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p,
@@ -270,7 +279,7 @@
 
 #ifdef CONFIG_BLK_DEV_INITRD
 	if (m68k_ramdisk.size) {
-		initrd_start = PTOV (m68k_ramdisk.addr);
+		initrd_start = (unsigned long)phys_to_virt(m68k_ramdisk.addr);
 		initrd_end = initrd_start + m68k_ramdisk.size;
 	}
 #endif
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/kernel/signal.c linux/arch/m68k/kernel/signal.c
--- v2.1.119/linux/arch/m68k/kernel/signal.c	Mon Aug  3 12:45:44 1998
+++ linux/arch/m68k/kernel/signal.c	Wed Sep  2 09:39:18 1998
@@ -808,16 +808,11 @@
 
 	push_cache ((unsigned long) &frame->retcode);
 
-	/*
-	 * no matter what frame format we were using before, we
-	 * will do the "RTE" using a normal 4 word frame.
-	 */
-	regs->format = 0;
-
 	/* Set up registers for signal handler */
 	wrusp ((unsigned long) frame);
 	regs->pc = (unsigned long) ka->sa.sa_handler;
 
+adjust_stack:
 	/* Prepare to skip over the extra stuff in the exception frame.  */
 	if (regs->stkadj) {
 		struct pt_regs *tregs =
@@ -827,8 +822,8 @@
 #endif
 		/* This must be copied with decreasing addresses to
                    handle overlaps.  */
-		tregs->vector = regs->vector;
-		tregs->format = regs->format;
+		tregs->vector = 0;
+		tregs->format = 0;
 		tregs->pc = regs->pc;
 		tregs->sr = regs->sr;
 	}
@@ -838,6 +833,7 @@
 	if (sig == SIGSEGV)
 		ka->sa.sa_handler = SIG_DFL;
 	force_sig(SIGSEGV, current);
+	goto adjust_stack;
 }
 
 static void setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info,
@@ -895,16 +891,11 @@
 
 	push_cache ((unsigned long) &frame->retcode);
 
-	/*
-	 * no matter what frame format we were using before, we
-	 * will do the "RTE" using a normal 4 word frame.
-	 */
-	regs->format = 0;
-
 	/* Set up registers for signal handler */
 	wrusp ((unsigned long) frame);
 	regs->pc = (unsigned long) ka->sa.sa_handler;
 
+adjust_stack:
 	/* Prepare to skip over the extra stuff in the exception frame.  */
 	if (regs->stkadj) {
 		struct pt_regs *tregs =
@@ -914,8 +905,8 @@
 #endif
 		/* This must be copied with decreasing addresses to
                    handle overlaps.  */
-		tregs->vector = regs->vector;
-		tregs->format = regs->format;
+		tregs->vector = 0;
+		tregs->format = 0;
 		tregs->pc = regs->pc;
 		tregs->sr = regs->sr;
 	}
@@ -925,6 +916,7 @@
 	if (sig == SIGSEGV)
 		ka->sa.sa_handler = SIG_DFL;
 	force_sig(SIGSEGV, current);
+	goto adjust_stack;
 }
 
 static inline void
@@ -970,7 +962,7 @@
 		setup_frame(sig, ka, oldset, regs);
 
 	if (ka->sa.sa_flags & SA_ONESHOT)
-		ka->sa.sa_handler = NULL;
+		ka->sa.sa_handler = SIG_DFL;
 
 	if (!(ka->sa.sa_flags & SA_NODEFER)) {
 		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
@@ -1030,10 +1022,8 @@
 			discard_frame:
 			    /* Make sure that a faulted bus cycle isn't
 			       restarted (only needed on the 680[23]0).  */
-			    if (regs->format == 10 || regs->format == 11) {
+			    if (regs->format == 10 || regs->format == 11)
 				regs->stkadj = frame_extra_sizes[regs->format];
-				regs->format = 0;
-			    }
 			    continue;
 			}
 			current->exit_code = 0;
@@ -1126,8 +1116,8 @@
 
 		/* This must be copied with decreasing addresses to
 		   handle overlaps.  */
-		tregs->vector = regs->vector;
-		tregs->format = regs->format;
+		tregs->vector = 0;
+		tregs->format = 0;
 		tregs->pc = regs->pc;
 		tregs->sr = regs->sr;
 	}
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/lib/checksum.c linux/arch/m68k/lib/checksum.c
--- v2.1.119/linux/arch/m68k/lib/checksum.c	Wed Apr 23 19:01:15 1997
+++ linux/arch/m68k/lib/checksum.c	Wed Sep  2 09:39:18 1998
@@ -26,6 +26,10 @@
  *		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.
+ *
+ * 1998/8/31	Andreas Schwab:
+ *		Zero out rest of buffer on exception in
+ *		csum_partial_copy_from_user.
  */
 
 #include <net/checksum.h>
@@ -239,23 +243,71 @@
 	     "8:\n"
 		".section .fixup,\"ax\"\n"
 		".even\n"
+		/* If any execption occurs zero out the rest.
+		   Similarities with the code above are intentional :-) */
+	     "90:\t"
+		"clrw %3@+\n\t"
+		"movel %1,%4\n\t"
+		"lsrl #5,%1\n\t"
+		"jeq 1f\n\t"
+		"subql #1,%1\n"
+	     "91:\t"
+		"clrl %3@+\n"
+	     "92:\t"
+		"clrl %3@+\n"
+	     "93:\t"
+		"clrl %3@+\n"
+	     "94:\t"
+		"clrl %3@+\n"
+	     "95:\t"
+		"clrl %3@+\n"
+	     "96:\t"
+		"clrl %3@+\n"
+	     "97:\t"
+		"clrl %3@+\n"
+	     "98:\t"
+		"clrl %3@+\n\t"
+		"dbra %1,91b\n\t"
+		"clrw %1\n\t"
+		"subql #1,%1\n\t"
+		"jcc 91b\n"
+	     "1:\t"
+		"movel %4,%1\n\t"
+		"andw #0x1c,%4\n\t"
+		"jeq 1f\n\t"
+		"lsrw #2,%4\n\t"
+		"subqw #1,%4\n"
+	     "99:\t"
+		"clrl %3@+\n\t"
+		"dbra %4,99b\n\t"
+	     "1:\t"
+		"andw #3,%1\n\t"
+		"jeq 9f\n"
+	     "100:\t"
+		"clrw %3@+\n\t"
+		"tstw %1\n\t"
+		"jeq 9f\n"
+	     "101:\t"
+		"clrb %3@+\n"
 	     "9:\t"
-		"moveq #-14,%5\n\t"	/* -EFAULT, out of inputs to asm ;( */
+#define STR(X) STR1(X)
+#define STR1(X) #X
+		"moveq #-" STR(EFAULT) ",%5\n\t"
 		"jra 8b\n"
 		".previous\n"
 		".section __ex_table,\"a\"\n"
-		".long 10b,9b\n"
-		".long 11b,9b\n"
-		".long 12b,9b\n"
-		".long 13b,9b\n"
-		".long 14b,9b\n"
-		".long 15b,9b\n"
-		".long 16b,9b\n"
-		".long 17b,9b\n"
-		".long 18b,9b\n"
-		".long 19b,9b\n"
-		".long 20b,9b\n"
-		".long 21b,9b\n"
+		".long 10b,90b\n"
+		".long 11b,91b\n"
+		".long 12b,92b\n"
+		".long 13b,93b\n"
+		".long 14b,94b\n"
+		".long 15b,95b\n"
+		".long 16b,96b\n"
+		".long 17b,97b\n"
+		".long 18b,98b\n"
+		".long 19b,99b\n"
+		".long 20b,100b\n"
+		".long 21b,101b\n"
 		".previous"
 		: "=d" (sum), "=d" (len), "=a" (src), "=a" (dst),
 		  "=&d" (tmp1), "=d" (tmp2)
@@ -267,16 +319,6 @@
 	return(sum);
 }
 
-/*
- * This one will go away soon.
- */
-unsigned int
-csum_partial_copy_fromuser(const char *src, char *dst, int len, int sum)
-{
-	int dummy;
-
-	return csum_partial_copy_from_user(src, dst, len, sum, &dummy);
-}
 /*
  * copy from kernel space while checksumming, otherwise like csum_partial
  */
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/mac/mac_ksyms.c linux/arch/m68k/mac/mac_ksyms.c
--- v2.1.119/linux/arch/m68k/mac/mac_ksyms.c	Tue Jun 23 10:01:21 1998
+++ linux/arch/m68k/mac/mac_ksyms.c	Wed Sep  2 09:39:18 1998
@@ -2,6 +2,6 @@
 #include <asm/ptrace.h>
 #include <asm/traps.h>
 /* Hook for mouse driver */
-extern void (*mac_mouse_interrupt_hook) (char *);
+extern void (*adb_mouse_interrupt_hook) (char *);
 
-EXPORT_SYMBOL(mac_mouse_interrupt_hook);
+EXPORT_SYMBOL(adb_mouse_interrupt_hook);
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/mac/mackeyb.c linux/arch/m68k/mac/mackeyb.c
--- v2.1.119/linux/arch/m68k/mac/mackeyb.c	Tue Aug 18 22:02:03 1998
+++ linux/arch/m68k/mac/mackeyb.c	Wed Sep  2 09:39:18 1998
@@ -59,9 +59,7 @@
 static void keyboard_input(unsigned char *, int, struct pt_regs *);
 static void mouse_input(unsigned char *, int, struct pt_regs *);
 /* Hook for mouse driver */
-void (*mac_mouse_interrupt_hook) (char *);
-int mac_emulate_button2;
-int mac_emulate_button3;
+void (*adb_mouse_interrupt_hook) (unsigned char *, int);
 /* The mouse driver - for debugging */
 extern void mac_mouse_interrupt(char *);
 /* end keyb */
@@ -290,11 +288,11 @@
 	 *	(wanted: command and alt/option, or KP= and KP( ...)
 	 *	Debug version; might be rewritten to be faster on normal keys.
 	 */
-	if (mac_mouse_interrupt_hook || console_loglevel >= 8) {
+	if (adb_mouse_interrupt_hook || console_loglevel >= 8) {
 		unsigned char button, button2, button3, fake_event;
 		static unsigned char button2state=0, button3state=0; /* up */
-		/* faked ADB packet: device type ff, handler 4 ! */
-		static char data[6] = { 0xff, 0x40, 0x3c, 0x80, 0x80, 0x80 };
+		/* faked ADB packet */
+		static unsigned char data[4] = { 0, 0x80, 0x80, 0x80 };
 
 		button = 0;
 		fake_event = 0;
@@ -321,16 +319,16 @@
 #endif
 		if (button) {		/* there's been a button state change */
 			/* fake a mouse packet : send all bytes, change one! */
-			data[button+2] = (up_flag ? 0x80 : 0);
-			if (mac_mouse_interrupt_hook)
-				mac_mouse_interrupt_hook(data);
+			data[button] = (up_flag ? 0x80 : 0);
+			if (adb_mouse_interrupt_hook)
+				adb_mouse_interrupt_hook(data, 4);
 #ifdef DEBUG_ADBMOUSE
 			else
 				printk("mouse_fake: data %2x %2x %2x buttons %2x \n", 
-					data[3], data[4], data[5],
-					~( (data[3] & 0x80 ? 0 : 4) 
-					 | (data[4] & 0x80 ? 0 : 1) 
-					 | (data[5] & 0x80 ? 0 : 2) )&7 );
+					data[1], data[2], data[3],
+					~( (data[1] & 0x80 ? 0 : 4) 
+					 | (data[2] & 0x80 ? 0 : 1) 
+					 | (data[3] & 0x80 ? 0 : 2) )&7 );
 #endif
 		}
 		/*
@@ -485,8 +483,8 @@
 		return;
 	}
 
-	if (mac_mouse_interrupt_hook) {
-		mac_mouse_interrupt_hook(data);
+	if (adb_mouse_interrupt_hook) {
+		adb_mouse_interrupt_hook(data+2, nb-2);
 		/*
 		 * passing the mouse data to i.e. the X server as done for
 		 * Xpmac will confuse applications on a sane X server :-)
@@ -609,10 +607,7 @@
 	memcpy(key_maps[12], mac_ctrl_alt_map, sizeof(plain_map));
 
 	/* initialize mouse interrupt hook */
-	mac_mouse_interrupt_hook = NULL;
-	/* assume broken mouse :-) */
-	mac_emulate_button2 = 1;
-	mac_emulate_button3 = 1;
+	adb_mouse_interrupt_hook = NULL;
 
 	/*
 	 * Might put that someplace else, possibly ....
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/mm/init.c linux/arch/m68k/mm/init.c
--- v2.1.119/linux/arch/m68k/mm/init.c	Thu Aug  6 14:06:28 1998
+++ linux/arch/m68k/mm/init.c	Wed Sep  2 09:39:18 1998
@@ -23,6 +23,7 @@
 #include <asm/pgtable.h>
 #include <asm/system.h>
 #include <asm/machdep.h>
+#include <asm/io.h>
 #ifdef CONFIG_ATARI
 #include <asm/atari_stram.h>
 #endif
@@ -238,7 +239,7 @@
 				ktablep = kernel_page_table (memavailp);
 			}
 
-			ktable = VTOP(ktablep);
+			ktable = virt_to_phys(ktablep);
 
 			/*
 			 * initialize section of the page table mapping
@@ -278,7 +279,7 @@
 				
 				tbl = (unsigned long *)get_kpointer_table();
 
-				kpointerp[pindex++] = VTOP(tbl) | _PAGE_TABLE |_PAGE_ACCESSED;
+				kpointerp[pindex++] = virt_to_phys(tbl) | _PAGE_TABLE |_PAGE_ACCESSED;
 
 				for (i = 0; i < 64; i++, physaddr += PAGE_SIZE)
 					tbl[i] = physaddr | _PAGE_PRESENT | _PAGE_ACCESSED;
@@ -289,7 +290,7 @@
 				/* not the first 256K */
 				kpointerp[pindex++] = physaddr | _PAGE_PRESENT | _PAGE_ACCESSED;
 #ifdef DEBUG
-				printk ("%lx=%lx ", VTOP(&kpointerp[pindex-1]),
+				printk ("%lx=%lx ", virt_to_phys(&kpointerp[pindex-1]),
 					kpointerp[pindex-1]);
 #endif
 				physaddr += 64 * PAGE_SIZE;
@@ -398,7 +399,7 @@
 
 	/* setup CPU root pointer for swapper task */
 	task[0]->tss.crp[0] = 0x80000000 | _PAGE_TABLE;
-	task[0]->tss.crp[1] = VTOP (swapper_pg_dir);
+	task[0]->tss.crp[1] = virt_to_phys (swapper_pg_dir);
 
 #ifdef DEBUG
 	printk ("task 0 pagedir at %p virt, %#lx phys\n",
@@ -456,7 +457,7 @@
 #endif
 
 	for (tmp = 0 ; tmp < end_mem ; tmp += PAGE_SIZE) {
-		if (VTOP (tmp) >= mach_max_dma_address)
+		if (virt_to_phys ((void *)tmp) >= mach_max_dma_address)
 			clear_bit(PG_DMA, &mem_map[MAP_NR(tmp)].flags);
 		if (PageReserved(mem_map+MAP_NR(tmp))) {
 			if (tmp >= (unsigned long)&_text
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/mm/kmap.c linux/arch/m68k/mm/kmap.c
--- v2.1.119/linux/arch/m68k/mm/kmap.c	Mon Aug  3 12:45:44 1998
+++ linux/arch/m68k/mm/kmap.c	Wed Sep  2 09:39:18 1998
@@ -277,8 +277,11 @@
 	size = (size + KMAP_STEP - 1) & ~(KMAP_STEP-1);
 	
 	down( &kmap_sem );
-	if (!(kmap = kmap_get_region( size, memavailp == NULL )))
-		return( 0 );
+	kmap = kmap_get_region(size, memavailp == NULL);
+	if (!kmap) {
+		up(&kmap_sem);
+		return 0;
+	}
 	from = kmap->addr;
 	retaddr += from;
 	kmap->mapaddr = retaddr;
diff -u --recursive --new-file v2.1.119/linux/arch/m68k/mm/memory.c linux/arch/m68k/mm/memory.c
--- v2.1.119/linux/arch/m68k/mm/memory.c	Tue Aug 18 22:02:03 1998
+++ linux/arch/m68k/mm/memory.c	Wed Sep  2 09:39:18 1998
@@ -17,6 +17,7 @@
 #include <asm/pgtable.h>
 #include <asm/system.h>
 #include <asm/traps.h>
+#include <asm/io.h>
 #ifdef CONFIG_AMIGA
 #include <asm/amigahw.h>
 #endif
@@ -299,14 +300,14 @@
     return( (vaddr & mask) == (base & mask) );
 }
 
-static unsigned long mm_vtop_fallback (unsigned long);
-
+#ifndef CONFIG_SINGLE_MEMORY_CHUNK
 /*
  * The following two routines map from a physical address to a kernel
  * virtual address and vice versa.
  */
 unsigned long mm_vtop (unsigned long vaddr)
 {
+#ifndef CONFIG_SINGLE_MEMORY_CHUNK
 	int i=0;
 	unsigned long voff = vaddr;
 	unsigned long offset = 0;
@@ -322,12 +323,18 @@
 			offset += m68k_memory[i].size;
 		i++;
 	}while (i < m68k_num_memory);
+#else
+	if (vaddr < m68k_memory[0].size)
+		return m68k_memory[0].addr + vaddr;
+#endif
+
 	return mm_vtop_fallback(vaddr);
 }
+#endif
 
 /* Separate function to make the common case faster (needs to save less
    registers) */
-static unsigned long mm_vtop_fallback (unsigned long vaddr)
+unsigned long mm_vtop_fallback (unsigned long vaddr)
 {
 	/* not in one of the memory chunks; test for applying transparent
 	 * translation */
@@ -420,7 +427,7 @@
 	  if (mmusr & (MMU_I|MMU_B|MMU_L))
 	    panic ("VTOP030: bad virtual address %08lx (%x)", vaddr, mmusr);
 
-	  descaddr = (unsigned long *)PTOV(descaddr);
+	  descaddr = phys_to_virt((unsigned long)descaddr);
 
 	  switch (mmusr & MMU_NUM) {
 	  case 1:
@@ -438,8 +445,10 @@
 	panic ("VTOP: bad virtual address %08lx", vaddr);
 }
 
+#ifndef CONFIG_SINGLE_MEMORY_CHUNK
 unsigned long mm_ptov (unsigned long paddr)
 {
+#ifndef CONFIG_SINGLE_MEMORY_CHUNK
 	int i = 0;
 	unsigned long offset = 0;
 
@@ -456,6 +465,11 @@
 			offset += m68k_memory[i].size;
 		i++;
 	}while (i < m68k_num_memory);
+#else
+	unsigned long base = m68k_memory[0].addr;
+	if (paddr >= base && paddr < (base + m68k_memory[0].size))
+		return (paddr - base);
+#endif
 
 	/*
 	 * assume that the kernel virtual address is the same as the
@@ -482,6 +496,7 @@
 #endif
 	return paddr;
 }
+#endif
 
 /* invalidate page in both caches */
 #define	clear040(paddr)					\
@@ -635,6 +650,7 @@
 #undef pushcl040
 #undef pushcli040
 
+#ifndef CONFIG_SINGLE_MEMORY_CHUNK
 int mm_end_of_chunk (unsigned long addr, int len)
 {
 	int i;
@@ -644,4 +660,4 @@
 			return 1;
 	return 0;
 }
-
+#endif
diff -u --recursive --new-file v2.1.119/linux/drivers/block/ide-pmac.c linux/drivers/block/ide-pmac.c
--- v2.1.119/linux/drivers/block/ide-pmac.c	Thu Aug 27 19:56:29 1998
+++ linux/drivers/block/ide-pmac.c	Sun Aug 30 11:27:35 1998
@@ -38,8 +38,12 @@
 static int pmac_ide_build_dmatable(ide_drive_t *drive, int wr);
 #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
 
-__initfunc(void
-pmac_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq))
+/*
+ * N.B. this can't be an initfunc, because the media-bay task can
+ * call ide_[un]register at any time.
+ */
+void
+pmac_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq)
 {
 	int i;
 
diff -u --recursive --new-file v2.1.119/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.1.119/linux/drivers/block/ide.c	Thu Aug 27 19:56:29 1998
+++ linux/drivers/block/ide.c	Sun Aug 30 11:27:35 1998
@@ -1657,7 +1657,7 @@
 	ide_drive_t *drive, *d;
 	ide_hwif_t *hwif, *g;
 	ide_hwgroup_t *hwgroup;
-	int irq_count = 0, unit;
+	int irq_count = 0, unit, i;
 	unsigned long flags;
 
 	if (index >= MAX_HWIFS)
@@ -1704,8 +1704,8 @@
 	 * the hwgroup if we were the only member
 	 */
 	d = hwgroup->drive;
-	for (index = 0; index < MAX_DRIVES; ++index) {
-		drive = &hwif->drives[index];
+	for (i = 0; i < MAX_DRIVES; ++i) {
+		drive = &hwif->drives[i];
 		if (!drive->present)
 			continue;
 		while (hwgroup->drive->next != drive)
diff -u --recursive --new-file v2.1.119/linux/drivers/block/nbd.c linux/drivers/block/nbd.c
--- v2.1.119/linux/drivers/block/nbd.c	Thu Aug 27 19:56:29 1998
+++ linux/drivers/block/nbd.c	Fri Aug 28 10:38:32 1998
@@ -61,13 +61,20 @@
 static int nbd_open(struct inode *inode, struct file *file)
 {
 	int dev;
+	struct nbd_device *nbdev;
 
 	if (!inode)
 		return -EINVAL;
 	dev = MINOR(inode->i_rdev);
 	if (dev >= MAX_NBD)
 		return -ENODEV;
+
+	nbdev = &nbd_dev[dev];
 	nbd_dev[dev].refcnt++;
+	if (!(nbdev->flags & NBD_INITIALISED)) {
+		nbdev->queue_lock = MUTEX;
+		nbdev->flags |= NBD_INITIALISED;
+	}
 	MOD_INC_USE_COUNT;
 	return 0;
 }
@@ -82,6 +89,7 @@
 	struct msghdr msg;
 	struct iovec iov;
 	unsigned long flags;
+	sigset_t oldset;
 
 	oldfs = get_fs();
 	set_fs(get_ds());
@@ -94,8 +102,6 @@
 
 
 	do {
-		sigset_t oldset;
-
 		iov.iov_base = buf;
 		iov.iov_len = size;
 		msg.msg_name = NULL;
@@ -209,16 +215,18 @@
 		req = nbd_read_stat(lo);
 		if (!req)
 			return;
+		down (&lo->queue_lock);
 #ifdef PARANOIA
 		if (req != lo->tail) {
 			printk(KERN_ALERT "NBD: I have problem...\n");
 		}
 		if (lo != &nbd_dev[MINOR(req->rq_dev)]) {
 			printk(KERN_ALERT "NBD: request corrupted!\n");
-			continue;
+			goto next;
 		}
 		if (lo->magic != LO_MAGIC) {
 			printk(KERN_ALERT "NBD: nbd_dev[] corrupted: Not enough magic\n");
+			up (&lo->queue_lock);
 			return;
 		}
 #endif
@@ -231,6 +239,8 @@
 			lo->head = NULL;
 		}
 		lo->tail = lo->tail->next;
+	next:
+		up (&lo->queue_lock);
 	}
 }
 
@@ -291,7 +301,7 @@
 		lo = &nbd_dev[dev];
 		if (!lo->file)
 			FAIL("Request when not-ready.");
-		if ((req->cmd == WRITE) && (lo->flags && NBD_READ_ONLY))
+		if ((req->cmd == WRITE) && (lo->flags & NBD_READ_ONLY))
 			FAIL("Write on read-only");
 #ifdef PARANOIA
 		if (lo->magic != LO_MAGIC)
@@ -301,6 +311,9 @@
 		req->errors = 0;
 		CURRENT = CURRENT->next;
 		req->next = NULL;
+
+		spin_unlock_irq(&io_request_lock);
+		down (&lo->queue_lock);
 		if (lo->head == NULL) {
 			lo->head = req;
 			lo->tail = req;
@@ -309,8 +322,8 @@
 			lo->head = req;
 		}
 
-		spin_unlock_irq(&io_request_lock);
 		nbd_send_req(lo->sock, req);	/* Why does this block?         */
+		up (&lo->queue_lock);
 		spin_lock_irq(&io_request_lock);
 		continue;
 
diff -u --recursive --new-file v2.1.119/linux/drivers/block/swim3.c linux/drivers/block/swim3.c
--- v2.1.119/linux/drivers/block/swim3.c	Wed Aug 26 11:37:35 1998
+++ linux/drivers/block/swim3.c	Fri Aug 28 15:18:04 1998
@@ -969,7 +969,7 @@
 	NULL,			/* mmap */
 	floppy_open,		/* open */
 	NULL,			/* flush */
-	floppy_release,		/* release *
+	floppy_release,		/* release */
 	block_fsync,		/* fsync */
 	NULL,			/* fasync */
 	floppy_check_change,	/* check_media_change */
diff -u --recursive --new-file v2.1.119/linux/drivers/char/ChangeLog linux/drivers/char/ChangeLog
--- v2.1.119/linux/drivers/char/ChangeLog	Wed Jun 24 22:54:04 1998
+++ linux/drivers/char/ChangeLog	Tue Sep  1 10:32:13 1998
@@ -1,3 +1,8 @@
+1998-08-26  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* serial.c (rs_open): Correctly decrement the module in-use count
+		on errors.
+
 Thu Feb 19 14:24:08 1998  Theodore Ts'o  <tytso@rsts-11.mit.edu>
 
 	* tty_io.c (tty_name): Remove the non-reentrant (and non-SMP safe)
diff -u --recursive --new-file v2.1.119/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- v2.1.119/linux/drivers/char/apm_bios.c	Wed Aug 26 11:37:36 1998
+++ linux/drivers/char/apm_bios.c	Tue Sep  1 10:18:42 1998
@@ -1,7 +1,7 @@
 /* -*- linux-c -*-
  * APM BIOS driver for Linux
- * Copyright 1994, 1995, 1996 Stephen Rothwell
- *                           (Stephen.Rothwell@canb.auug.org.au)
+ * Copyright 1994-1998 Stephen Rothwell
+ *                     (Stephen.Rothwell@canb.auug.org.au)
  *
  * 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
@@ -13,8 +13,6 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  *
- * $Id: apm_bios.c,v 0.22 1995/03/09 14:12:02 sfr Exp $
- *
  * October 1995, Rik Faith (faith@cs.unc.edu):
  *    Minor enhancements and updates (to the patch set) for 1.3.x
  *    Documentation
@@ -28,6 +26,7 @@
  * May 1996, Version 1.2
  * Feb 1998, Version 1.3
  * Feb 1998, Version 1.4
+ * Aug 1998, Version 1.5
  *
  * History:
  *    0.6b: first version in official kernel, Linux 1.3.46
@@ -50,6 +49,9 @@
  *    1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by 
  *         Dean Gaudet <dgaudet@arctic.org>.
  *         C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87
+ *    1.5: Fix segment register reloading (in case of bad segments saved
+ *         across BIOS call).
+ *         Stephen ROthwell
  *
  * APM 1.1 Reference:
  *
@@ -158,6 +160,7 @@
  *                         [Confirmed by TI representative]
  * U: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
  *                    [Confirmed by BIOS disassembly]
+ *                    [This may work now ...]
  * P: Toshiba 1950S: battery life information only gets updated after resume
  * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking
  * 	broken in BIOS [Reported by Garst R. Reese <reese@isn.net>]
@@ -180,6 +183,7 @@
 /*
  * Define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call
  * should turn interrupts on before it does a 'hlt').
+ * This reportedly needs undefining for the ThinkPad 600.
  */
 #define APM_NOINTS
 
@@ -207,121 +211,9 @@
 #define APM_CHECK_TIMEOUT	(HZ)
 
 /*
- * These are the actual BIOS calls in assembler.  Depending on
- * APM_ZERO_SEGS and APM_NOINTS, we are being really paranoid here!  Not
- * only are interrupts disabled, but all the segment registers (except SS)
- * are saved and zeroed this means that if the BIOS tries to reference any
- * data without explicitly loading the segment registers, the kernel will
- * fault immediately rather than have some unforeseen circumstances for the
- * rest of the kernel.  And it will be very obvious!  :-) Doing this
- * depends on CS referring to the same physical memory as DS so that DS can
- * be zeroed before the call. Unfortunately, we can't do anything about the
- * stack segment/pointer.  Also, we tell the compiler that everything could
- * change.
+ * Save a segment register away
  */
-#ifdef APM_NOINTS
-#	define APM_DO_CLI	"cli\n\t"
-#else
-#	define APM_DO_CLI
-#endif
-#ifdef APM_ZERO_SEGS
-#	define APM_DO_ZERO_SEGS	\
-		"pushl %%ds\n\t" \
-		"pushl %%es\n\t" \
-		"pushl %%fs\n\t" \
-		"pushl %%gs\n\t" \
-		"xorl %%edx, %%edx\n\t" \
-		"movl %%dx, %%ds\n\t" \
-		"movl %%dx, %%es\n\t" \
-		"movl %%dx, %%fs\n\t" \
-		"movl %%dx, %%gs\n\t"
-#	define APM_DO_RESTORE_SEGS	\
-		"popl %%gs\n\t" \
-		"popl %%fs\n\t" \
-		"popl %%es\n\t" \
-		"popl %%ds\n\t"
-#else
-#	define APM_DO_ZERO_SEGS
-#	define APM_DO_RESTORE_SEGS
-#endif
-
-#define APM_BIOS_CALL(error_reg) \
-	__asm__ __volatile__( \
-		APM_DO_ZERO_SEGS \
-		"pushfl\n\t" \
-		APM_DO_CLI \
-		"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" \
-		"setc %%" # error_reg "\n\t" \
-		"popfl\n\t" \
-		APM_DO_RESTORE_SEGS
-#define APM_BIOS_CALL_END \
-		: "ax", "bx", "cx", "dx", "si", "di", "bp", "memory")
-
-#ifdef CONFIG_APM_CPU_IDLE
-#define APM_SET_CPU_IDLE(error) \
-	APM_BIOS_CALL(al) \
-	: "=a" (error) \
-	: "a" (0x5305) \
-	APM_BIOS_CALL_END
-#endif
-
-#define APM_SET_CPU_BUSY(error) \
-	APM_BIOS_CALL(al) \
-	: "=a" (error) \
-	: "a" (0x5306) \
-	APM_BIOS_CALL_END
-
-#define APM_SET_POWER_STATE(state, error) \
-	APM_BIOS_CALL(al) \
-	: "=a" (error) \
-	: "a" (0x5307), "b" (0x0001), "c" (state) \
-	APM_BIOS_CALL_END
-
-#ifdef CONFIG_APM_DISPLAY_BLANK
-#define APM_SET_DISPLAY_POWER_STATE(state, error) \
-	APM_BIOS_CALL(al) \
-	: "=a" (error) \
-	: "a" (0x5307), "b" (0x01ff), "c" (state) \
-	APM_BIOS_CALL_END
-#endif
-
-#ifdef CONFIG_APM_DO_ENABLE
-#define APM_ENABLE_POWER_MANAGEMENT(device, error) \
-	APM_BIOS_CALL(al) \
-	: "=a" (error) \
-	: "a" (0x5308), "b" (device), "c" (1) \
-	APM_BIOS_CALL_END
-#endif
-
-#define APM_GET_POWER_STATUS(bx, cx, dx, error) \
-	APM_BIOS_CALL(al) \
-	: "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx) \
-	: "a" (0x530a), "b" (1) \
-	APM_BIOS_CALL_END
-
-#define APM_GET_BATTERY_STATUS(which, bx, cx, dx, si, error) \
-	APM_BIOS_CALL(al) \
-	: "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx), "=S" (si) \
-	: "a" (0x530a), "b" (0x8000 | (which)) \
-	APM_BIOS_CALL_END
-
-#define APM_GET_EVENT(event, info, error)	\
-	APM_BIOS_CALL(al) \
-	: "=a" (error), "=b" (event), "=c" (info) \
-	: "a" (0x530b) \
-	APM_BIOS_CALL_END
-
-#define APM_DRIVER_VERSION(ver, ax, error) \
-	APM_BIOS_CALL(bl) \
-	: "=a" (ax), "=b" (error) \
-	: "a" (0x530e), "b" (0), "c" (ver) \
-	APM_BIOS_CALL_END
-
-#define APM_ENGAGE_POWER_MANAGEMENT(device, error) \
-	APM_BIOS_CALL(al) \
-	: "=a" (error) \
-	: "a" (0x530f), "b" (device), "c" (1) \
-	APM_BIOS_CALL_END
+#define savesegment(seg, where)	__asm__ __volatile__("movw %%" #seg ", %0\n" : "=m" (where))
 
 /*
  * Forward declarations
@@ -371,7 +263,7 @@
 
 static struct timer_list	apm_timer;
 
-static char			driver_version[] = "1.4";	/* no spaces */
+static char			driver_version[] = "1.5";	/* no spaces */
 
 #ifdef APM_DEBUG
 static char *	apm_event_name[] = {
@@ -445,74 +337,197 @@
 };
 #define ERROR_COUNT	(sizeof(error_table)/sizeof(lookup_t))
 
-static int apm_driver_version(u_short *val)
+/*
+ * These are the actual BIOS calls.  Depending on APM_ZERO_SEGS
+ * and APM_NOINTS, we are being really paranoid here!  Not only are
+ * interrupts disabled, but all the segment registers (except SS) are
+ * saved and zeroed this means that if the BIOS tries to reference any
+ * data without explicitly loading the segment registers, the kernel will
+ * fault immediately rather than have some unforeseen circumstances for
+ * the rest of the kernel.  And it will be very obvious!  :-) Doing this
+ * depends on CS referring to the same physical memory as DS so that DS
+ * can be zeroed before the call. Unfortunately, we can't do anything
+ * about the stack segment/pointer.  Also, we tell the compiler that
+ * everything could change.
+ */
+
+static inline int apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in,
+	u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi)
+{
+	u16	old_fs;
+	u16	old_gs;
+	int	error;
+
+#ifdef APM_ZERO_SEGS
+	savesegment(fs, old_fs);
+	savesegment(gs, old_gs);
+#endif
+	__asm__ __volatile__(
+		"pushfl\n\t"
+#ifdef APM_NOINTS
+		"cli\n\t"
+#endif
+#ifdef APM_ZERO_SEGS
+		"pushl %%ds\n\t"
+		"pushl %%es\n\t"
+		"movw %9, %%ds\n\t"
+		"movw %9, %%es\n\t"
+		"movw %9, %%fs\n\t"
+		"movw %9, %%gs\n\t"
+#endif
+		"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
+		"movl $0, %%edi\n\t"
+		"jnc 1f\n\t"
+		"movl $1, %%edi\n"
+		"1:\tpopl %%es\n\t"
+		"popl %%ds\n\t"
+		"popfl\n\t"
+		:  "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx),
+		   "=S" (*esi), "=D" (error)
+		: "a" (eax_in), "b" (ebx_in), "c" (ecx_in)
+#ifdef APM_ZERO_SEGS
+		  , "r" (0)
+#endif
+		: "ax", "bx", "cx", "dx", "si", "di", "bp", "memory");
+#ifdef APM_ZERO_SEGS
+	loadsegment(fs, old_fs);
+	loadsegment(gs, old_gs);
+#endif
+	return error;
+}
+
+/*
+ * This version only returns one value (usually an error code)
+ */
+
+static inline int apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in, u32 *eax)
 {
-	u_short	error;
+	u16	old_fs;
+	u16	old_gs;
+	int	error;
 
-	APM_DRIVER_VERSION(*val, *val, error);
+#ifdef APM_ZERO_SEGS
+	savesegment(fs, old_fs);
+	savesegment(gs, old_gs);
+#endif
+	__asm__ __volatile__(
+		"pushfl\n\t"
+#ifdef APM_NOINTS
+		"cli\n\t"
+#endif
+#ifdef APM_ZERO_SEGS
+		"pushl %%ds\n\t"
+		"pushl %%es\n\t"
+		"movw %5, %%ds\n\t"
+		"movw %5, %%es\n\t"
+		"movw %5, %%fs\n\t"
+		"movw %5, %%gs\n\t"
+#endif
+		"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
+		"movl $0, %%edi\n\t"
+		"jnc 1f\n\t"
+		"movl $1, %%edi\n"
+		"1:\tpopl %%es\n\t"
+		"popl %%ds\n\t"
+		"popfl\n\t"
+		: "=a" (*eax), "=D" (error)
+		: "a" (eax_in), "b" (ebx_in), "c" (ecx_in)
+#ifdef APM_ZERO_SEGS
+		  , "r" (0)
+#endif
+		: "ax", "bx", "cx", "dx", "si", "di", "bp", "memory");
+#ifdef APM_ZERO_SEGS
+	loadsegment(fs, old_fs);
+	loadsegment(gs, old_gs);
+#endif
+	return error;
+}
 
-	if (error & 0xff)
-		return (*val >> 8);
+static int apm_driver_version(u_short *val)
+{
+	int	error;
+	u32	eax;
+
+	error = apm_bios_call_simple(0x530e, 0, *val, &eax);
+	if (error)
+		return (eax >> 8) & 0xff;
+	*val = eax;
 	return APM_SUCCESS;
 }
 
 static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info)
 {
-	u_short	error;
-
-	APM_GET_EVENT(*event, *info, error);
-	if (error & 0xff)
-		return (error >> 8);
+	int	error;
+	u32	eax;
+	u32	ebx;
+	u32	ecx;
+	u32	dummy;
+
+	error = apm_bios_call(0x530b, 0, 0, &eax, &ebx, &ecx, &dummy, &dummy);
+	if (error)
+		return (eax >> 8) & 0xff;
+	*event = ebx;
 	if (apm_bios_info.version < 0x0102)
 		*info = ~0; /* indicate info not valid */
+	else
+		*info = ecx;
 	return APM_SUCCESS;
 }
 
-int apm_set_power_state(u_short state)
+static int set_power_state(u_short what, u_short state)
 {
-	u_short	error;
+	int	error;
+	u32	eax;
 
-	APM_SET_POWER_STATE(state, error);
-	if (error & 0xff)
-		return (error >> 8);
+	error = apm_bios_call_simple(0x5307, what, state, &eax);
+	if (error)
+		return (eax >> 8) & 0xff;
 	return APM_SUCCESS;
 }
 
+int apm_set_power_state(u_short state)
+{
+	return set_power_state(0x0001, state);
+}
+
 #ifdef CONFIG_APM_DISPLAY_BLANK
 /* Called by apm_display_blank and apm_display_unblank when apm_enabled. */
 static int apm_set_display_power_state(u_short state)
 {
-	u_short	error;
-
-	APM_SET_DISPLAY_POWER_STATE(state, error);
-	if (error & 0xff)
-		return (error >> 8);
-	return APM_SUCCESS;
+	return set_power_state(0x01ff, state);
 }
 #endif
 
 #ifdef CONFIG_APM_DO_ENABLE
 /* Called by apm_setup if apm_enabled will be true. */
-static inline int apm_enable_power_management(void)
+static int apm_enable_power_management(void)
 {
-	u_short	error;
+	int	error;
+	u32	eax;
 
-	APM_ENABLE_POWER_MANAGEMENT((apm_bios_info.version > 0x100)
-				    ? 0x0001 : 0xffff,
-				    error);
-	if (error & 0xff)
-		return (error >> 8);
+	error = apm_bios_call_simple(0x5308,
+			(apm_bios_info.version > 0x100) ? 0x0001 : 0xffff, 1, &eax);
+	if (error)
+		return (eax >> 8) & 0xff;
 	return APM_SUCCESS;
 }
 #endif
 
 static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
 {
-	u_short	error;
-
-	APM_GET_POWER_STATUS(*status, *bat, *life, error);
-	if (error & 0xff)
-		return (error >> 8);
+	int	error;
+	u32	eax;
+	u32	ebx;
+	u32	ecx;
+	u32	edx;
+	u32	dummy;
+
+	error = apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy);
+	if (error)
+		return (eax >> 8) & 0xff;
+	*status = ebx;
+	*bat = ecx;
+	*life = edx;
 	return APM_SUCCESS;
 }
 
@@ -521,29 +536,40 @@
 static int apm_get_battery_status(u_short which, 
 				  u_short *bat, u_short *life, u_short *nbat)
 {
-	u_short status, error;
+	u_short status;
+	int	error;
+	u32	eax;
+	u32	ebx;
+	u32	ecx;
+	u32	edx;
+	u32	esi;
 
 	if (apm_bios_info.version < 0x0102) {
 		/* pretend we only have one battery. */
-		if (which!=1) return APM_BAD_DEVICE;
+		if (which != 1)
+			return APM_BAD_DEVICE;
 		*nbat = 1;
 		return apm_get_power_status(&status, bat, life);
 	}
 
-	APM_GET_BATTERY_STATUS(which, status, *bat, *life, *nbat, error);
-	if (error & 0xff)
-		return (error >> 8);
+	error = apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax, &ebx, &ecx, &edx, &esi);
+	if (error)
+		return (eax >> 8) & 0xff;
+	*bat = ecx;
+	*life = edx;
+	*nbat = esi;
 	return APM_SUCCESS;
 }
 #endif
 
-static inline int apm_engage_power_management(u_short device)
+static int apm_engage_power_management(u_short device)
 {
-	u_short	error;
+	int	error;
+	u32	eax;
 
-	APM_ENGAGE_POWER_MANAGEMENT(device, error);
-	if (error & 0xff)
-		return (error >> 8);
+	error = apm_bios_call_simple(0x530f, device, 1, &eax);
+	if (error)
+		return (eax >> 8) & 0xff;
 	return APM_SUCCESS;
 }
 
@@ -850,13 +876,14 @@
 int apm_do_idle(void)
 {
 #ifdef CONFIG_APM_CPU_IDLE
-	unsigned short	error;
+	int	error;
+	u32	dummy;
 
 	if (!apm_enabled)
 		return 0;
 
-	APM_SET_CPU_IDLE(error);
-	if (error & 0xff)
+	error = apm_bios_call_simple(0x5305, 0, 0, &dummy);
+	if (error)
 		return 0;
 
 	clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
@@ -870,19 +897,16 @@
 void apm_do_busy(void)
 {
 #ifdef CONFIG_APM_CPU_IDLE
-	unsigned short	error;
-
-	if (!apm_enabled)
-		return;
+	u32	dummy;
 
+	if (apm_enabled
 #ifndef ALWAYS_CALL_BUSY
-	if (!clock_slowed)
-		return;
+		&& clock_slowed
 #endif
-
-	APM_SET_CPU_BUSY(error);
-
-	clock_slowed = 0;
+	) {
+		(void) apm_bios_call_simple(0x5306, 0, 0, &dummy);
+		clock_slowed = 0;
+	}
 #endif
 }
 
@@ -1242,11 +1266,11 @@
 		 */
 		apm_bios_info.version = 0x0102;
 		error = apm_driver_version(&apm_bios_info.version);
-		if (error != 0) { /* Fall back to an APM 1.1 connection. */
+		if (error != APM_SUCCESS) { /* Fall back to an APM 1.1 connection. */
 			apm_bios_info.version = 0x0101;
 			error = apm_driver_version(&apm_bios_info.version);
 		}
-		if (error != 0) /* Fall back to an APM 1.0 connection. */
+		if (error != APM_SUCCESS) /* Fall back to an APM 1.0 connection. */
 			apm_bios_info.version = 0x100;
 		else {
 			apm_engage_power_management(0x0001);
diff -u --recursive --new-file v2.1.119/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.1.119/linux/drivers/char/bttv.c	Wed Aug 26 11:37:36 1998
+++ linux/drivers/char/bttv.c	Fri Sep  4 15:32:26 1998
@@ -85,7 +85,7 @@
 static unsigned int radio[BTTV_MAX];
 static unsigned int card[BTTV_MAX] = { 0, 0, 
                                        0, 0 };
-static unsigned int pll[BTTV_MAX] = { 0, 0, 0, 0 };
+static unsigned int pll[BTTV_MAX] = { 0, 0, 0, 0 };          
 
 static int bttv_num;			/* number of Bt848s in use */
 static struct bttv bttvs[BTTV_MAX];
@@ -433,6 +433,9 @@
         {0, 0xc00, 0x800, 0x400, 0xc00, 0}},
         /* TurboTV */
         { 3, 0, 2, 3, { 2, 3, 1, 1}, { 1, 1, 2, 3, 0}},
+        /* Newer Hauppauge */
+	{ 2, 0, 2, 1, { 2, 0, 0, 0}, {0, 1, 2, 3, 4}},
+  
 };
 #define TVCARDS (sizeof(tvcards)/sizeof(tvcard))
 
@@ -545,30 +548,25 @@
         int i;
         unsigned long tv;
 
-        if (!btv->pll)
+        if (!btv->pll.pll_crystal)
 		return 0;
-        if ((btread(BT848_IFORM)&BT848_IFORM_XT0))
+        if ((btread(BT848_IFORM)&btv->pll.pll_crystal))
         {
                 /* printk ("switching PLL off\n");*/
                 btwrite(0x00,BT848_TGCTRL);
                 btwrite(0x00,BT848_PLL_XCI);
-                btv->pll&=~2;
+                btv->pll.pll_crystal&=~2;
                 return 0;
         }
         
         /* do not set pll again if already active */
-        if (btv->pll&2)
+        if (btv->pll.pll_crystal&2)
                 return 1;
         
         /* printk ("setting PLL for PAL/SECAM\n");*/
 
-	set_pll_freq(btv, 28636363, 35468950);
-/*	
-        btwrite(0x00,BT848_TGCTRL);
-        btwrite(0xf9,BT848_PLL_F_LO);
-        btwrite(0xdc,BT848_PLL_F_HI);
-        btwrite(14|BT848_PLL_X,BT848_PLL_XCI);
-*/
+	set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+
 	/*
 	 *	Let other people run while the PLL stabilizes
 	 */
@@ -587,7 +585,7 @@
                 else
                 {
                         btwrite(0x08,BT848_TGCTRL);
-                        btv->pll|=2;
+                        btv->pll.pll_crystal|=2;
                         return 1;            
                 }
                 udelay(10000);
@@ -1179,7 +1177,7 @@
 	audio(btv, AUDIO_MUTE);
 	udelay(AUDIO_MUTE_DELAY);
 
-	if (radio[btv->nr]) 
+	if (btv->radio) 
 	{
 		if (btv->have_tuner)
 			i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER,
@@ -1831,12 +1829,24 @@
 				return -EFAULT;
 			break;
 
-		case BTTV_FIELDNR:
+                case BTTV_FIELDNR: 
 			if(copy_to_user((void *) arg, (void *) &btv->last_field, 
                                         sizeof(btv->last_field)))
 				return -EFAULT;
                         break;
+      
+                case BTTV_PLLSET: {
+                        struct bttv_pll_info p;
+                        if(!capable(CAP_SYS_ADMIN))
+                        	return -EPERM;
+                        if(copy_from_user(&p , (void *) arg, sizeof(btv->pll)))
+				return -EFAULT;
+                        btv->pll.pll_ifreq = p.pll_ifreq;
+                        btv->pll.pll_ofreq = p.pll_ofreq;
+                        btv->pll.pll_crystal = p.pll_crystal;
 
+			break;
+                }						
 	        case VIDIOCMCAPTURE:
 		{
                         struct video_mmap vm;
@@ -2406,7 +2416,12 @@
 	        btv->type=BTTV_MIRO;
     
 		if (I2CRead(&(btv->i2c), I2C_HAUPEE)>=0)
-		        btv->type=BTTV_HAUPPAUGE;
+		{
+			if(btv->id>849)
+				btv->type=BTTV_HAUPPAUGE878;
+			else
+			        btv->type=BTTV_HAUPPAUGE;
+		}
 		else
 		        if (I2CRead(&(btv->i2c), I2C_STBEE)>=0)
 			        btv->type=BTTV_STB;
@@ -2441,6 +2456,8 @@
 
 	/* How do I detect the tuner type for other cards but Miro ??? */
 	printk(KERN_INFO "bttv%d: model: ", btv->nr);
+	
+	sprintf(btv->video_dev.name,"BT%d",btv->id);
 	switch (btv->type) 
 	{
 		case BTTV_MIRO:
@@ -2452,31 +2469,32 @@
 						   I2C_DRIVERID_TUNER,
 						   TUNER_SET_TYPE,&tunertype);
 			}
-			strcpy(btv->video_dev.name,"BT848(Miro)");
+			strcat(btv->video_dev.name, "(Miro)");
 			break;
 		case BTTV_HAUPPAUGE:
+		case BTTV_HAUPPAUGE878:
 			printk("HAUPPAUGE\n");
-			strcpy(btv->video_dev.name,"BT848(Hauppauge)");
+			strcat(btv->video_dev.name,"(Hauppauge)");
 			break;
 		case BTTV_STB: 
 			printk("STB\n");
-			strcpy(btv->video_dev.name,"BT848(STB)");
+			strcat(btv->video_dev.name,"(STB)");
 			break;
 		case BTTV_INTEL: 
 			printk("Intel\n");
-			strcpy(btv->video_dev.name,"BT848(Intel)");
+			strcat(btv->video_dev.name,"(Intel)");
 			break;
 		case BTTV_DIAMOND: 
 			printk("Diamond\n");
-			strcpy(btv->video_dev.name,"BT848(Diamond)");
+			strcat(btv->video_dev.name,"(Diamond)");
 			break;
 		case BTTV_AVERMEDIA: 
 			printk("AVerMedia\n");
-			strcpy(btv->video_dev.name,"BT848(AVerMedia)");
+			strcat(btv->video_dev.name,"(AVerMedia)");
 			break;
 		case BTTV_MATRIX_VISION: 
 			printk("MATRIX-Vision\n");
-			strcpy(btv->video_dev.name,"BT848(MATRIX-Vision)");
+			strcat(btv->video_dev.name,"(MATRIX-Vision)");
 			break;
 	}
 	audio(btv, AUDIO_MUTE);
@@ -2902,12 +2920,16 @@
         printk("irq: %d, ",btv->irq);
         printk("memory: 0x%08x.\n", btv->bt848_adr);
         
-        btv->pll=0;
+        btv->pll.pll_ifreq=0;
+        btv->pll.pll_ifreq=0;
+        btv->pll.pll_crystal=0;
         if(pll[btv->nr])
                 if (!(btv->id==848 && btv->revision==0x11))
                 {
                         printk(KERN_INFO "bttv%d: internal PLL, single crystal operation enabled\n",bttv_num);
-                        btv->pll=1;
+                        btv->pll.pll_ofreq=28636363;
+                        btv->pll.pll_ifreq=35468950;
+                        btv->pll.pll_crystal=BT848_IFORM_XT1;
                 }
         
         btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000);
@@ -2972,7 +2994,7 @@
                 dev = dev->next;
         }
 	if(bttv_num)
-		printk(KERN_INFO "bttv: %d Bt848 card(s) found.\n", bttv_num);
+		printk(KERN_INFO "bttv: %d BT8xx card(s) found.\n", bttv_num);
 	return bttv_num;
 }
  
@@ -3088,3 +3110,4 @@
  * tab-width: 8
  * End:
  */
+ 
diff -u --recursive --new-file v2.1.119/linux/drivers/char/bttv.h linux/drivers/char/bttv.h
--- v2.1.119/linux/drivers/char/bttv.h	Wed Aug 26 11:37:36 1998
+++ linux/drivers/char/bttv.h	Fri Sep  4 15:32:26 1998
@@ -57,6 +57,12 @@
 };
 
 
+struct bttv_pll_info {
+	unsigned int pll_ifreq;	/* PLL input frequency 	  */
+	unsigned int pll_ofreq;	/* PLL output frequency   */
+	unsigned int pll_crystal;	/* Crystal used for input */
+};
+
 struct bttv 
 {
 	struct video_device video_dev;
@@ -130,14 +136,13 @@
 	int grab;
 	int grabcount;
 
-	int pll;
+	struct bttv_pll_info pll;
 	unsigned int Fsc;
 	unsigned int field;
 	unsigned int last_field; /* number of last grabbed field */
 	int i2c_command;
 	int triton1;
 };
-
 #endif
 
 /*The following should be done in more portable way. It depends on define
@@ -161,6 +166,7 @@
 #define BTTV_WRITEE		_IOR('v',  BASE_VIDIOCPRIVATE+1, char [256])
 #define BTTV_GRAB		_IOR('v' , BASE_VIDIOCPRIVATE+2, struct gbuf)
 #define BTTV_FIELDNR		_IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int)
+#define BTTV_PLLSET		_IOW('v' , BASE_VIDIOCPRIVATE+3, struct bttv_pll_info)
 
 
 #define BTTV_UNKNOWN       0x00
@@ -172,6 +178,7 @@
 #define BTTV_AVERMEDIA     0x06 
 #define BTTV_MATRIX_VISION 0x07 
 #define BTTV_FLYVIDEO      0x08
+#define BTTV_HAUPPAUGE878  0x09
 
 #define AUDIO_TUNER        0x00
 #define AUDIO_RADIO        0x01
diff -u --recursive --new-file v2.1.119/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- v2.1.119/linux/drivers/char/cyclades.c	Wed Aug 26 11:37:36 1998
+++ linux/drivers/char/cyclades.c	Thu Sep  3 16:02:52 1998
@@ -1,7 +1,7 @@
 #define BLOCKMOVE
 #define	Z_WAKE
 static char rcsid[] =
-"$Revision: 2.2.1.6 $$Date: 1998/08/20 17:15:39 $";
+"$Revision: 2.2.1.7 $$Date: 1998/09/03 12:07:28 $";
 
 /*
  *  linux/drivers/char/cyclades.c
@@ -31,6 +31,11 @@
  *   void cleanup_module(void);
  *
  * $Log: cyclades.c,v $
+ * Revision 2.2.1.7  1998/09/03 12:07:28 ivan
+ * Fixed bug in cy_close function, which was not informing HW of
+ * which port should have the reception disabled before doing so;
+ * fixed Cyclom-8YoP hardware detection bug.
+ *
  * Revision 2.2.1.6  1998/08/20 17:15:39 ivan
  * Fixed bug in cy_close function, which causes malfunction
  * of one of the first 4 ports when a higher port is closed
@@ -2690,6 +2695,8 @@
 			(cy_card[info->card].base_addr +
 			 (cy_chip_offset[channel>>2] <<index));
 	/* Stop accepting input */
+	channel &= 0x03;
+	cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
 	cy_writeb((u_long)base_addr+(CySRER<<index),
 			cy_readb(base_addr+(CySRER<<index)) & ~CyRxData);
 	if (info->flags & ASYNC_INITIALIZED) {
@@ -4539,7 +4546,7 @@
   unsigned long         pci_intr_ctrl;
   unsigned char         cy_pci_irq = 0;
   uclong                cy_pci_addr0, cy_pci_addr1, cy_pci_addr2;
-  unsigned short        i,j,cy_pci_nchan;
+  unsigned short        i,j,cy_pci_nchan, plx_ver;
   unsigned short        device_id,dev_index = 0;
   uclong		mailbox;
   uclong		Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
@@ -4651,10 +4658,26 @@
                 IRQ_cards[cy_pci_irq] = &cy_card[j];
 
                 /* enable interrupts in the PCI interface */
-                outw(inw(cy_pci_addr1+0x68)|0x0900,cy_pci_addr1+0x68);
-                pci_intr_ctrl = (unsigned long)
-		                (inw(cy_pci_addr1+0x68)
+		plx_ver = cy_readb(cy_pci_addr2 + CyPLX_VER) & 0x0f;
+		switch (plx_ver) {
+		    case PLX_9050:
+
+		    outw(inw(cy_pci_addr1+0x4c)|0x0040,cy_pci_addr1+0x4c);
+		    pci_intr_ctrl = (unsigned long)
+				(inw(cy_pci_addr1+0x4c)
+				| inw(cy_pci_addr1+0x4e)<<16);
+		    break;
+
+		    case PLX_9060:
+		    case PLX_9080:
+		    default: /* Old boards, use PLX_9060 */
+
+		    outw(inw(cy_pci_addr1+0x68)|0x0900,cy_pci_addr1+0x68);
+		    pci_intr_ctrl = (unsigned long)
+				(inw(cy_pci_addr1+0x68)
 				| inw(cy_pci_addr1+0x6a)<<16);
+		    break;
+		}
 
                 /* print message */
                 printk("Cyclom-Y/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
diff -u --recursive --new-file v2.1.119/linux/drivers/char/pc_keyb.c linux/drivers/char/pc_keyb.c
--- v2.1.119/linux/drivers/char/pc_keyb.c	Tue Aug 18 22:02:03 1998
+++ linux/drivers/char/pc_keyb.c	Wed Sep  2 16:19:04 1998
@@ -62,34 +62,41 @@
 
 __initfunc(static int kbd_wait_for_input(void))
 {
-	int     status, data;
-	unsigned long start = jiffies;
+	long timeout = KBD_INIT_TIMEOUT;
+	int retval = -1;
 
-	do {
-                status = inb(KBD_STATUS_REG);
+	goto in_loop;
+	for (;;) {
+		unsigned char status, data;
+		if (--timeout < 0)
+			break;
+		mdelay(1);
+in_loop:
+		status = inb(KBD_STATUS_REG);
 
-                /*
-                 * Wait for input data to become available.  This bit will
-                 * then be cleared by the following read of the DATA
-                 * register.
-                 */
-                if (!(status & KBD_STAT_OBF))
+		/*
+		 * Wait for input data to become available.  This bit will
+		 * then be cleared by the following read of the DATA
+		 * register.
+		 */
+		if (!(status & KBD_STAT_OBF))
 			continue;
 
 		data = inb(KBD_DATA_REG);
 
-                /*
-                 * Check to see if a timeout error has occurred.  This means
-                 * that transmission was started but did not complete in the
-                 * normal time cycle.  PERR is set when a parity error occurred
-                 * in the last transmission.
-                 */
-                if (status & (KBD_STAT_GTO | KBD_STAT_PERR)) {
+		/*
+		 * Check to see if a timeout error has occurred.  This means
+		 * that transmission was started but did not complete in the
+		 * normal time cycle.  PERR is set when a parity error occurred
+		 * in the last transmission.
+		 */
+		if (status & (KBD_STAT_GTO | KBD_STAT_PERR))
 			continue;
-                }
-		return (data & 0xff);
-        } while (jiffies - start < KBD_INIT_TIMEOUT);
-        return -1;	/* timed-out if fell through to here... */
+
+		retval = data;
+		break;
+	}
+	return retval;
 }
 
 __initfunc(static void kbd_write(int address, int data))
@@ -144,14 +151,14 @@
 	 * Set up to try again if the keyboard asks for RESEND.
 	 */
 
-        do {
+	do {
 		kbd_write(KBD_DATA_REG, KBD_CMD_RESET);
-                status = kbd_wait_for_input();
-                if (status == KBD_REPLY_ACK)
+		status = kbd_wait_for_input();
+		if (status == KBD_REPLY_ACK)
 			break;
-                else if (status != KBD_REPLY_RESEND)
+		else if (status != KBD_REPLY_RESEND)
 			return "Keyboard reset failed, no ACK";
-        } while (1);
+	} while (1);
 
 	if (kbd_wait_for_input() != KBD_REPLY_POR)
 		return "Keyboard reset failed, no POR";
@@ -174,9 +181,9 @@
 
 	kbd_write(KBD_CNTL_REG, KBD_CCMD_WRITE_MODE);
 	kbd_write(KBD_DATA_REG, KBD_MODE_KBD_INT
-		              | KBD_MODE_SYS
-		              | KBD_MODE_DISABLE_MOUSE
-		              | KBD_MODE_KCC);
+			      | KBD_MODE_SYS
+			      | KBD_MODE_DISABLE_MOUSE
+			      | KBD_MODE_KCC);
 
 	/* ibm powerpc portables need this to use scan-code set 1 -- Cort */
 	kbd_write(KBD_CNTL_REG, KBD_CCMD_READ_MODE);
@@ -439,7 +446,7 @@
 int pckbd_pretranslate(unsigned char scancode, char raw_mode)
 {
 	if (scancode == 0xff) {
-	        /* in scancode mode 1, my ESC key generates 0xff */
+		/* in scancode mode 1, my ESC key generates 0xff */
 		/* the calculator keys on a FOCUS 9000 generate 0xff */
 #ifndef KBD_IS_FOCUS_9000
 #ifdef KBD_REPORT_ERR
diff -u --recursive --new-file v2.1.119/linux/drivers/char/pc_keyb.h linux/drivers/char/pc_keyb.h
--- v2.1.119/linux/drivers/char/pc_keyb.h	Thu Aug  6 14:06:31 1998
+++ linux/drivers/char/pc_keyb.h	Fri Aug 28 18:06:06 1998
@@ -15,7 +15,7 @@
 #define KBD_REPORT_TIMEOUTS		/* Report keyboard timeouts */
 #undef KBD_IS_FOCUS_9000		/* We have the brain-damaged FOCUS-9000 keyboard */
 
-#define KBD_INIT_TIMEOUT HZ		/* Timeout in jiffies for initializing the keyboard */
+#define KBD_INIT_TIMEOUT 1000		/* Timeout in ms for initializing the keyboard */
 #define KBC_TIMEOUT 250			/* Timeout in ms for sending to keyboard controller */
 #define KBD_TIMEOUT 1000		/* Timeout in ms for keyboard command acknowledge */
 
diff -u --recursive --new-file v2.1.119/linux/drivers/char/pms.c linux/drivers/char/pms.c
--- v2.1.119/linux/drivers/char/pms.c	Tue Aug 18 22:02:03 1998
+++ linux/drivers/char/pms.c	Fri Aug 28 10:48:39 1998
@@ -255,20 +255,6 @@
 	}
 }
 
-static void pms_hstart(short start)
-{
-	switch(decoder)
-	{
-		case PHILIPS1:
-			i2c_write(0x8A, 0x05, start);
-			i2c_write(0x8A, 0x18, start);
-			break;
-		case PHILIPS2:
-			i2c_write(0x42, 0x05, start);
-			i2c_write(0x42, 0x18, start);
-			break;
-	}
-}
 
 static void pms_format(short format)
 {
@@ -303,6 +289,29 @@
 	}
 }
 
+#ifdef FOR_FUTURE_EXPANSION
+
+/*
+ *	These features of the PMS card are not currently exposes. They
+ *	could become a private v4l ioctl for PMSCONFIG or somesuch if 
+ *	people need it. We also don't yet use the PMS interrupt.
+ */
+
+static void pms_hstart(short start)
+{
+	switch(decoder)
+	{
+		case PHILIPS1:
+			i2c_write(0x8A, 0x05, start);
+			i2c_write(0x8A, 0x18, start);
+			break;
+		case PHILIPS2:
+			i2c_write(0x42, 0x05, start);
+			i2c_write(0x42, 0x18, start);
+			break;
+	}
+}
+
 /*
  *	Bandpass filters
  */
@@ -347,14 +356,6 @@
 		i2c_andor(0x42, 0x10, 0xFC, noise&3);
 }
 
-static void pms_secamcross(short cross)
-{
-	if(decoder==PHILIPS2)
-		i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5);
-	else if(decoder==PHILIPS1)
-		i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5);
-}
-
 static void pms_forcecolour(short colour)
 {
 	if(decoder==PHILIPS2)
@@ -409,20 +410,6 @@
 	}
 }
 
-static void pms_swsense(short sense)
-{
-	if(decoder==PHILIPS2)
-	{
-		i2c_write(0x8A, 0x0A, sense);
-		i2c_write(0x8A, 0x0B, sense);
-	}
-	else if(decoder==PHILIPS1)
-	{
-		i2c_write(0x42, 0x0A, sense);
-		i2c_write(0x42, 0x0B, sense);
-	}
-}
-
 static void pms_chromagain(short chroma)
 {
 	if(decoder==PHILIPS2)
@@ -446,6 +433,38 @@
 	mvv_write(0x3A, data);
 }
 
+static void pms_vstart(short start)
+{
+	mvv_write(0x16, start);
+	mvv_write(0x17, (start>>8)&0x01);
+}
+
+#endif
+
+static void pms_secamcross(short cross)
+{
+	if(decoder==PHILIPS2)
+		i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5);
+	else if(decoder==PHILIPS1)
+		i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5);
+}
+
+
+static void pms_swsense(short sense)
+{
+	if(decoder==PHILIPS2)
+	{
+		i2c_write(0x8A, 0x0A, sense);
+		i2c_write(0x8A, 0x0B, sense);
+	}
+	else if(decoder==PHILIPS1)
+	{
+		i2c_write(0x42, 0x0A, sense);
+		i2c_write(0x42, 0x0B, sense);
+	}
+}
+
+
 static void pms_framerate(short frr)
 {
 	int fps=(standard==1)?30:25;
@@ -586,11 +605,6 @@
 	mvv_write(0x33, MVVMEMORYWIDTH);
 }
 
-static void pms_vstart(short start)
-{
-	mvv_write(0x16, start);
-	mvv_write(0x17, (start>>8)&0x01);
-}
 
 /*
  *	Set Input
diff -u --recursive --new-file v2.1.119/linux/drivers/char/psaux.c linux/drivers/char/psaux.c
--- v2.1.119/linux/drivers/char/psaux.c	Wed Aug 26 11:37:37 1998
+++ linux/drivers/char/psaux.c	Wed Sep  2 17:39:12 1998
@@ -245,6 +245,9 @@
 	fasync_aux(-1, file, 0);
 	if (--aux_count)
 		return 0;
+#ifdef CONFIG_VT
+	pckbd_read_mask = KBD_STAT_OBF;
+#endif
 	aux_start_atomic();
 	aux_write_cmd(AUX_INTS_OFF);			    /* Disable controller ints */
 	poll_aux_status();
@@ -297,6 +300,10 @@
 	poll_aux_status();
 	aux_end_atomic();
 
+#ifdef CONFIG_VT
+	pckbd_read_mask = AUX_STAT_OBF;
+#endif
+
 	aux_ready = 0;
 	return 0;
 }
@@ -613,9 +620,6 @@
 	if (aux_device_present == 0xaa) {
 		printk(KERN_INFO "PS/2 auxiliary pointing device detected -- driver installed.\n");
 	 	aux_present = 1;
-#ifdef CONFIG_VT
-		pckbd_read_mask = AUX_STAT_OBF;
-#endif
 	} else {
 		return -EIO;
 	}
@@ -643,6 +647,7 @@
 		poll_aux_status();
 		aux_end_atomic();
 	}
+
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.119/linux/drivers/char/radio-miropcm20.c linux/drivers/char/radio-miropcm20.c
--- v2.1.119/linux/drivers/char/radio-miropcm20.c	Wed Aug 26 11:37:37 1998
+++ linux/drivers/char/radio-miropcm20.c	Mon Aug 31 10:32:19 1998
@@ -10,7 +10,6 @@
 #include <linux/init.h>			/* Initdata			*/
 #include <asm/uaccess.h>		/* copy to/from user		*/
 #include <linux/videodev.h>		/* kernel radio structs		*/
-#include <linux/config.h>		/* CONFIG_RADIO_MIROPCM20 	*/
 #include "../sound/lowlevel/miroaci.h"	/* ACI Control by acimixer	*/
 
 static int users = 0;
diff -u --recursive --new-file v2.1.119/linux/drivers/char/rocket.c linux/drivers/char/rocket.c
--- v2.1.119/linux/drivers/char/rocket.c	Thu Jul 16 18:09:25 1998
+++ linux/drivers/char/rocket.c	Tue Sep  1 10:32:36 1998
@@ -100,8 +100,8 @@
 #include "version.h"
 #else
 #include <linux/rocket.h>
-#define ROCKET_VERSION "1.14b"
-#define ROCKET_DATE "29-Jun-98"
+#define ROCKET_VERSION "1.14c"
+#define ROCKET_DATE "24-Aug-98"
 #endif /* LOCAL_ROCKET_H */
 
 #define ROCKET_PARANOIA_CHECK
@@ -1935,7 +1935,8 @@
 #ifdef ENABLE_PCI
 #if (LINUX_VERSION_CODE < 0x020163) /* 2.1.99 */
 /* For compatibility */
-static struct pci_dev *pci_find_slot(char bus, char device_fn)
+static struct pci_dev *pci_find_slot(unsigned char bus,
+				     unsigned char device_fn)
 {
 	unsigned short		vendor_id, device_id;
 	int			ret, error;
@@ -1962,7 +1963,7 @@
 }
 #endif
      
-__initfunc(int register_PCI(int i, char bus, char device_fn))
+__initfunc(int register_PCI(int i, unsigned int bus, unsigned int device_fn))
 {
 	int	num_aiops, aiop, max_num_aiops, num_chan, chan;
 	unsigned int	aiopio[MAX_AIOPS_PER_BOARD];
diff -u --recursive --new-file v2.1.119/linux/drivers/char/saa5249.c linux/drivers/char/saa5249.c
--- v2.1.119/linux/drivers/char/saa5249.c	Sun Jul 26 11:57:15 1998
+++ linux/drivers/char/saa5249.c	Fri Aug 28 10:47:15 1998
@@ -172,6 +172,9 @@
 		kfree(vd);
 		return -ENOMEM;
 	}
+	
+	memset(t, 0, sizeof(*t));
+	
 	for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) 
 	{
 		memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
@@ -680,6 +683,7 @@
 	saa5249_release,
 	saa5249_read,
 	saa5249_write,
+	NULL,	/* poll */
 	saa5249_ioctl,
 	NULL,
 	NULL,
diff -u --recursive --new-file v2.1.119/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.1.119/linux/drivers/char/serial.c	Wed Jul  1 19:38:54 1998
+++ linux/drivers/char/serial.c	Tue Sep  1 10:32:13 1998
@@ -2569,15 +2569,21 @@
 
 	MOD_INC_USE_COUNT;
 	line = MINOR(tty->device) - tty->driver.minor_start;
-	if ((line < 0) || (line >= NR_PORTS))
+	if ((line < 0) || (line >= NR_PORTS)) {
+		MOD_DEC_USE_COUNT;
 		return -ENODEV;
+	}
 	retval = get_async_struct(line, &info);
-	if (retval)
+	if (retval) {
+		MOD_DEC_USE_COUNT;
 		return retval;
+	}
 	tty->driver_data = info;
 	info->tty = tty;
-	if (serial_paranoia_check(info, tty->device, "rs_open"))
+	if (serial_paranoia_check(info, tty->device, "rs_open")) {
+		MOD_DEC_USE_COUNT;
 		return -ENODEV;
+	}
 
 #ifdef SERIAL_DEBUG_OPEN
 	printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
@@ -2587,8 +2593,10 @@
 
 	if (!tmp_buf) {
 		page = get_free_page(GFP_KERNEL);
-		if (!page)
+		if (!page) {
+			MOD_DEC_USE_COUNT;
 			return -ENOMEM;
+		}
 		if (tmp_buf)
 			free_page(page);
 		else
@@ -2602,6 +2610,7 @@
 	    (info->flags & ASYNC_CLOSING)) {
 		if (info->flags & ASYNC_CLOSING)
 			interruptible_sleep_on(&info->close_wait);
+		MOD_DEC_USE_COUNT;
 #ifdef SERIAL_DO_RESTART
 		return ((info->flags & ASYNC_HUP_NOTIFY) ?
 			-EAGAIN : -ERESTARTSYS);
@@ -2614,11 +2623,14 @@
 	 * Start up serial port
 	 */
 	retval = startup(info);
-	if (retval)
+	if (retval) {
+		MOD_DEC_USE_COUNT;
 		return retval;
+	}
 
 	retval = block_til_ready(tty, filp, info);
 	if (retval) {
+		MOD_DEC_USE_COUNT;
 #ifdef SERIAL_DEBUG_OPEN
 		printk("rs_open returning after block_til_ready with %d\n",
 		       retval);
diff -u --recursive --new-file v2.1.119/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v2.1.119/linux/drivers/net/3c59x.c	Tue Jul 21 00:15:31 1998
+++ linux/drivers/net/3c59x.c	Thu Aug 27 19:33:08 1998
@@ -15,14 +15,14 @@
 */
 
 static char *version =
-"3c59x.c:v0.99E 5/12/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
+"3c59x.c:v0.99F 8/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
 
 /* "Knobs" that adjust features and parameters. */
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1512 effectively disables this feature. */
-static const rx_copybreak = 200;
+static const int rx_copybreak = 200;
 /* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
-static const mtu = 1500;
+static const int mtu = 1500;
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 20;
 
@@ -37,9 +37,6 @@
    debugging. */
 static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0, rx_csumhits;
 
-/* Enable the automatic media selection code -- usually set. */
-#define AUTOMEDIA 1
-
 /* Allow the use of fragment bus master transfers instead of only
    programmed-I/O for Vortex cards.  Full-bus-master transfers are always
    enabled by default on Boomerang cards.  If VORTEX_BUS_MASTER is defined,
@@ -77,7 +74,9 @@
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
+#if LINUX_VERSION_CODE < 0x20155  ||  defined(CARDBUS)
 #include <linux/bios32.h>
+#endif
 #include <linux/timer.h>
 #include <asm/irq.h>			/* For NR_IRQS only. */
 #include <asm/bitops.h>
@@ -105,11 +104,6 @@
 #define RUN_AT(x) (jiffies + (x))
 #define DEV_ALLOC_SKB(len) dev_alloc_skb(len)
 #endif
-#if LINUX_VERSION_CODE < 0x20159
-#define DEV_FREE_SKB(skb) dev_kfree_skb (skb, FREE_WRITE);
-#else  /* Grrr, unneeded incompatible change. */
-#define DEV_FREE_SKB(skb) dev_kfree_skb(skb);
-#endif
 
 #ifdef SA_SHIRQ
 #define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev)
@@ -128,9 +122,27 @@
 #define udelay(microsec)	do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
 #endif
 
+#if LINUX_VERSION_CODE <= 0x20139
+#define	net_device_stats enet_statistics
+#define NETSTATS_VER2
+#endif
 #if LINUX_VERSION_CODE < 0x20138
 #define test_and_set_bit(val, addr) set_bit(val, addr)
+#define le32_to_cpu(val) (val)
+#define cpu_to_le32(val) (val)
 #endif
+#if LINUX_VERSION_CODE < 0x20155
+#define PCI_SUPPORT_VER1
+#else
+#define PCI_SUPPORT_VER2
+#endif
+#if LINUX_VERSION_CODE < 0x20159
+#define DEV_FREE_SKB(skb) dev_kfree_skb (skb, FREE_WRITE);
+#else  /* Grrr, unneeded incompatible change. */
+#define DEV_FREE_SKB(skb) dev_kfree_skb(skb);
+#endif
+
+
 #if defined(MODULE) && (LINUX_VERSION_CODE >= 0x20115)
 MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
 MODULE_DESCRIPTION("3Com 3c590/3c900 series Vortex/Boomerang driver");
@@ -141,7 +153,7 @@
 MODULE_PARM(max_interrupt_work, "i");
 MODULE_PARM(compaq_ioaddr, "i");
 MODULE_PARM(compaq_irq, "i");
-MODULE_PARM(compaq_prod_id, "i");
+MODULE_PARM(compaq_device_id, "i");
 #endif
 
 /* Operational parameter that usually are not changed. */
@@ -166,7 +178,7 @@
 /* Caution!  These entries must be consistent. */
 static const int product_ids[] = {
 	0x5900, 0x5920, 0x5970, 0x5950, 0x5951, 0x5952, 0x9000, 0x9001,
-	0x9050, 0x9051, 0x9055,	0x5057, 0 };
+	0x9050, 0x9051, 0x9055,	0x5057, 0x5175, 0 };
 static const char *product_names[] = {
 	"3c590 Vortex 10Mbps",
 	"3c592 EISA 10mbps Demon/Vortex",
@@ -180,6 +192,7 @@
 	"3c905 Boomerang 100baseT4",
 	"3c905B Cyclone 100baseTx",
 	"3c575",						/* Cardbus Boomerang */
+	"3CCFE575",						/* Cardbus ?Cyclone? */
 };
 
 /*
@@ -192,17 +205,16 @@
 versions of the FastEtherLink cards.  The supported product IDs are
   3c590, 3c592, 3c595, 3c597, 3c900, 3c905
 
-The ISA 3c515 is supported with a seperate driver, 3c515.c, included with
-the kernel source or available from
+The related ISA 3c515 is supported with a separate driver, 3c515.c, included
+with the kernel source or available from
     cesdis.gsfc.nasa.gov:/pub/linux/drivers/3c515.html
 
 II. Board-specific settings
 
 PCI bus devices are configured by the system at boot time, so no jumpers
 need to be set on the board.  The system BIOS should be set to assign the
-PCI INTA signal to an otherwise unused system IRQ line.  While it's
-physically possible to shared PCI interrupt lines, the 1.2.0 kernel doesn't
-support it.
+PCI INTA signal to an otherwise unused system IRQ line.  Note: The 1.2.*
+kernels did not support PCI interrupt sharing.
 
 III. Driver operation
 
@@ -210,10 +222,10 @@
 series.  The primary interface is two programmed-I/O FIFOs, with an
 alternate single-contiguous-region bus-master transfer (see next).
 
-The 3c900 "Boomerang" series uses a full-bus-master interface with seperate
+The 3c900 "Boomerang" series uses a full-bus-master interface with separate
 lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
 DEC Tulip and Intel Speedo3.  The first chip version retains a compatible
-programmed-I/O interface that will be removed in the 'B' and subsequent
+programmed-I/O interface that has been removed in 'B' and subsequent board
 revisions.
 
 One extension that is advertised in a very large font is that the adapters
@@ -231,7 +243,7 @@
 single frame.
 
 With full-bus-master support, this driver uses a "RX_COPYBREAK" scheme.
-Tather than a fixed intermediate receive buffer, this scheme allocates
+Rather than a fixed intermediate receive buffer, this scheme allocates
 full-sized skbuffs as receive buffers.  The value RX_COPYBREAK is used as
 the copying breakpoint: it is chosen to trade-off the memory wasted by
 passing the full-sized skbuff to the queue layer for all frames vs. the
@@ -404,7 +416,7 @@
 	struct sk_buff* tx_skbuff[TX_RING_SIZE];
 	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
 	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
-	struct enet_statistics stats;
+	struct net_device_stats stats;
 	struct sk_buff *tx_skb;		/* Packet being eaten by bus master ctrl.  */
 
 	/* PCI configuration space information. */
@@ -412,17 +424,16 @@
 	u16 pci_device_id;
 
 	/* The remainder are related to chip state, mostly media selection. */
-	int in_interrupt;
+	unsigned long in_interrupt;
 	struct timer_list timer;	/* Media selection timer. */
 	int options;				/* User-settable misc. driver options. */
-	unsigned int
-	  media_override:3, 			/* Passed-in media type. */
-	  default_media:3,				/* Read from the EEPROM/Wn3_Config. */
-	  full_duplex:1, autoselect:1,
-	  bus_master:1,				/* Vortex can only do a fragment bus-m. */
-	  full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang  */
-	  hw_csums:1,				/* Has hardware checksums. */
-      tx_full:1;
+	unsigned int media_override:3, 			/* Passed-in media type. */
+		default_media:4,				/* Read from the EEPROM/Wn3_Config. */
+		full_duplex:1, force_fd:1, autoselect:1,
+		bus_master:1,				/* Vortex can only do a fragment bus-m. */
+		full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang  */
+		hw_csums:1,				/* Has hardware checksums. */
+		tx_full:1;
 	u16 status_enable;
 	u16 available_media;				/* From Wn3_Options. */
 	u16 capabilities, info1, info2;		/* Various, from EEPROM. */
@@ -459,15 +470,15 @@
 };
 
 static int vortex_scan(struct device *dev);
-static struct device *vortex_found_device(struct device *dev, int ioaddr,
+static struct device *vortex_found_device(struct device *dev, long ioaddr,
 										  int irq, int device_id,
 										  int options, int card_idx);
 static int vortex_probe1(struct device *dev);
 static int vortex_open(struct device *dev);
-static void mdio_sync(int ioaddr, int bits);
-static int mdio_read(int ioaddr, int phy_id, int location);
+static void mdio_sync(long ioaddr, int bits);
+static int mdio_read(long ioaddr, int phy_id, int location);
 #ifdef HAVE_PRIVATE_IOCTL
-static void mdio_write(int ioaddr, int phy_id, int location, int value);
+static void mdio_write(long ioaddr, int phy_id, int location, int value);
 #endif
 static void vortex_timer(unsigned long arg);
 static int vortex_start_xmit(struct sk_buff *skb, struct device *dev);
@@ -476,8 +487,8 @@
 static int boomerang_rx(struct device *dev);
 static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs);
 static int vortex_close(struct device *dev);
-static void update_stats(int addr, struct device *dev);
-static struct enet_statistics *vortex_get_stats(struct device *dev);
+static void update_stats(long ioaddr, struct device *dev);
+static struct net_device_stats *vortex_get_stats(struct device *dev);
 static void set_rx_mode(struct device *dev);
 #ifdef HAVE_PRIVATE_IOCTL
 static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd);
@@ -660,6 +671,24 @@
 			if (vendor != TCOM_VENDOR_ID)
 				continue;
 
+			/* Power-up the card. */
+			pcibios_read_config_word(pci_bus, pci_device_fn,
+										 0xe0, &pci_command);
+			if (pci_command & 0x3) {
+				/* Save the ioaddr and IRQ info! */
+				printk(KERN_INFO "  A 3Com network adapter is powered down!"
+					   "  Setting the power state %4.4x->%4.4x.\n",
+					   pci_command, pci_command & ~3);
+				pcibios_write_config_word(pci_bus, pci_device_fn,
+										  0xe0, pci_command & ~3);
+				printk(KERN_INFO "  Setting the IRQ to %d, IOADDR to %#lx.\n",
+					   irq, ioaddr);
+				pcibios_write_config_byte(pci_bus, pci_device_fn,
+										 PCI_INTERRUPT_LINE, irq);
+				pcibios_write_config_dword(pci_bus, pci_device_fn,
+										  PCI_BASE_ADDRESS_0, ioaddr);
+			}
+
 			if (ioaddr == 0) {
 				printk(KERN_WARNING "  A 3Com network adapter has been found, "
 					   "however it has not been assigned an I/O address.\n"
@@ -715,7 +744,7 @@
 
 	/* Now check all slots of the EISA bus. */
 	if (EISA_bus) {
-		static int ioaddr = 0x1000;
+		static long ioaddr = 0x1000;
 		for ( ; ioaddr < 0x9000; ioaddr += 0x1000) {
 			int device_id;
 			if (check_region(ioaddr, VORTEX_TOTAL_SIZE))
@@ -753,7 +782,7 @@
 }
 
 static struct device *
-vortex_found_device(struct device *dev, int ioaddr, int irq,
+vortex_found_device(struct device *dev, long ioaddr, int irq,
 					int device_id, int option, int card_idx)
 {
 	struct vortex_private *vp;
@@ -839,6 +868,7 @@
 		vp->full_duplex = 0;
 		vp->bus_master = 0;
 	}
+	vp->force_fd = vp->full_duplex;
 
 	vortex_probe1(dev);
 #endif /* MODULE */
@@ -847,13 +877,13 @@
 
 static int vortex_probe1(struct device *dev)
 {
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	u16 *ether_addr = (u16 *)dev->dev_addr;
 	unsigned int eeprom[0x40], checksum = 0;		/* EEPROM contents */
 	int i;
 
-	printk(KERN_INFO "%s: 3Com %s at %#3x,",
+	printk(KERN_INFO "%s: 3Com %s at %#3lx,",
 		   dev->name, vp->product_name, ioaddr);
 
 	/* Read the station address from the EEPROM. */
@@ -888,11 +918,15 @@
 		ether_addr[i] = htons(eeprom[i + 10]);
 	for (i = 0; i < 6; i++)
 		printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]);
+#ifdef __sparc__
+	printk(", IRQ %s\n", __irq_itoa(dev->irq));
+#else
 	printk(", IRQ %d\n", dev->irq);
 	/* Tell them about an invalid IRQ. */
 	if (vortex_debug && (dev->irq <= 0 || dev->irq >= NR_IRQS))
 		printk(KERN_WARNING " *** Warning: IRQ %d is unlikely to work! ***\n",
 			   dev->irq);
+#endif
 
 	/* Extract our information from the EEPROM data. */
 	vp->info1 = eeprom[13];
@@ -918,7 +952,7 @@
 			   config.u.ram_width ? "word" : "byte",
 			   ram_split[config.u.ram_split],
 			   config.u.autoselect ? "autoselect/" : "",
-			   config.u.xcvr ? "NWay Autonegotiation" :
+			   config.u.xcvr > XCVR_ExtMII ? "<invalid transceiver>" :
 			   media_tbl[config.u.xcvr].name);
 		vp->default_media = config.u.xcvr;
 		vp->autoselect = config.u.autoselect;
@@ -931,7 +965,7 @@
 	} else
 		dev->if_port = vp->default_media;
 
-	if (dev->if_port == XCVR_MII) {
+	if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) {
 		int phy, phy_idx = 0;
 		EL3WINDOW(4);
 		for (phy = 0; phy < 32 && phy_idx < sizeof(vp->phys); phy++) {
@@ -991,7 +1025,7 @@
 static int
 vortex_open(struct device *dev)
 {
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	union wn3_config config;
 	int i;
@@ -1011,6 +1045,8 @@
 		dev->if_port = XCVR_100baseTx;
 		while (! (vp->available_media & media_tbl[dev->if_port].mask))
 			dev->if_port = media_tbl[dev->if_port].next;
+		if (vp->phys[0])
+			dev->if_port = XCVR_NWAY;
 
 		if (vortex_debug > 1)
 			printk(KERN_DEBUG "%s: Initial media type %s.\n",
@@ -1019,15 +1055,16 @@
 		init_timer(&vp->timer);
 		vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
 		vp->timer.data = (unsigned long)dev;
-		vp->timer.function = &vortex_timer;    /* timer handler */
+		vp->timer.function = &vortex_timer;		/* timer handler */
 		add_timer(&vp->timer);
 	} else
 		dev->if_port = vp->default_media;
 
+	vp->full_duplex = vp->force_fd;
 	config.u.xcvr = dev->if_port;
 	outl(config.i, ioaddr + Wn3_Config);
 
-	if (dev->if_port == XCVR_MII) {
+	if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) {
 		int mii_reg1, mii_reg5;
 		EL3WINDOW(4);
 		/* Read BMSR (reg1) only to clear old status. */
@@ -1127,9 +1164,9 @@
 			printk(KERN_DEBUG "%s:  Filling in the Rx ring.\n", dev->name);
 		for (i = 0; i < RX_RING_SIZE; i++) {
 			struct sk_buff *skb;
-			vp->rx_ring[i].next = virt_to_bus(&vp->rx_ring[i+1]);
+			vp->rx_ring[i].next = cpu_to_le32(virt_to_bus(&vp->rx_ring[i+1]));
 			vp->rx_ring[i].status = 0;	/* Clear complete bit. */
-			vp->rx_ring[i].length = PKT_BUF_SZ | LAST_FRAG;
+			vp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ | LAST_FRAG);
 			skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
 			vp->rx_skbuff[i] = skb;
 			if (skb == NULL)
@@ -1137,12 +1174,13 @@
 			skb->dev = dev;			/* Mark as being used by this device. */
 #if LINUX_VERSION_CODE >= 0x10300
 			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-			vp->rx_ring[i].addr = virt_to_bus(skb->tail);
+			vp->rx_ring[i].addr = cpu_to_le32(virt_to_bus(skb->tail));
 #else
 			vp->rx_ring[i].addr = virt_to_bus(skb->data);
 #endif
 		}
-		vp->rx_ring[i-1].next = virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */
+		/* Wrap the ring. */
+		vp->rx_ring[i-1].next = cpu_to_le32(virt_to_bus(&vp->rx_ring[0]));
 		outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
 	}
 	if (vp->full_bus_master_tx) { 		/* Boomerang bus master Tx. */
@@ -1186,24 +1224,25 @@
 
 static void vortex_timer(unsigned long data)
 {
-#ifdef AUTOMEDIA
 	struct device *dev = (struct device *)data;
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	unsigned long flags;
+	int next_tick = 0;
 	int ok = 0;
+	int media_status, old_window;
 
 	if (vortex_debug > 1)
 		printk(KERN_DEBUG "%s: Media selection timer tick happened, %s.\n",
 			   dev->name, media_tbl[dev->if_port].name);
 
-	save_flags(flags);	cli(); {
-	  int old_window = inw(ioaddr + EL3_CMD) >> 13;
-	  int media_status;
-	  EL3WINDOW(4);
-	  media_status = inw(ioaddr + Wn4_Media);
-	  switch (dev->if_port) {
-	  case XCVR_10baseT:  case XCVR_100baseTx:  case XCVR_100baseFx:
+	save_flags(flags);
+	cli();
+	old_window = inw(ioaddr + EL3_CMD) >> 13;
+	EL3WINDOW(4);
+	media_status = inw(ioaddr + Wn4_Media);
+	switch (dev->if_port) {
+	case XCVR_10baseT:  case XCVR_100baseTx:  case XCVR_100baseFx:
 		if (media_status & Media_LnkBeat) {
 		  ok = 1;
 		  if (vortex_debug > 1)
@@ -1212,18 +1251,27 @@
 		} else if (vortex_debug > 1)
 		  printk(KERN_DEBUG "%s: Media %s is has no link beat, %x.\n",
 				   dev->name, media_tbl[dev->if_port].name, media_status);
-
 		break;
-	  case XCVR_MII:
-		  {
-			  int mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1);
+	  case XCVR_MII: case XCVR_NWAY:
+		  if (mdio_read(ioaddr, vp->phys[0], 1) & 0x0004) {
 			  int mii_reg5 = mdio_read(ioaddr, vp->phys[0], 5);
-			  if (vortex_debug > 1)
-				  printk(KERN_DEBUG "%s: MII #%d status register is %4.4x, "
-						 "link partner capability %4.4x.\n",
-						 dev->name, vp->phys[0], mii_reg1, mii_reg5);
-			  if (mii_reg1 & 0x0004)
-				  ok = 1;
+			  ok = 1;
+			  if (! vp->force_fd  &&  mii_reg5 != 0xffff) {
+				  int duplex = (mii_reg5&0x0100) ||
+					  (mii_reg5 & 0x01C0) == 0x0040;
+				  if (vp->full_duplex != duplex) {
+					  vp->full_duplex = duplex;
+					  printk(KERN_INFO "%s: Setting %s-duplex based on MII "
+							 "#%d link partner capability of %4.4x.\n",
+							 dev->name, vp->full_duplex ? "full" : "half",
+							 vp->phys[0], mii_reg5);
+					  /* Set the full-duplex bit. */
+					  outb((vp->full_duplex ? 0x20 : 0) |
+						   (dev->mtu > 1500 ? 0x40 : 0),
+						   ioaddr + Wn3_MAC_Ctrl);
+				  }
+				  next_tick = 60*HZ;
+			  }
 			  break;
 		  }
 	  default:					/* Other media types handled by Tx timeouts. */
@@ -1231,8 +1279,8 @@
 		  printk(KERN_DEBUG "%s: Media %s is has no indication, %x.\n",
 				 dev->name, media_tbl[dev->if_port].name, media_status);
 		ok = 1;
-	  }
-	  if ( ! ok) {
+	}
+	if ( ! ok) {
 		union wn3_config config;
 
 		do {
@@ -1249,8 +1297,7 @@
 			printk(KERN_DEBUG "%s: Media selection failed, now trying "
 				   "%s port.\n",
 				   dev->name, media_tbl[dev->if_port].name);
-		  vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
-		  add_timer(&vp->timer);
+		  next_tick = RUN_AT(media_tbl[dev->if_port].wait);
 		}
 		outw((media_status & ~(Media_10TP|Media_SQE)) |
 			 media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
@@ -1262,21 +1309,25 @@
 
 		outw(dev->if_port == XCVR_10base2 ? StartCoax : StopCoax,
 			 ioaddr + EL3_CMD);
-	  }
-	  EL3WINDOW(old_window);
-	}   restore_flags(flags);
+	}
+	EL3WINDOW(old_window);
+	restore_flags(flags);
+
 	if (vortex_debug > 1)
 	  printk(KERN_DEBUG "%s: Media selection timer finished, %s.\n",
 			 dev->name, media_tbl[dev->if_port].name);
 
-#endif /* AUTOMEDIA*/
+	if (next_tick) {
+		vp->timer.expires = RUN_AT(next_tick);
+		add_timer(&vp->timer);
+	}
 	return;
 }
 
 static void vortex_tx_timeout(struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int j;
 
 	printk(KERN_ERR "%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
@@ -1309,8 +1360,8 @@
 		for (i = 0; i < TX_RING_SIZE; i++) {
 			printk(KERN_DEBUG "  %d: @%p  length %8.8x status %8.8x\n", i,
 				   &vp->tx_ring[i],
-				   vp->tx_ring[i].length,
-				   vp->tx_ring[i].status);
+				   le32_to_cpu(vp->tx_ring[i].length),
+				   le32_to_cpu(vp->tx_ring[i].status));
 		}
 	}
 #endif
@@ -1340,14 +1391,14 @@
 }
 
 /*
- * Handle uncommon interrupt sources.  This is a seperate routine to minimize
+ * Handle uncommon interrupt sources.  This is a separate routine to minimize
  * the cache impact.
  */
 static void
 vortex_error(struct device *dev, int status)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int do_tx_reset = 0;
 	int i;
 
@@ -1434,7 +1485,7 @@
 vortex_start_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 
 	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
 		if (jiffies - dev->trans_start >= TX_TIMEOUT)
@@ -1506,7 +1557,7 @@
 boomerang_start_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 
 	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
 		if (jiffies - dev->trans_start >= TX_TIMEOUT)
@@ -1528,13 +1579,13 @@
 				printk(KERN_WARNING "%s: Tx Ring full, refusing to send buffer.\n",
 					   dev->name);
 			return 1;
-		} 
+		}
 		/* end change 06/25/97 M. Sievers */	
 		vp->tx_skbuff[entry] = skb;
 		vp->tx_ring[entry].next = 0;
-		vp->tx_ring[entry].addr = virt_to_bus(skb->data);
-		vp->tx_ring[entry].length = skb->len | LAST_FRAG;
-		vp->tx_ring[entry].status = skb->len | TxIntrUploaded;
+		vp->tx_ring[entry].addr = cpu_to_le32(virt_to_bus(skb->data));
+		vp->tx_ring[entry].length = cpu_to_le32(skb->len | LAST_FRAG);
+		vp->tx_ring[entry].status = cpu_to_le32(skb->len | TxIntrUploaded);
 
 		save_flags(flags);
 		cli();
@@ -1543,7 +1594,7 @@
 		for (i = 600; i >= 0 ; i--)
 			if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
 				break;
-		prev_entry->next = virt_to_bus(&vp->tx_ring[entry]);
+		prev_entry->next = cpu_to_le32(virt_to_bus(&vp->tx_ring[entry]));
 		if (inl(ioaddr + DownListPtr) == 0) {
 			outl(virt_to_bus(&vp->tx_ring[entry]), ioaddr + DownListPtr);
 			queued_packet++;
@@ -1555,7 +1606,7 @@
 		if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
 			vp->tx_full = 1;
 		else {					/* Clear previous interrupt enable. */
-			prev_entry->status &= ~TxIntrUploaded;
+			prev_entry->status &= cpu_to_le32(~TxIntrUploaded);
 			clear_bit(0, (void*)&dev->tbusy);
 		}
 		dev->trans_start = jiffies;
@@ -1573,8 +1624,8 @@
 	struct device *dev = (struct device *)(irq2dev_map[irq]);
 #endif
 	struct vortex_private *vp;
-	int ioaddr, status;
-	int latency;
+	long ioaddr;
+	int latency, status;
 	int work_done = max_interrupt_work;
 
 	vp = (struct vortex_private *)dev->priv;
@@ -1586,7 +1637,6 @@
 	dev->interrupt = 1;
 	ioaddr = dev->base_addr;
 	latency = inb(ioaddr + Timer);
-
 	status = inw(ioaddr + EL3_STATUS);
 
 	if (vortex_debug > 4)
@@ -1681,7 +1731,7 @@
 vortex_rx(struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int i;
 	short rx_status;
 
@@ -1751,7 +1801,7 @@
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	int entry = vp->cur_rx % RX_RING_SIZE;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int rx_status;
 	int rx_work_limit = vp->dirty_rx + RX_RING_SIZE - vp->cur_rx;
 
@@ -1759,8 +1809,7 @@
 		printk(KERN_DEBUG "  In boomerang_rx(), status %4.4x, rx_status "
 			   "%4.4x.\n",
 			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
-	while ((--rx_work_limit >= 0) &&
-			((rx_status = vp->rx_ring[entry].status) & RxDComplete)) {
+	while ((rx_status = le32_to_cpu(vp->rx_ring[entry].status)) & RxDComplete) {
 		if (rx_status & RxDError) { /* Error, update stats. */
 			unsigned char rx_error = rx_status >> 16;
 			if (vortex_debug > 2)
@@ -1789,21 +1838,18 @@
 				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 				/* 'skb_put()' points to the start of sk_buff data area. */
 				memcpy(skb_put(skb, pkt_len),
-					   bus_to_virt(vp->rx_ring[entry].addr),
+					   bus_to_virt(le32_to_cpu(vp->rx_ring[entry].addr)),
 					   pkt_len);
 #else
-				memcpy(skb->data, bus_to_virt(vp->rx_ring[entry].addr), pkt_len);
+				memcpy(skb->data, bus_to_virt(vp->rx_ring[entry].addr),
+					   pkt_len);
 				skb->len = pkt_len;
 #endif
 				rx_copy++;
-			} else{
+			} else {
 				void *temp;
 				/* Pass up the skbuff already on the Rx ring. */
 				skb = vp->rx_skbuff[entry];
-				if (skb == NULL) {
-					printk(KERN_WARNING "%s: in boomerang_rx -- attempt to use NULL skb caught\n", dev->name);
-					break;
-				}
 				vp->rx_skbuff[entry] = NULL;
 #if LINUX_VERSION_CODE >= 0x10300
 				temp = skb_put(skb, pkt_len);
@@ -1811,10 +1857,11 @@
 				temp = skb->data;
 #endif
 				/* Remove this checking code for final release. */
-				if (bus_to_virt(vp->rx_ring[entry].addr) != temp)
+				if (bus_to_virt(le32_to_cpu(vp->rx_ring[entry].addr)) != temp)
 					printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match"
 						   " in boomerang_rx: %p vs. %p.\n", dev->name,
-						   bus_to_virt(vp->rx_ring[entry].addr), temp);
+						   bus_to_virt(le32_to_cpu(vp->rx_ring[entry].addr)),
+						   temp);
 				rx_nocopy++;
 			}
 #if LINUX_VERSION_CODE > 0x10300
@@ -1836,6 +1883,8 @@
 			vp->stats.rx_packets++;
 		}
 		entry = (++vp->cur_rx) % RX_RING_SIZE;
+		if (--rx_work_limit < 0)
+			break;
 	}
 	/* Refill the Rx ring buffers. */
 	for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) {
@@ -1843,14 +1892,12 @@
 		entry = vp->dirty_rx % RX_RING_SIZE;
 		if (vp->rx_skbuff[entry] == NULL) {
 			skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
-			if (skb == NULL) {
-				printk(KERN_DEBUG "%s: in boomerang_rx -- could not allocate skbuff\n", dev->name);
+			if (skb == NULL)
 				break;			/* Bad news!  */
-			}
 			skb->dev = dev;			/* Mark as being used by this device. */
 #if LINUX_VERSION_CODE > 0x10300
 			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-			vp->rx_ring[entry].addr = virt_to_bus(skb->tail);
+			vp->rx_ring[entry].addr = cpu_to_le32(virt_to_bus(skb->tail));
 #else
 			vp->rx_ring[entry].addr = virt_to_bus(skb->data);
 #endif
@@ -1859,12 +1906,6 @@
 		vp->rx_ring[entry].status = 0;	/* Clear complete bit. */
 		outw(UpUnstall, ioaddr + EL3_CMD);
 	}
-
-	if (vp->dirty_rx >= RX_RING_SIZE ) {
-		vp->cur_rx -= RX_RING_SIZE;
-		vp->dirty_rx -= RX_RING_SIZE;
-	}
-
 	return 0;
 }
 
@@ -1872,7 +1913,7 @@
 vortex_close(struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int i;
 
 	dev->start = 0;
@@ -1934,8 +1975,7 @@
 	return 0;
 }
 
-static struct enet_statistics *
-vortex_get_stats(struct device *dev)
+static struct net_device_stats *vortex_get_stats(struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 	unsigned long flags;
@@ -1956,7 +1996,7 @@
 	table.  This is done by checking that the ASM (!) code generated uses
 	atomic updates with '+='.
 	*/
-static void update_stats(int ioaddr, struct device *dev)
+static void update_stats(long ioaddr, struct device *dev)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
 
@@ -1991,7 +2031,7 @@
 static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd)
 {
 	struct vortex_private *vp = (struct vortex_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	u16 *data = (u16 *)&rq->ifr_data;
 	int phy = vp->phys[0] & 0x1f;
 
@@ -2000,7 +2040,7 @@
 			   dev->name, rq->ifr_ifrn.ifrn_name, cmd,
 			   data[0], data[1], data[2], data[3]);
 
-    switch(cmd) {
+	switch(cmd) {
 	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
 		data[0] = phy;
 	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
@@ -2025,7 +2065,7 @@
 static void
 set_rx_mode(struct device *dev)
 {
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int new_mode;
 
 	if (dev->flags & IFF_PROMISC) {
@@ -2068,11 +2108,11 @@
 
 /* Generate the preamble required for initial synchronization and
    a few older transceivers. */
-static void mdio_sync(int ioaddr, int bits)
+static void mdio_sync(long ioaddr, int bits)
 {
-	int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+	long mdio_addr = ioaddr + Wn4_PhysicalMgmt;
 
-	/* Establish sync by sending at least 32 logic ones. */ 
+	/* Establish sync by sending at least 32 logic ones. */
 	while (-- bits >= 0) {
 		outw(MDIO_DATA_WRITE1, mdio_addr);
 		mdio_delay();
@@ -2081,12 +2121,12 @@
 	}
 }
 
-static int mdio_read(int ioaddr, int phy_id, int location)
+static int mdio_read(long ioaddr, int phy_id, int location)
 {
 	int i;
 	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
 	unsigned int retval = 0;
-	int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+	long mdio_addr = ioaddr + Wn4_PhysicalMgmt;
 
 	if (mii_preamble_required)
 		mdio_sync(ioaddr, 32);
@@ -2110,10 +2150,10 @@
 	return retval>>1 & 0xffff;
 }
 
-static void mdio_write(int ioaddr, int phy_id, int location, int value)
+static void mdio_write(long ioaddr, int phy_id, int location, int value)
 {
 	int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
-	int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+	long mdio_addr = ioaddr + Wn4_PhysicalMgmt;
 	int i;
 
 	if (mii_preamble_required)
@@ -2166,7 +2206,7 @@
  * Local variables:
  *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
  *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c"
- *  compile-command-alt1: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c -o 3c59x_cb.o"
+ *  cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c -o 3c575_cb.o -I/usr/src/pcmcia-cs-3.0.5/include/"
  *  c-indent-level: 4
  *  c-basic-offset: 4
  *  tab-width: 4
diff -u --recursive --new-file v2.1.119/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c
--- v2.1.119/linux/drivers/net/arcnet.c	Tue Jun  9 11:57:29 1998
+++ linux/drivers/net/arcnet.c	Thu Sep  3 15:58:37 1998
@@ -149,8 +149,8 @@
            retransmit immediately)
          - Add support for the new 1.3.x IP header cache, and other features.
          - Replace setting of debug level with the "metric" flag hack by
-	   something better. SIOCDEVPRIVATE is a good candidate, but it would
-	   require an extra user-level utility.
+	   something that still exists. SIOCDEVPRIVATE is a good candidate, 
+	   but it would require an extra user-level utility.
 
          - What about cards with shared memory that can be "turned off?"
            (or that have none at all, like the SMC PC500longboard)
@@ -428,13 +428,13 @@
 {
   struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
 
-  if (dev->metric>=1000)
-    {
-      arcnet_debug=dev->metric-1000;
-      printk(KERN_INFO "%6s: debug level set to %d\n",dev->name,arcnet_debug);
-      dev->metric=1;
-    }
-
+  /*  if (dev->metric>=1000)
+   *  {
+   *  arcnet_debug=dev->metric-1000;
+   *  printk(KERN_INFO "%6s: debug level set to %d\n",dev->name,arcnet_debug);
+   *  dev->metric=1;
+   *}
+   */
   BUGMSG(D_INIT,"arcnet_open: resetting card.\n");
 
   /* try to put the card in a defined state - if it fails the first
diff -u --recursive --new-file v2.1.119/linux/drivers/net/bmac.c linux/drivers/net/bmac.c
--- v2.1.119/linux/drivers/net/bmac.c	Mon Aug  3 12:45:45 1998
+++ linux/drivers/net/bmac.c	Thu Aug 27 19:33:08 1998
@@ -27,6 +27,9 @@
  */
 #define ENET_CRCPOLY 0x04c11db7
 
+/* switch to use multicast code lifted from sunhme driver */
+#define SUNHME_MULTICAST
+
 /* a bunch of constants for the "Heathrow" interrupt controller.
    These really should be in an include file somewhere */
 #define IoBaseHeathrow ((unsigned *)0xf3000000)
@@ -51,7 +54,7 @@
 #define XXDEBUG(args)
 
 struct bmac_data {
-/*     volatile struct bmac *bmac; */
+	/* volatile struct bmac *bmac; */
 	struct sk_buff_head *queue;
 	volatile struct dbdma_regs *tx_dma;
 	int tx_dma_intr;
@@ -82,7 +85,7 @@
 	unsigned short reg_offset;
 } bmac_reg_entry_t;
 
-#define N_REG_ENTRIES 30
+#define N_REG_ENTRIES 31
 
 bmac_reg_entry_t reg_entries[N_REG_ENTRIES] = {
 	{"MEMADD", MEMADD},
@@ -98,6 +101,7 @@
 	{"PAPAT", PAPAT},
 	{"TXSFD", TXSFD},
 	{"JAM", JAM},
+	{"TXCFG", TXCFG},
 	{"TXMAX", TXMAX},
 	{"TXMIN", TXMIN},
 	{"PAREG", PAREG},
@@ -133,8 +137,8 @@
  * buffers on a 16 byte boundary.
  */
 #define PRIV_BYTES	(sizeof(struct bmac_data) \
-			 + (N_RX_RING + N_TX_RING + 4) * sizeof(struct dbdma_cmd) \
-			 + sizeof(struct sk_buff_head))
+	+ (N_RX_RING + N_TX_RING + 4) * sizeof(struct dbdma_cmd) \
+	+ sizeof(struct sk_buff_head))
 
 static unsigned char bitrev(unsigned char b);
 static int bmac_open(struct device *dev);
@@ -263,6 +267,7 @@
 	udelay(50000);
 	
 	out_le32(heathrowFCR, fcrValue);
+	udelay(50000);
 }
 
 static void
@@ -273,7 +278,7 @@
 	unsigned short *pWord16;
 	int i;
 
-/* XXDEBUG(("bmac: enter init_registers\n")); */
+	/* XXDEBUG(("bmac: enter init_registers\n")); */
 
 	bmwrite(dev, TXRST, TxResetBit);
 
@@ -468,6 +473,7 @@
 	if (!bp->tx_allocated) {
 		/* zero out tx cmds, alloc space for double buffering */
 		addr = (char *)kmalloc(ETHERMTU * N_TX_RING, GFP_DMA);
+		if (addr == NULL) return 0;
 		for (i = 0; i < N_TX_RING; i++, addr += ETHERMTU) bp->tx_double[i] = addr;
 		bp->tx_allocated = 1;
 	}
@@ -500,6 +506,7 @@
 	if (!bp->rx_allocated) {
 		for (i = 0; i < N_RX_RING; i++) {
 			bp->rx_bufs[i] = dev_alloc_skb(RX_BUFLEN+2);
+			if (bp->rx_bufs[i] == NULL) return 0;
 			skb_reserve(bp->rx_bufs[i], 2);
 		}
 		bp->rx_allocated = 1;
@@ -531,8 +538,8 @@
 	int i;
 
 	/* see if there's a free slot in the tx ring */
-/* XXDEBUG(("bmac_xmit_start: empty=%d fill=%d\n", */
-/* 	     bp->tx_empty, bp->tx_fill)); */
+	/* XXDEBUG(("bmac_xmit_start: empty=%d fill=%d\n", */
+	/* 	     bp->tx_empty, bp->tx_fill)); */
 	i = bp->tx_fill + 1;
 	if (i >= N_TX_RING) i = 0;
 	if (i == bp->tx_empty) {
@@ -637,8 +644,8 @@
 		XXDEBUG(("bmac_txdma_intr\n"));
 	}
 
-/*     del_timer(&bp->tx_timeout); */
-/*     bp->timeout_active = 0; */
+	/*     del_timer(&bp->tx_timeout); */
+	/*     bp->timeout_active = 0; */
 
 	while (1) {
 		cp = &bp->tx_cmds[bp->tx_empty];
@@ -655,8 +662,8 @@
 		bp->tx_bufs[bp->tx_empty] = NULL;
 		bp->tx_fullup = 0;
 		dev->tbusy = 0;
-/* XXDEBUG(("bmac_intr: cleared tbusy, empty=%d fill=%d\n", */
-/* 		 i, bp->tx_fill)); */
+		/* XXDEBUG(("bmac_intr: cleared tbusy, empty=%d fill=%d\n", */
+		/* 		 i, bp->tx_fill)); */
 		mark_bh(NET_BH);
 		if (++bp->tx_empty >= N_TX_RING) bp->tx_empty = 0;
 		if (bp->tx_empty == bp->tx_fill) break;
@@ -678,7 +685,7 @@
 	return &p->stats;
 }
 
-#if 0
+#ifndef SUNHME_MULTICAST
 /* Real fast bit-reversal algorithm, 6-bit values */
 static int reverse6[64] = {
 	0x0,0x20,0x10,0x30,0x8,0x28,0x18,0x38,
@@ -743,98 +750,98 @@
 	unsigned int	 crc;
 	unsigned short	 mask;
 
-	if (!(*addr 
-	      crc = bmac_crc((unsigned short *)addr) & 0x3f; /* Big-endian alert! */
-	      crc = reverse6[crc];	/* Hyperfast bit-reversing algorithm */
-	      if (bp->hash_use_count[crc]++) return; /* This bit is already set */
-	      mask = crc % 16;
-	      mask = (unsigned char)1 << mask;
-	      bp->hash_use_count[crc/16] |= mask;
-	      }
-
-	    static void
-	    bmac_removehash(struct bmac_data *bp, unsigned char *addr)
-	    {	
-		    unsigned int crc;
-		    unsigned char mask;
-
-		    /* Now, delete the address from the filter copy, as indicated */
-		    crc = bmac_crc((unsigned short *)addr) & 0x3f; /* Big-endian alert! */
-		    crc = reverse6[crc];	/* Hyperfast bit-reversing algorithm */
-		    if (bp->hash_use_count[crc] == 0) return; /* That bit wasn't in use! */
-		    if (--bp->hash_use_count[crc]) return; /* That bit is still in use */
-		    mask = crc % 16;
-		    mask = ((unsigned char)1 << mask) ^ 0xffff; /* To turn off bit */
-		    bp->hash_table_mask[crc/16] &= mask;
-	    }
+	if (!(*addr)) return;
+	crc = bmac_crc((unsigned short *)addr) & 0x3f; /* Big-endian alert! */
+	crc = reverse6[crc];	/* Hyperfast bit-reversing algorithm */
+	if (bp->hash_use_count[crc]++) return; /* This bit is already set */
+	mask = crc % 16;
+	mask = (unsigned char)1 << mask;
+	bp->hash_use_count[crc/16] |= mask;
+}
+
+static void
+bmac_removehash(struct bmac_data *bp, unsigned char *addr)
+{	
+	unsigned int crc;
+	unsigned char mask;
+
+	/* Now, delete the address from the filter copy, as indicated */
+	crc = bmac_crc((unsigned short *)addr) & 0x3f; /* Big-endian alert! */
+	crc = reverse6[crc];	/* Hyperfast bit-reversing algorithm */
+	if (bp->hash_use_count[crc] == 0) return; /* That bit wasn't in use! */
+	if (--bp->hash_use_count[crc]) return; /* That bit is still in use */
+	mask = crc % 16;
+	mask = ((unsigned char)1 << mask) ^ 0xffff; /* To turn off bit */
+	bp->hash_table_mask[crc/16] &= mask;
+}
 
 /*
  * Sync the adapter with the software copy of the multicast mask
  *  (logical address filter).
  */
 
-		    static void
-		    bmac_rx_off(struct device *dev)
-		    {
-			    unsigned short rx_cfg;
-
-			    rx_cfg = bmread(dev, RXCFG);
-			    rx_cfg &= ~RxMACEnable;
-			    bmwrite(dev, RXCFG, rx_cfg);
-			    do {
-				    rx_cfg = bmread(dev, RXCFG);
-			    }  while (rx_cfg & RxMACEnable);
-		    }
-
-			    unsigned short
-			    bmac_rx_on(struct device *dev, int hash_enable, int promisc_enable)
-			    {
-				    unsigned short rx_cfg;
-
-				    rx_cfg = bmread(dev, RXCFG);
-				    rx_cfg |= RxMACEnable;
-				    if (hash_enable) rx_cfg |= RxHashFilterEnable;
-				    else rx_cfg &= ~RxHashFilterEnable;
-				    if (promisc_enable) rx_cfg |= RxPromiscEnable;
-				    else rx_cfg &= ~RxPromiscEnable;
-				    bmwrite(dev, RXRST, RxResetValue);
-				    bmwrite(dev, RXFIFOCSR, 0);	/* first disable rxFIFO */
-				    bmwrite(dev, RXFIFOCSR, RxFIFOEnable ); 
-				    bmwrite(dev, RXCFG, rx_cfg );
-				    return rx_cfg;
-			    }
-
-				    static void
-				    bmac_update_hash_table_mask(struct device *dev, struct bmac_data *bp)
-				    {
-					    bmwrite(dev, BHASH3, bp->hash_table_mask[0]); /* bits 15 - 0 */
-					    bmwrite(dev, BHASH2, bp->hash_table_mask[1]); /* bits 31 - 16 */
-					    bmwrite(dev, BHASH1, bp->hash_table_mask[2]); /* bits 47 - 32 */
-					    bmwrite(dev, BHASH0, bp->hash_table_mask[3]); /* bits 63 - 48 */
-				    }
+static void
+bmac_rx_off(struct device *dev)
+{
+	unsigned short rx_cfg;
+
+	rx_cfg = bmread(dev, RXCFG);
+	rx_cfg &= ~RxMACEnable;
+	bmwrite(dev, RXCFG, rx_cfg);
+	do {
+		rx_cfg = bmread(dev, RXCFG);
+	}  while (rx_cfg & RxMACEnable);
+}
+
+unsigned short
+bmac_rx_on(struct device *dev, int hash_enable, int promisc_enable)
+{
+	unsigned short rx_cfg;
+
+	rx_cfg = bmread(dev, RXCFG);
+	rx_cfg |= RxMACEnable;
+	if (hash_enable) rx_cfg |= RxHashFilterEnable;
+	else rx_cfg &= ~RxHashFilterEnable;
+	if (promisc_enable) rx_cfg |= RxPromiscEnable;
+	else rx_cfg &= ~RxPromiscEnable;
+	bmwrite(dev, RXRST, RxResetValue);
+	bmwrite(dev, RXFIFOCSR, 0);	/* first disable rxFIFO */
+	bmwrite(dev, RXFIFOCSR, RxFIFOEnable ); 
+	bmwrite(dev, RXCFG, rx_cfg );
+	return rx_cfg;
+}
+
+static void
+bmac_update_hash_table_mask(struct device *dev, struct bmac_data *bp)
+{
+	bmwrite(dev, BHASH3, bp->hash_table_mask[0]); /* bits 15 - 0 */
+	bmwrite(dev, BHASH2, bp->hash_table_mask[1]); /* bits 31 - 16 */
+	bmwrite(dev, BHASH1, bp->hash_table_mask[2]); /* bits 47 - 32 */
+	bmwrite(dev, BHASH0, bp->hash_table_mask[3]); /* bits 63 - 48 */
+}
 
 #if 0
-					    static void
-					    bmac_add_multi(struct device *dev,
-							   struct bmac_data *bp, unsigned char *addr)
-					    {
-/* XXDEBUG(("bmac: enter bmac_add_multi\n")); */
-						    bmac_addhash(bp, addr);
-						    bmac_rx_off(dev);
-						    bmac_update_hash_table_mask(dev, bp);
-						    bmac_rx_on(dev, 1, (dev->flags & IFF_PROMISC)? 1 : 0);
-/* XXDEBUG(("bmac: exit bmac_add_multi\n")); */
-					    }
-
-						    static void
-						    bmac_remove_multi(struct device *dev,
-								      struct bmac_data *bp, unsigned char *addr)
-						    {
-							    bmac_removehash(bp, addr);
-							    bmac_rx_off(dev);
-							    bmac_update_hash_table_mask(dev, bp);
-							    bmac_rx_on(dev, 1, (dev->flags & IFF_PROMISC)? 1 : 0);
-						    }
+static void
+bmac_add_multi(struct device *dev,
+	       struct bmac_data *bp, unsigned char *addr)
+{
+	/* XXDEBUG(("bmac: enter bmac_add_multi\n")); */
+	bmac_addhash(bp, addr);
+	bmac_rx_off(dev);
+	bmac_update_hash_table_mask(dev, bp);
+	bmac_rx_on(dev, 1, (dev->flags & IFF_PROMISC)? 1 : 0);
+	/* XXDEBUG(("bmac: exit bmac_add_multi\n")); */
+}
+
+static void
+bmac_remove_multi(struct device *dev,
+		  struct bmac_data *bp, unsigned char *addr)
+{
+	bmac_removehash(bp, addr);
+	bmac_rx_off(dev);
+	bmac_update_hash_table_mask(dev, bp);
+	bmac_rx_on(dev, 1, (dev->flags & IFF_PROMISC)? 1 : 0);
+}
 #endif
 
 /* Set or clear the multicast filter for this adaptor.
@@ -843,134 +850,138 @@
     num_addrs > 0	Multicast mode, receive normal and MC packets, and do
 			best-effort filtering.
  */
-							    static void bmac_set_multicast(struct device *dev)
-							    {
-								    struct dev_mc_list *dmi;
-								    struct bmac_data *bp = (struct bmac_data *) dev->priv;
-								    int num_addrs = dev->mc_count;
-								    unsigned short rx_cfg;
-								    int i;
-
-								    XXDEBUG(("bmac: enter bmac_set_multicast, n_addrs=%d\n", num_addrs));
-
-								    if((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) {
-									    for (i=0; i<4; i++) bp->hash_table_mask[i] = 0xffff;
-									    bmac_update_hash_table_mask(dev, bp);
-									    rx_cfg = bmac_rx_on(dev, 1, 0);
-									    XXDEBUG(("bmac: all multi, rx_cfg=%#08x\n"));
-								    } else if if ((dev->flags & IFF_PROMISC) || (num_addrs < 0)) {
-									    rx_cfg = bmread(dev, RXCFG);
-									    rx_cfg |= RxPromiscEnable;
-									    bmwrite(dev, RXCFG, rx_cfg);
-									    rx_cfg = bmac_rx_on(dev, 0, 1);
-									    XXDEBUG(("bmac: promisc mode enabled, rx_cfg=%#08x\n", rx_cfg));
-								    } else {
-									    for (i=0; i<4; i++) bp->hash_table_mask[i] = 0;
-									    for (i=0; i<64; i++) bp->hash_use_count[i] = 0;
-									    if (num_addrs == 0) {
-										    rx_cfg = bmac_rx_on(dev, 0, 0);
-										    XXDEBUG(("bmac: multi disabled, rx_cfg=%#08x\n", rx_cfg));
-									    } else {
-										    for (dmi=dev->mc_list; dmi!=NULL; dmi=dmi->next)
-											    bmac_addhash(bp, dmi->dmi_addr);
-										    bmac_update_hash_table_mask(dev, bp);
-										    rx_cfg = bmac_rx_on(dev, 1, 0);
-										    XXDEBUG(("bmac: multi enabled, rx_cfg=%#08x\n", rx_cfg));
-									    }
-								    }
-/* XXDEBUG(("bmac: exit bmac_set_multicast\n")); */
-							    }
-#endif
+static void bmac_set_multicast(struct device *dev)
+{
+	struct dev_mc_list *dmi;
+	struct bmac_data *bp = (struct bmac_data *) dev->priv;
+	int num_addrs = dev->mc_count;
+	unsigned short rx_cfg;
+	int i;
+
+	XXDEBUG(("bmac: enter bmac_set_multicast, n_addrs=%d\n", num_addrs));
+
+	if((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) {
+		for (i=0; i<4; i++) bp->hash_table_mask[i] = 0xffff;
+		bmac_update_hash_table_mask(dev, bp);
+		rx_cfg = bmac_rx_on(dev, 1, 0);
+		XXDEBUG(("bmac: all multi, rx_cfg=%#08x\n"));
+	} else if ((dev->flags & IFF_PROMISC) || (num_addrs < 0)) {
+		rx_cfg = bmread(dev, RXCFG);
+		rx_cfg |= RxPromiscEnable;
+		bmwrite(dev, RXCFG, rx_cfg);
+		rx_cfg = bmac_rx_on(dev, 0, 1);
+		XXDEBUG(("bmac: promisc mode enabled, rx_cfg=%#08x\n", rx_cfg));
+	} else {
+		for (i=0; i<4; i++) bp->hash_table_mask[i] = 0;
+		for (i=0; i<64; i++) bp->hash_use_count[i] = 0;
+		if (num_addrs == 0) {
+			rx_cfg = bmac_rx_on(dev, 0, 0);
+			XXDEBUG(("bmac: multi disabled, rx_cfg=%#08x\n", rx_cfg));
+		} else {
+			for (dmi=dev->mc_list; dmi!=NULL; dmi=dmi->next)
+				bmac_addhash(bp, dmi->dmi_addr);
+			bmac_update_hash_table_mask(dev, bp);
+			rx_cfg = bmac_rx_on(dev, 1, 0);
+			XXDEBUG(("bmac: multi enabled, rx_cfg=%#08x\n", rx_cfg));
+		}
+	}
+	/* XXDEBUG(("bmac: exit bmac_set_multicast\n")); */
+}
+#else /* ifdef SUNHME_MULTICAST */
 
 /* The version of set_multicast below was lifted from sunhme.c */
 
 #define CRC_POLYNOMIAL_BE 0x04c11db7UL  /* Ethernet CRC, big endian */
 #define CRC_POLYNOMIAL_LE 0xedb88320UL  /* Ethernet CRC, little endian */
 
-								    static void bmac_set_multicast(struct device *dev)
-								    {
-									    struct dev_mc_list *dmi = dev->mc_list;
-									    char *addrs;
-									    int i, j, bit, byte;
-									    unsigned short rx_cfg;
-									    u32 crc, poly = CRC_POLYNOMIAL_LE;
-    
-									    /* Let the transmits drain. */
-/*     while(dev->tbusy) schedule(); */
-    
-									    /* Lock out others. */
-/*     set_bit(0, (void *) &dev->tbusy); */
-    
-									    if((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) {
-										    bmwrite(dev, BHASH0, 0xffff);
-										    bmwrite(dev, BHASH1, 0xffff);
-										    bmwrite(dev, BHASH2, 0xffff);
-										    bmwrite(dev, BHASH3, 0xffff);
-									    } else if(dev->flags & IFF_PROMISC) {
-										    rx_cfg = bmread(dev, RXCFG);
-										    rx_cfg |= RxPromiscEnable;
-										    bmwrite(dev, RXCFG, rx_cfg);
-									    } else {
-										    u16 hash_table[4];
+static void bmac_set_multicast(struct device *dev)
+{
+	struct dev_mc_list *dmi = dev->mc_list;
+	char *addrs;
+	int i, j, bit, byte;
+	unsigned short rx_cfg;
+	u32 crc, poly = CRC_POLYNOMIAL_LE;
+    
+	/* Let the transmits drain. */
+	/*     while(dev->tbusy) schedule(); */
+    
+	/* Lock out others. */
+	/*     set_bit(0, (void *) &dev->tbusy); */
+    
+	if((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 64)) {
+		bmwrite(dev, BHASH0, 0xffff);
+		bmwrite(dev, BHASH1, 0xffff);
+		bmwrite(dev, BHASH2, 0xffff);
+		bmwrite(dev, BHASH3, 0xffff);
+	} else if(dev->flags & IFF_PROMISC) {
+		rx_cfg = bmread(dev, RXCFG);
+		rx_cfg |= RxPromiscEnable;
+		bmwrite(dev, RXCFG, rx_cfg);
+	} else {
+		u16 hash_table[4];
 	
-										    for(i = 0; i < 4; i++) hash_table[i] = 0;
+		rx_cfg = bmread(dev, RXCFG);
+		rx_cfg &= ~RxPromiscEnable;
+		bmwrite(dev, RXCFG, rx_cfg);
+
+		for(i = 0; i < 4; i++) hash_table[i] = 0;
 	
-										    for(i = 0; i < dev->mc_count; i++) {
-											    addrs = dmi->dmi_addr;
-											    dmi = dmi->next;
+		for(i = 0; i < dev->mc_count; i++) {
+			addrs = dmi->dmi_addr;
+			dmi = dmi->next;
 	    
-											    if(!(*addrs & 1))
-												    continue;
+			if(!(*addrs & 1))
+				continue;
 	    
-											    crc = 0xffffffffU;
-											    for(byte = 0; byte < 6; byte++) {
-												    for(bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
-													    int test;
+			crc = 0xffffffffU;
+			for(byte = 0; byte < 6; byte++) {
+				for(bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
+					int test;
 		    
-													    test = ((bit ^ crc) & 0x01);
-													    crc >>= 1;
-													    if(test)
-														    crc = crc ^ poly;
-												    }
-											    }
-											    crc >>= 26;
-											    hash_table[crc >> 4] |= 1 << (crc & 0xf);
-										    }
-										    bmwrite(dev, BHASH0, hash_table[0]);
-										    bmwrite(dev, BHASH1, hash_table[1]);
-										    bmwrite(dev, BHASH2, hash_table[2]);
-										    bmwrite(dev, BHASH3, hash_table[3]);
-									    }
-    
-									    /* Let us get going again. */
-/*     dev->tbusy = 0; */
-								    }
-
-
-									    static int miscintcount = 0;
-
-									    static void bmac_misc_intr(int irq, void *dev_id, struct pt_regs *regs)
-									    {
-										    struct device *dev = (struct device *) dev_id;
-										    struct bmac_data *bp = (struct bmac_data *)dev->priv;
-										    unsigned int status = bmread(dev, STATUS);
-										    if (miscintcount++ < 10) {
-											    XXDEBUG(("bmac_misc_intr\n"));
-										    }
-/* XXDEBUG(("bmac_misc_intr, status=%#08x\n", status)); */
-/*     bmac_txdma_intr_inner(irq, dev_id, regs); */
-/*   if (status & FrameReceived) bp->stats.rx_dropped++; */
-										    if (status & RxErrorMask) bp->stats.rx_errors++;
-										    if (status & RxCRCCntExp) bp->stats.rx_crc_errors++;
-										    if (status & RxLenCntExp) bp->stats.rx_length_errors++;
-										    if (status & RxOverFlow) bp->stats.rx_over_errors++;
-										    if (status & RxAlignCntExp) bp->stats.rx_frame_errors++;
-
-/*   if (status & FrameSent) bp->stats.tx_dropped++; */
-										    if (status & TxErrorMask) bp->stats.tx_errors++;
-										    if (status & TxUnderrun) bp->stats.tx_fifo_errors++;
-										    if (status & TxNormalCollExp) bp->stats.collisions++;
-									    }
+					test = ((bit ^ crc) & 0x01);
+					crc >>= 1;
+					if(test)
+						crc = crc ^ poly;
+				}
+			}
+			crc >>= 26;
+			hash_table[crc >> 4] |= 1 << (crc & 0xf);
+		}
+		bmwrite(dev, BHASH0, hash_table[0]);
+		bmwrite(dev, BHASH1, hash_table[1]);
+		bmwrite(dev, BHASH2, hash_table[2]);
+		bmwrite(dev, BHASH3, hash_table[3]);
+	}
+    
+	/* Let us get going again. */
+	/*     dev->tbusy = 0; */
+}
+#endif /* SUNHME_MULTICAST */
+
+static int miscintcount = 0;
+
+static void bmac_misc_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *) dev_id;
+	struct bmac_data *bp = (struct bmac_data *)dev->priv;
+	unsigned int status = bmread(dev, STATUS);
+	if (miscintcount++ < 10) {
+		XXDEBUG(("bmac_misc_intr\n"));
+	}
+	/* XXDEBUG(("bmac_misc_intr, status=%#08x\n", status)); */
+	/*     bmac_txdma_intr_inner(irq, dev_id, regs); */
+	/*   if (status & FrameReceived) bp->stats.rx_dropped++; */
+	if (status & RxErrorMask) bp->stats.rx_errors++;
+	if (status & RxCRCCntExp) bp->stats.rx_crc_errors++;
+	if (status & RxLenCntExp) bp->stats.rx_length_errors++;
+	if (status & RxOverFlow) bp->stats.rx_over_errors++;
+	if (status & RxAlignCntExp) bp->stats.rx_frame_errors++;
+
+	/*   if (status & FrameSent) bp->stats.tx_dropped++; */
+	if (status & TxErrorMask) bp->stats.tx_errors++;
+	if (status & TxUnderrun) bp->stats.tx_fifo_errors++;
+	if (status & TxNormalCollExp) bp->stats.collisions++;
+}
 
 /*
  * Procedure for reading EEPROM 
@@ -988,464 +999,467 @@
 #define SROMAddressBits		6
 #define EnetAddressOffset	20
 
-										    static unsigned char
-										    bmac_clock_out_bit(struct device *dev)
-										    {
-											    unsigned short         data;
-											    unsigned short         val;
-
-											    bmwrite(dev, SROMCSR, ChipSelect | Clk);
-											    udelay(DelayValue);
-    
-											    data = bmread(dev, SROMCSR);
-											    udelay(DelayValue);
-											    val = (data >> SD0ShiftCount) & 1;
-
-											    bmwrite(dev, SROMCSR, ChipSelect);
-											    udelay(DelayValue);
-    
-											    return val;
-										    }
-
-											    static void
-											    bmac_clock_in_bit(struct device *dev, unsigned int val)
-											    {
-												    unsigned short		data;    
-
-												    if (val != 0 && val != 1) return;
-    
-												    data = (val << SDIShiftCount);
-												    bmwrite(dev, SROMCSR, data | ChipSelect  );
-												    udelay(DelayValue);
-    
-												    bmwrite(dev, SROMCSR, data | ChipSelect | Clk );
-												    udelay(DelayValue);
-
-												    bmwrite(dev, SROMCSR, data | ChipSelect);
-												    udelay(DelayValue);
-											    }
-
-												    static void
-												    reset_and_select_srom(struct device *dev)
-												    {
-													    /* first reset */
-													    bmwrite(dev, SROMCSR, 0);
-													    udelay(DelayValue);
-    
-													    /* send it the read command (110) */
-													    bmac_clock_in_bit(dev, 1);
-													    bmac_clock_in_bit(dev, 1);
-													    bmac_clock_in_bit(dev, 0);
-												    }
-
-													    static unsigned short
-													    read_srom(struct device *dev, unsigned int addr, unsigned int addr_len)
-													    {
-														    unsigned short data, val;
-														    int i;
-    
-														    /* send out the address we want to read from */
-														    for (i = 0; i < addr_len; i++)	{
-															    val = addr >> (addr_len-i-1);
-															    bmac_clock_in_bit(dev, val & 1);
-														    }
-    
-														    /* Now read in the 16-bit data */
-														    data = 0;
-														    for (i = 0; i < 16; i++)	{
-															    val = bmac_clock_out_bit(dev);
-															    data <<= 1;
-															    data |= val;
-														    }
-														    bmwrite(dev, SROMCSR, 0);
+static unsigned char
+bmac_clock_out_bit(struct device *dev)
+{
+	unsigned short         data;
+	unsigned short         val;
+
+	bmwrite(dev, SROMCSR, ChipSelect | Clk);
+	udelay(DelayValue);
+    
+	data = bmread(dev, SROMCSR);
+	udelay(DelayValue);
+	val = (data >> SD0ShiftCount) & 1;
+
+	bmwrite(dev, SROMCSR, ChipSelect);
+	udelay(DelayValue);
     
-														    return data;
-													    }
+	return val;
+}
+
+static void
+bmac_clock_in_bit(struct device *dev, unsigned int val)
+{
+	unsigned short		data;    
+
+	if (val != 0 && val != 1) return;
+    
+	data = (val << SDIShiftCount);
+	bmwrite(dev, SROMCSR, data | ChipSelect  );
+	udelay(DelayValue);
+    
+	bmwrite(dev, SROMCSR, data | ChipSelect | Clk );
+	udelay(DelayValue);
+
+	bmwrite(dev, SROMCSR, data | ChipSelect);
+	udelay(DelayValue);
+}
+
+static void
+reset_and_select_srom(struct device *dev)
+{
+	/* first reset */
+	bmwrite(dev, SROMCSR, 0);
+	udelay(DelayValue);
+    
+	/* send it the read command (110) */
+	bmac_clock_in_bit(dev, 1);
+	bmac_clock_in_bit(dev, 1);
+	bmac_clock_in_bit(dev, 0);
+}
+
+static unsigned short
+read_srom(struct device *dev, unsigned int addr, unsigned int addr_len)
+{
+	unsigned short data, val;
+	int i;
+    
+	/* send out the address we want to read from */
+	for (i = 0; i < addr_len; i++)	{
+		val = addr >> (addr_len-i-1);
+		bmac_clock_in_bit(dev, val & 1);
+	}
+    
+	/* Now read in the 16-bit data */
+	data = 0;
+	for (i = 0; i < 16; i++)	{
+		val = bmac_clock_out_bit(dev);
+		data <<= 1;
+		data |= val;
+	}
+	bmwrite(dev, SROMCSR, 0);
+    
+	return data;
+}
 
 /*
  * It looks like Cogent and SMC use different methods for calculating
  * checksums. What a pain.. 
  */
 
-														    static int
-														    bmac_verify_checksum(struct device *dev)
-														    {
-															    unsigned short data, storedCS;
-    
-															    reset_and_select_srom(dev);
-															    data = read_srom(dev, 3, SROMAddressBits);
-															    storedCS = ((data >> 8) & 0x0ff) | ((data << 8) & 0xff00);
-    
-															    return 0;
-														    }
-
-
-															    static void
-															    bmac_get_station_address(struct device *dev, unsigned char *ea)
-															    {
-																    int i;
-																    unsigned short data;
-
-																    for (i = 0; i < 6; i++)	
-																    {
-																	    reset_and_select_srom(dev);
-																	    data = read_srom(dev, i + EnetAddressOffset/2, SROMAddressBits);
-																	    ea[2*i]   = bitrev(data & 0x0ff);
-																	    ea[2*i+1] = bitrev((data >> 8) & 0x0ff);
-																    }
-															    }
-
-																    static int bmac_reset_and_enable(struct device *dev, int enable)
-																    {
-																	    struct bmac_data *bp = dev->priv;
-																	    unsigned long flags;
-
-																	    save_flags(flags); cli();
-																	    bp->reset_and_enabled = 0;
-																	    bmac_reset_chip(dev);
-																	    if (enable) {
-																		    if (!bmac_init_tx_ring(bp) || !bmac_init_rx_ring(bp)) return 0;
-																		    if (!bmac_init_chip(dev)) return 0;
-																		    bmac_start_chip(dev);
-																		    bmwrite(dev, INTDISABLE, EnableNormal);
-																		    bp->reset_and_enabled = 1;
-/* 	{ */
-/* 	    unsigned char random_packet[100]; */
-/* 	    unsigned int i; */
-/* 	    struct sk_buff *skb = dev_alloc_skb(RX_BUFLEN+2); */
-/* 	    unsigned char *data = skb_put(skb, sizeof(random_packet)); */
-/* XXDEBUG(("transmitting random packet\n")); */
-/* 	    for (i = 0; i < sizeof(random_packet); i++) data[i] = i; */
-/* 	    bmac_transmit_packet(skb, dev); */
-/* XXDEBUG(("done transmitting random packet\n")); */
-/* 	} */
-																	    }
-																	    restore_flags(flags);
-																	    return 1;
-																    }
-
-																	    int
-																	    bmac_probe(struct device *dev)
-																	    {
-																		    int j, rev;
-																		    struct bmac_data *bp;
-																		    struct device_node *bmacs;
-																		    unsigned char *addr;
-
-																		    bmacs = find_devices("bmac");
-																		    if (bmacs == NULL) return ENODEV;
-
-																		    bmac_devs = dev; /* KLUDGE!! */
-
-																		    if (bmacs->n_addrs != 3 || bmacs->n_intrs != 3) {
-																			    printk(KERN_ERR "can't use BMAC %s: expect 3 addrs and 3 intrs\n",
-																				   bmacs->full_name);
-																			    return EINVAL;
-																		    }
-    
-																		    if (dev == NULL) {
-																			    dev = init_etherdev(NULL, PRIV_BYTES);
-																			    bmac_devs = dev;  /*KLUDGE!!*/
-																		    } else {
-																			    /* XXX this doesn't look right (but it's never used :-) */
-																			    dev->priv = kmalloc(PRIV_BYTES, GFP_KERNEL);
-																			    if (dev->priv == 0) return -ENOMEM;
-																		    }
-    
-																		    dev->base_addr = bmacs->addrs[0].address;
-																		    dev->irq = bmacs->intrs[0].line;
-    
-																		    bmwrite(dev, INTDISABLE, DisableAll);
-
-																		    if (request_irq(dev->irq, bmac_misc_intr, 0, "BMAC-misc", dev)) {
-																			    printk(KERN_ERR "BMAC: can't get irq %d\n", dev->irq);
-																			    return -EAGAIN;
-																		    }
-																		    if (request_irq(bmacs->intrs[1].line, bmac_txdma_intr, 0, "BMAC-txdma",
-																				    dev)) {
-																			    printk(KERN_ERR "BMAC: can't get irq %d\n", bmacs->intrs[1].line);
-																			    return -EAGAIN;
-																		    }
-																		    if (request_irq(bmacs->intrs[2].line, bmac_rxdma_intr, 0, "BMAC-rxdma",
-																				    dev)) {
-																			    printk(KERN_ERR "BMAC: can't get irq %d\n", bmacs->intrs[2].line);
-																			    return -EAGAIN;
-																		    }
-    
-																		    addr = get_property(bmacs, "mac-address", NULL);
-																		    if (addr == NULL) {
-																			    addr = get_property(bmacs, "local-mac-address", NULL);
-																			    if (addr == NULL) {
-																				    printk(KERN_ERR "Can't get mac-address for BMAC at %lx\n",
-																					   dev->base_addr);
-																				    return -EAGAIN;
-																			    }
-																		    }
-    
-																		    printk(KERN_INFO "%s: BMAC at", dev->name);
-																		    rev = addr[0] == 0 && addr[1] == 0xA0;
-																		    for (j = 0; j < 6; ++j) {
-																			    dev->dev_addr[j] = rev? bitrev(addr[j]): addr[j];
-																			    printk("%c%.2x", (j? ':': ' '), dev->dev_addr[j]);
-																		    }
-																		    XXDEBUG((", base_addr=%#0lx", dev->base_addr));
-																		    printk("\n");
-    
-																		    dev->open = bmac_open;
-																		    dev->stop = bmac_close;
-																		    dev->hard_start_xmit = bmac_output;
-																		    dev->get_stats = bmac_stats;
-																		    dev->set_multicast_list = bmac_set_multicast;
-																		    dev->set_mac_address = bmac_set_address;
-
-																		    bmac_get_station_address(dev, addr);
-																		    if (bmac_verify_checksum(dev) != 0) return EINVAL;
-    
-																		    ether_setup(dev);
-    
-																		    bp = (struct bmac_data *) dev->priv;
-																		    memset(bp, 0, sizeof(struct bmac_data));
-																		    bp->tx_dma = (volatile struct dbdma_regs *) bmacs->addrs[1].address;
-																		    bp->tx_dma_intr = bmacs->intrs[1].line;
-																		    bp->rx_dma = (volatile struct dbdma_regs *) bmacs->addrs[2].address;
-																		    bp->rx_dma_intr = bmacs->intrs[2].line;
-    
-																		    bp->tx_cmds = (volatile struct dbdma_cmd *) DBDMA_ALIGN(bp + 1);
-																		    bp->rx_cmds = bp->tx_cmds + N_TX_RING + 1;
-
-																		    bp->queue = (struct sk_buff_head *)(bp->rx_cmds + N_RX_RING + 1);
-																		    skb_queue_head_init(bp->queue);
-    
-																		    memset(&bp->stats, 0, sizeof(bp->stats));
-																		    memset((char *) bp->tx_cmds, 0,
-																			   (N_TX_RING + N_RX_RING + 2) * sizeof(struct dbdma_cmd));
-/*     init_timer(&bp->tx_timeout); */
-/*     bp->timeout_active = 0; */
+static int
+bmac_verify_checksum(struct device *dev)
+{
+	unsigned short data, storedCS;
+    
+	reset_and_select_srom(dev);
+	data = read_srom(dev, 3, SROMAddressBits);
+	storedCS = ((data >> 8) & 0x0ff) | ((data << 8) & 0xff00);
     
-																		    if (!bmac_reset_and_enable(dev, 0)) return EINVAL;
+	return 0;
+}
+
+
+static void
+bmac_get_station_address(struct device *dev, unsigned char *ea)
+{
+	int i;
+	unsigned short data;
+
+	for (i = 0; i < 6; i++)	
+		{
+			reset_and_select_srom(dev);
+			data = read_srom(dev, i + EnetAddressOffset/2, SROMAddressBits);
+			ea[2*i]   = bitrev(data & 0x0ff);
+			ea[2*i+1] = bitrev((data >> 8) & 0x0ff);
+		}
+}
+
+static int bmac_reset_and_enable(struct device *dev, int enable)
+{
+	struct bmac_data *bp = dev->priv;
+	unsigned long flags;
 
+	save_flags(flags); cli();
+	bp->reset_and_enabled = 0;
+	bmac_reset_chip(dev);
+	if (enable) {
+		if (!bmac_init_tx_ring(bp) || !bmac_init_rx_ring(bp)) return 0;
+		if (!bmac_init_chip(dev)) return 0;
+		bmac_start_chip(dev);
+		bmwrite(dev, INTDISABLE, EnableNormal);
+		bp->reset_and_enabled = 1;
+		/* 	{ */
+		/* 	    unsigned char random_packet[100]; */
+		/* 	    unsigned int i; */
+		/* 	    struct sk_buff *skb = dev_alloc_skb(RX_BUFLEN+2); */
+		/* 	    unsigned char *data = skb_put(skb, sizeof(random_packet)); */
+		/* XXDEBUG(("transmitting random packet\n")); */
+		/* 	    for (i = 0; i < sizeof(random_packet); i++) data[i] = i; */
+		/* 	    bmac_transmit_packet(skb, dev); */
+		/* XXDEBUG(("done transmitting random packet\n")); */
+		/* 	} */
+	}
+	restore_flags(flags);
+	return 1;
+}
+
+int
+bmac_probe(struct device *dev)
+{
+	int j, rev;
+	struct bmac_data *bp;
+	struct device_node *bmacs;
+	unsigned char *addr;
+	static struct device_node *all_bmacs = NULL, *next_bmac;
+
+	if (all_bmacs == NULL)
+		all_bmacs = next_bmac = find_devices("bmac");
+	bmacs = next_bmac;
+	if (bmacs == NULL) return -ENODEV;
+	next_bmac = bmacs->next;
+
+	bmac_devs = dev; /* KLUDGE!! */
+
+	if (bmacs->n_addrs != 3 || bmacs->n_intrs != 3) {
+		printk(KERN_ERR "can't use BMAC %s: expect 3 addrs and 3 intrs\n",
+		       bmacs->full_name);
+		return -EINVAL;
+	}
+    
+	if (dev == NULL) {
+		dev = init_etherdev(NULL, PRIV_BYTES);
+		bmac_devs = dev;  /*KLUDGE!!*/
+	} else {
+		/* XXX this doesn't look right (but it's never used :-) */
+		dev->priv = kmalloc(PRIV_BYTES, GFP_KERNEL);
+		if (dev->priv == 0) return -ENOMEM;
+	}
+    
+	dev->base_addr = bmacs->addrs[0].address;
+	dev->irq = bmacs->intrs[0].line;
+    
+	bmwrite(dev, INTDISABLE, DisableAll);
+    
+	addr = get_property(bmacs, "mac-address", NULL);
+	if (addr == NULL) {
+		addr = get_property(bmacs, "local-mac-address", NULL);
+		if (addr == NULL) {
+			printk(KERN_ERR "Can't get mac-address for BMAC at %lx\n",
+			       dev->base_addr);
+			return -EAGAIN;
+		}
+	}
+    
+	printk(KERN_INFO "%s: BMAC at", dev->name);
+	rev = addr[0] == 0 && addr[1] == 0xA0;
+	for (j = 0; j < 6; ++j) {
+		dev->dev_addr[j] = rev? bitrev(addr[j]): addr[j];
+		printk("%c%.2x", (j? ':': ' '), dev->dev_addr[j]);
+	}
+	XXDEBUG((", base_addr=%#0lx", dev->base_addr));
+	printk("\n");
+    
+	dev->open = bmac_open;
+	dev->stop = bmac_close;
+	dev->hard_start_xmit = bmac_output;
+	dev->get_stats = bmac_stats;
+	dev->set_multicast_list = bmac_set_multicast;
+	dev->set_mac_address = bmac_set_address;
+
+	bmac_get_station_address(dev, addr);
+	if (bmac_verify_checksum(dev) != 0) return -EINVAL;
+    
+	ether_setup(dev);
+    
+	bp = (struct bmac_data *) dev->priv;
+	memset(bp, 0, sizeof(struct bmac_data));
+	bp->tx_dma = (volatile struct dbdma_regs *) bmacs->addrs[1].address;
+	bp->tx_dma_intr = bmacs->intrs[1].line;
+	bp->rx_dma = (volatile struct dbdma_regs *) bmacs->addrs[2].address;
+	bp->rx_dma_intr = bmacs->intrs[2].line;
+    
+	bp->tx_cmds = (volatile struct dbdma_cmd *) DBDMA_ALIGN(bp + 1);
+	bp->rx_cmds = bp->tx_cmds + N_TX_RING + 1;
+
+	bp->queue = (struct sk_buff_head *)(bp->rx_cmds + N_RX_RING + 1);
+	skb_queue_head_init(bp->queue);
+    
+	memset(&bp->stats, 0, sizeof(bp->stats));
+	memset((char *) bp->tx_cmds, 0,
+	       (N_TX_RING + N_RX_RING + 2) * sizeof(struct dbdma_cmd));
+	/*     init_timer(&bp->tx_timeout); */
+	/*     bp->timeout_active = 0; */
+
+	if (request_irq(dev->irq, bmac_misc_intr, 0, "BMAC-misc", dev)) {
+		printk(KERN_ERR "BMAC: can't get irq %d\n", dev->irq);
+		return -EAGAIN;
+	}
+	if (request_irq(bmacs->intrs[1].line, bmac_txdma_intr, 0, "BMAC-txdma",
+			dev)) {
+		printk(KERN_ERR "BMAC: can't get irq %d\n", bmacs->intrs[1].line);
+		return -EAGAIN;
+	}
+	if (request_irq(bmacs->intrs[2].line, bmac_rxdma_intr, 0, "BMAC-rxdma",
+			dev)) {
+		printk(KERN_ERR "BMAC: can't get irq %d\n", bmacs->intrs[2].line);
+		return -EAGAIN;
+	}
+    
+	if (!bmac_reset_and_enable(dev, 0)) return -ENOMEM;
+    
 #ifdef CONFIG_PROC_FS
-																		    proc_net_register(&(struct proc_dir_entry) {
-																			    PROC_NET_BMAC, 4, "bmac",
-																				    S_IFREG | S_IRUGO, 1, 0, 0,
-																				    0, &proc_net_inode_operations,
-																				    bmac_proc_info
-																				    });
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_BMAC, 4, "bmac",
+			S_IFREG | S_IRUGO, 1, 0, 0,
+			0, &proc_net_inode_operations,
+			bmac_proc_info
+			});
 #endif
 
-																		    return 0;
-																	    }
+	return 0;
+}
+
+static int bmac_open(struct device *dev)
+{
+	/* XXDEBUG(("bmac: enter open\n")); */
+	/* reset the chip */
+	bmac_reset_and_enable(dev, 1);
+
+	dev->flags |= IFF_UP | IFF_RUNNING;
+
+	return 0;
+}
+
+static int bmac_close(struct device *dev)
+{
+	struct bmac_data *bp = (struct bmac_data *) dev->priv;
+	volatile struct dbdma_regs *rd = bp->rx_dma;
+	volatile struct dbdma_regs *td = bp->tx_dma;
+	unsigned short config;
+	int i;
+
+	dev->flags &= ~(IFF_UP | IFF_RUNNING);
+
+	/* disable rx and tx */
+	config = bmread(dev, RXCFG);
+	bmwrite(dev, RXCFG, (config & ~RxMACEnable));
+
+	config = bmread(dev, TXCFG);
+	bmwrite(dev, TXCFG, (config & ~TxMACEnable));
+
+	bmwrite(dev, INTDISABLE, DisableAll); /* disable all intrs */
+
+	/* disable rx and tx dma */
+	st_le32(&rd->control, DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE));	/* clear run bit */
+	st_le32(&td->control, DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE));	/* clear run bit */
+
+	/* free some skb's */
+	XXDEBUG(("bmac: free rx bufs\n"));
+	for (i=0; i<N_RX_RING; i++) {
+		if (bp->rx_bufs[i] != NULL) {
+			dev_kfree_skb(bp->rx_bufs[i]);
+			bp->rx_bufs[i] = NULL;
+		}
+	}
+	bp->rx_allocated = 0;
+	XXDEBUG(("bmac: free doubles\n"));/*MEMORY LEAK BELOW!!! FIX!!! */
+	if (bp->tx_double[0] != NULL) kfree(bp->tx_double[0]);
+	XXDEBUG(("bmac: free tx bufs\n"));
+	for (i = 0; i<N_TX_RING; i++) {
+		if (bp->tx_bufs[i] != NULL) {
+			dev_kfree_skb(bp->tx_bufs[i]);
+			bp->tx_bufs[i] = NULL;
+		}
+	}
+	bp->tx_allocated = 0;
+	bp->reset_and_enabled = 0;
+	XXDEBUG(("bmac: all bufs freed\n"));
+
+	return 0;
+}
+
+static void
+bmac_start(struct device *dev)
+{
+	struct bmac_data *bp = dev->priv;
+	int i;
+	struct sk_buff *skb;
+	unsigned long flags;
+    
+	save_flags(flags); cli();
+	while (1) {
+		i = bp->tx_fill + 1;
+		if (i >= N_TX_RING) i = 0;
+		if (i == bp->tx_empty) break;
+		skb = skb_dequeue(bp->queue);
+		if (skb == NULL) break;
+		bmac_transmit_packet(skb, dev);
+	}
+	restore_flags(flags);
+}
+
+static int
+bmac_output(struct sk_buff *skb, struct device *dev)
+{
+	struct bmac_data *bp = dev->priv;
+	skb_queue_tail(bp->queue, skb);
+	bmac_start(dev);
+	return 0;
+}
+
+static void bmac_tx_timeout(unsigned long data)
+{
+	struct device *dev = (struct device *) data;
+	struct bmac_data *bp = (struct bmac_data *) dev->priv;
+	volatile struct dbdma_regs *td = bp->tx_dma;
+	volatile struct dbdma_regs *rd = bp->rx_dma;
+	volatile struct dbdma_cmd *cp;
+	unsigned long flags;
+	unsigned short config, oldConfig;
+	int i;
 
-																		    static int bmac_open(struct device *dev)
-																		    {
-/* XXDEBUG(("bmac: enter open\n")); */
-																			    /* reset the chip */
-																			    bmac_reset_and_enable(dev, 1);
-
-																			    dev->flags |= IFF_UP | IFF_RUNNING;
-
-																			    return 0;
-																		    }
-
-																			    static int bmac_close(struct device *dev)
-																			    {
-																				    struct bmac_data *bp = (struct bmac_data *) dev->priv;
-																				    volatile struct dbdma_regs *rd = bp->rx_dma;
-																				    volatile struct dbdma_regs *td = bp->tx_dma;
-																				    unsigned short config;
-																				    int i;
-
-																				    dev->flags &= ~(IFF_UP | IFF_RUNNING);
-
-																				    /* disable rx and tx */
-																				    config = bmread(dev, RXCFG);
-																				    bmwrite(dev, RXCFG, (config & ~RxMACEnable));
-
-																				    config = bmread(dev, TXCFG);
-																				    bmwrite(dev, TXCFG, (config & ~TxMACEnable));
-
-																				    bmwrite(dev, INTDISABLE, DisableAll); /* disable all intrs */
-
-																				    /* disable rx and tx dma */
-																				    st_le32(&rd->control, DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE));	/* clear run bit */
-																				    st_le32(&td->control, DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE));	/* clear run bit */
-
-																				    /* free some skb's */
-																				    XXDEBUG(("bmac: free rx bufs\n"));
-																				    for (i=0; i<N_RX_RING; i++) {
-																					    if (bp->rx_bufs[i] != NULL) {
-																						    dev_kfree_skb(bp->rx_bufs[i]);
-																						    bp->rx_bufs[i] = NULL;
-																					    }
-																				    }
-																				    bp->rx_allocated = 0;
-																				    XXDEBUG(("bmac: free doubles\n"));/*MEMORY LEAK BELOW!!! FIX!!! */
-																				    if (bp->tx_double[0] != NULL) kfree(bp->tx_double[0]);
-																				    XXDEBUG(("bmac: free tx bufs\n"));
-																				    for (i = 0; i<N_TX_RING; i++) {
-																					    if (bp->tx_bufs[i] != NULL) {
-																						    dev_kfree_skb(bp->tx_bufs[i]);
-																						    bp->tx_bufs[i] = NULL;
-																					    }
-																				    }
-																				    bp->tx_allocated = 0;
-																				    bp->reset_and_enabled = 0;
-																				    XXDEBUG(("bmac: all bufs freed\n"));
-
-																				    return 0;
-																			    }
-
-																				    static void
-																				    bmac_start(struct device *dev)
-																				    {
-																					    struct bmac_data *bp = dev->priv;
-																					    int i;
-																					    struct sk_buff *skb;
-																					    unsigned long flags;
-    
-																					    save_flags(flags); cli();
-																					    while (1) {
-																						    i = bp->tx_fill + 1;
-																						    if (i >= N_TX_RING) i = 0;
-																						    if (i == bp->tx_empty) break;
-																						    skb = skb_dequeue(bp->queue);
-																						    if (skb == NULL) break;
-																						    bmac_transmit_packet(skb, dev);
-																					    }
-																					    restore_flags(flags);
-																				    }
-
-																					    static int
-																					    bmac_output(struct sk_buff *skb, struct device *dev)
-																					    {
-																						    struct bmac_data *bp = dev->priv;
-																						    skb_queue_tail(bp->queue, skb);
-																						    bmac_start(dev);
-																						    return 0;
-																					    }
-
-																						    static void bmac_tx_timeout(unsigned long data)
-																						    {
-																							    struct device *dev = (struct device *) data;
-																							    struct bmac_data *bp = (struct bmac_data *) dev->priv;
-																							    volatile struct dbdma_regs *td = bp->tx_dma;
-																							    volatile struct dbdma_regs *rd = bp->rx_dma;
-																							    volatile struct dbdma_cmd *cp;
-																							    unsigned long flags;
-																							    unsigned short config, oldConfig;
-																							    int i;
-
-																							    XXDEBUG(("bmac: tx_timeout called\n"));
-																							    save_flags(flags); cli();
-																							    bp->timeout_active = 0;
+	XXDEBUG(("bmac: tx_timeout called\n"));
+	save_flags(flags); cli();
+	bp->timeout_active = 0;
 
-																							    /* update various counters */
-/*     bmac_handle_misc_intrs(bp, 0); */
+	/* update various counters */
+/*     	bmac_handle_misc_intrs(bp, 0); */
 
-																							    cp = &bp->tx_cmds[bp->tx_empty];
-/*     XXDEBUG((KERN_DEBUG "bmac: tx dmastat=%x %x runt=%d pr=%x fs=%x fc=%x\n", */
+	cp = &bp->tx_cmds[bp->tx_empty];
+/*	XXDEBUG((KERN_DEBUG "bmac: tx dmastat=%x %x runt=%d pr=%x fs=%x fc=%x\n", */
 /* 	   ld_le32(&td->status), ld_le16(&cp->xfer_status), bp->tx_bad_runt, */
 /* 	   mb->pr, mb->xmtfs, mb->fifofc)); */
 
-																							    /* turn off both tx and rx and reset the chip */
-																							    config = bmread(dev, RXCFG);
-																							    bmwrite(dev, RXCFG, (config & ~RxMACEnable));
-																							    config = bmread(dev, TXCFG);
-																							    bmwrite(dev, TXCFG, (config & ~TxMACEnable));
-																							    out_le32(&td->control, DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE|ACTIVE|DEAD));
-																							    printk(KERN_ERR "bmac: transmit timeout - resetting\n");
-																							    bmac_reset_chip(dev);
-
-																							    /* restart rx dma */
-																							    cp = bus_to_virt(ld_le32(&rd->cmdptr));
-																							    out_le32(&rd->control, DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE|ACTIVE|DEAD));
-																							    out_le16(&cp->xfer_status, 0);
-																							    out_le32(&rd->cmdptr, virt_to_bus(cp));
-																							    out_le32(&rd->control, DBDMA_SET(RUN|WAKE));
-
-																							    /* fix up the transmit side */
-																							    XXDEBUG((KERN_DEBUG "bmac: tx empty=%d fill=%d fullup=%d\n",
-																								     bp->tx_empty, bp->tx_fill, bp->tx_fullup));
-																							    i = bp->tx_empty;
-																							    ++bp->stats.tx_errors;
-																							    if (i != bp->tx_fill) {
-																								    dev_kfree_skb(bp->tx_bufs[i]);
-																								    bp->tx_bufs[i] = NULL;
-																								    if (++i >= N_TX_RING) i = 0;
-																								    bp->tx_empty = i;
-																							    }
-																							    bp->tx_fullup = 0;
-																							    dev->tbusy = 0;
-																							    mark_bh(NET_BH);
-																							    XXDEBUG((KERN_DEBUG "bmac: clearing tbusy\n"));
-																							    if (i != bp->tx_fill) {
-																								    cp = &bp->tx_cmds[i];
-																								    out_le16(&cp->xfer_status, 0);
-																								    out_le16(&cp->command, OUTPUT_LAST);
-																								    out_le32(&td->cmdptr, virt_to_bus(cp));
-																								    out_le32(&td->control, DBDMA_SET(RUN));
-/* 	bmac_set_timeout(dev); */
-																								    XXDEBUG((KERN_DEBUG "bmac: starting %d\n", i));
-																							    }
-
-																							    /* turn it back on */
-																							    oldConfig = bmread(dev, RXCFG);		
-																							    bmwrite(dev, RXCFG, oldConfig | RxMACEnable ); 
-																							    oldConfig = bmread(dev, TXCFG);		
-																							    bmwrite(dev, TXCFG, oldConfig | TxMACEnable );  
+	/* turn off both tx and rx and reset the chip */
+	config = bmread(dev, RXCFG);
+	bmwrite(dev, RXCFG, (config & ~RxMACEnable));
+	config = bmread(dev, TXCFG);
+	bmwrite(dev, TXCFG, (config & ~TxMACEnable));
+	out_le32(&td->control, DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE|ACTIVE|DEAD));
+	printk(KERN_ERR "bmac: transmit timeout - resetting\n");
+	bmac_reset_chip(dev);
+
+	/* restart rx dma */
+	cp = bus_to_virt(ld_le32(&rd->cmdptr));
+	out_le32(&rd->control, DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE|ACTIVE|DEAD));
+	out_le16(&cp->xfer_status, 0);
+	out_le32(&rd->cmdptr, virt_to_bus(cp));
+	out_le32(&rd->control, DBDMA_SET(RUN|WAKE));
+
+	/* fix up the transmit side */
+	XXDEBUG((KERN_DEBUG "bmac: tx empty=%d fill=%d fullup=%d\n",
+		 bp->tx_empty, bp->tx_fill, bp->tx_fullup));
+	i = bp->tx_empty;
+	++bp->stats.tx_errors;
+	if (i != bp->tx_fill) {
+		dev_kfree_skb(bp->tx_bufs[i]);
+		bp->tx_bufs[i] = NULL;
+		if (++i >= N_TX_RING) i = 0;
+		bp->tx_empty = i;
+	}
+	bp->tx_fullup = 0;
+	dev->tbusy = 0;
+	mark_bh(NET_BH);
+	XXDEBUG((KERN_DEBUG "bmac: clearing tbusy\n"));
+	if (i != bp->tx_fill) {
+		cp = &bp->tx_cmds[i];
+		out_le16(&cp->xfer_status, 0);
+		out_le16(&cp->command, OUTPUT_LAST);
+		out_le32(&td->cmdptr, virt_to_bus(cp));
+		out_le32(&td->control, DBDMA_SET(RUN));
+		/* 	bmac_set_timeout(dev); */
+		XXDEBUG((KERN_DEBUG "bmac: starting %d\n", i));
+	}
+
+	/* turn it back on */
+	oldConfig = bmread(dev, RXCFG);		
+	bmwrite(dev, RXCFG, oldConfig | RxMACEnable ); 
+	oldConfig = bmread(dev, TXCFG);		
+	bmwrite(dev, TXCFG, oldConfig | TxMACEnable );  
 
-																							    restore_flags(flags);
-																						    }
+	restore_flags(flags);
+}
 
 #if 0
-																							    static void dump_dbdma(volatile struct dbdma_cmd *cp,int count)
-																							    {
-																								    int i,*ip;
+static void dump_dbdma(volatile struct dbdma_cmd *cp,int count)
+{
+	int i,*ip;
 	
-																								    for (i=0;i< count;i++)
-																								    {
-																									    ip = (int*)(cp+i);
+	for (i=0;i< count;i++) {
+		ip = (int*)(cp+i);
 	
-																									    printk("dbdma req 0x%x addr 0x%x baddr 0x%x xfer/res 0x%x\n",
-																										   ld_le32(ip+0),
-																										   ld_le32(ip+1),
-																										   ld_le32(ip+2),
-																										   ld_le32(ip+3));
-																								    }
+		printk("dbdma req 0x%x addr 0x%x baddr 0x%x xfer/res 0x%x\n",
+		       ld_le32(ip+0),
+		       ld_le32(ip+1),
+		       ld_le32(ip+2),
+		       ld_le32(ip+3));
+	}
 
-																							    }
+}
 #endif
 
-																								    static int
-																								    bmac_proc_info ( char *buffer, char **start, off_t offset, int length, int dummy)
-																								    {
-																									    int len = 0;
-																									    off_t pos   = 0;
-																									    off_t begin = 0;
-																									    int i;
-
-																									    if (bmac_devs == NULL) return (-ENOSYS);
-
-																									    len += sprintf(buffer, "BMAC counters & registers\n");
-
-																									    for (i = 0; i<N_REG_ENTRIES; i++) {
-																										    len += sprintf(buffer + len, "%s: %#08x\n",
-																												   reg_entries[i].name,
-																												   bmread(bmac_devs, reg_entries[i].reg_offset));
-																										    pos = begin + len;
-    
-																										    if (pos < offset) {
-																											    len = 0;
-																											    begin = pos;
-																										    }
+static int
+bmac_proc_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	int len = 0;
+	off_t pos   = 0;
+	off_t begin = 0;
+	int i;
+
+	if (bmac_devs == NULL) return (-ENOSYS);
+
+	len += sprintf(buffer, "BMAC counters & registers\n");
+
+	for (i = 0; i<N_REG_ENTRIES; i++) {
+		len += sprintf(buffer + len, "%s: %#08x\n",
+			       reg_entries[i].name,
+			       bmread(bmac_devs, reg_entries[i].reg_offset));
+		pos = begin + len;
+    
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
     
-																										    if (pos > offset+length) break;
-																									    }
+		if (pos > offset+length) break;
+	}
   
-																									    *start = buffer + (offset - begin);
-																									    len -= (offset - begin);
+	*start = buffer + (offset - begin);
+	len -= (offset - begin);
   
-																									    if (len > length) len = length;
+	if (len > length) len = length;
   
-																									    return len;
-																								    }
+	return len;
+}
diff -u --recursive --new-file v2.1.119/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.1.119/linux/drivers/net/de4x5.c	Tue Aug 18 22:02:04 1998
+++ linux/drivers/net/de4x5.c	Sat Aug 29 10:24:05 1998
@@ -399,11 +399,21 @@
 			   from report & fix by <paubert@iram.es>.
 			  Fix probe bug with EISA & PCI cards present from
                            report by <eirik@netcom.com>.
+      0.541  24-Aug-98    Fix compiler problems associated with i386-string
+                           ops from multiple bug reports and temporary fix
+			   from <paubert@iram.es>.
+			  Fix pci_probe() to correctly emulate the old
+			   pcibios_find_class() function.
+			  Add an_exception() for old ZYNX346 and fix compile
+			   warning on PPC & SPARC, from <ecd@skynet.be>.
+			  Fix lastPCI to correctly work with compiled in
+			   kernels and modules from bug report by 
+			   <Zlatko.Calusic@CARNet.hr> et al.
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:V0.540 1998/7/5 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.541 1998/8/24 davies@maniac.ultranet.com\n";
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -954,6 +964,7 @@
 static int     get_hw_addr(struct device *dev);
 static void    srom_repair(struct device *dev, int card);
 static int     test_bad_enet(struct device *dev, int status);
+static int     an_exception(struct bus_type *lp);
 #if !defined(__sparc_v9__) && !defined(__powerpc__)
 static void    eisa_probe(struct device *dev, u_long iobase);
 #endif
@@ -1013,6 +1024,7 @@
 static int cfrv = 0, useSROM = 0;
 static int lastPCI = -1;
 static struct device *lastModule = NULL;
+static struct pci_dev *pdev = NULL;
 
 /*
 ** List the SROM infoleaf functions and chipsets
@@ -2103,7 +2115,6 @@
     u_int irq = 0, device, class = DE4X5_CLASS_CODE;
     u_long iobase = 0;                     /* Clear upper 32 bits in Alphas */
     struct bus_type *lp = &bus;
-    struct pci_dev *pdev = NULL;
 
     if (lastPCI == NO_MORE_PCI) return;
 
@@ -2123,7 +2134,7 @@
 	dnum = 0;
     }
 
-    for (index=lastPCI+1; (pdev=pci_find_class(class, pdev))!=NULL; index++) {
+    for (index=lastPCI+1;(pdev = pci_find_class(class, pdev))!=NULL;index++) {
 	dev_num = PCI_SLOT(pdev->devfn);
 	pb = pdev->bus->number;
 	if ((pbus || dnum) && ((pbus != pb) || (dnum != dev_num))) continue;
@@ -2185,10 +2196,8 @@
 	    dev->irq = irq;
 	    if ((status = de4x5_hw_init(dev, iobase)) == 0) {
 		num_de4x5s++;
-		if (loading_module) {
-		    link_modules(lastModule, dev);
-		    lastPCI = index;
-		}
+		lastPCI = index;
+		if (loading_module) link_modules(lastModule, dev);
 		return;
 	    }
 	} else if (ioaddr != 0) {
@@ -2209,27 +2218,26 @@
 ** For single port cards this is a time waster...
 */
 __initfunc(static void
-srom_search(struct pci_dev *pdev))
+srom_search(struct pci_dev *dev))
 {
     u_char pb;
     u_short vendor, status;
-    u_int irq = 0, device, class = DE4X5_CLASS_CODE;
+    u_int irq = 0, device;
     u_long iobase = 0;                     /* Clear upper 32 bits in Alphas */
     int i, j;
     struct bus_type *lp = &bus;
 
-    while ((pdev = pci_find_class(class, pdev))!= NULL) {
-	if (lp->bus_num != pdev->bus->number) return;
-	pb = pdev->bus->number;
-	vendor = pdev->vendor;
-	device = pdev->device << 8;
+    for (; (dev=dev->sibling)!= NULL;) {
+	pb = dev->bus->number;
+	vendor = dev->vendor;
+	device = dev->device << 8;
 	if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue;
 
 	/* Get the chip configuration revision register */
-	pcibios_read_config_dword(pb, pdev->devfn, PCI_REVISION_ID, &cfrv);
+	pcibios_read_config_dword(pb, dev->devfn, PCI_REVISION_ID, &cfrv);
 
 	/* Set the device number information */
-	lp->device = PCI_SLOT(pdev->devfn);
+	lp->device = PCI_SLOT(dev->devfn);
 	lp->bus_num = pb;
 	    
 	/* Set the chipset information */
@@ -2237,14 +2245,14 @@
 	lp->chipset = device;
 
 	/* Get the board I/O address (64 bits on sparc64) */
-	iobase = pdev->base_address[0] & CBIO_MASK;
+	iobase = dev->base_address[0] & CBIO_MASK;
 
 	/* Fetch the IRQ to be used */
-	irq = pdev->irq;
+	irq = dev->irq;
 	if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue;
 	    
 	/* Check if I/O accesses are enabled */
-	pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status);
+	pcibios_read_config_word(pb, dev->devfn, PCI_COMMAND, &status);
 	if (!(status & PCI_COMMAND_IO)) continue;
 
 	/* Search for a valid SROM attached to this DECchip */
@@ -4204,7 +4212,9 @@
 		if (dev->dev_addr[i] != 0) break;
 	    }
 	    for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i];
-	    dev->irq = last.irq;
+	    if (!an_exception(lp)) {
+		dev->irq = last.irq;
+	    }
 
 	    status = 0;
 	}
@@ -4219,6 +4229,20 @@
 }
 
 /*
+** List of board exceptions with correctly wired IRQs
+*/
+static int
+an_exception(struct bus_type *lp)
+{
+    if ((*(u_short *)lp->srom.sub_vendor_id == 0x00c0) && 
+	(*(u_short *)lp->srom.sub_system_id == 0x95e0)) {
+	return -1;
+    }
+
+    return 0;
+}
+
+/*
 ** SROM Read
 */
 static short
@@ -5788,14 +5812,12 @@
 count_adapters(void)
 {
     int i, j=0;
-    char name[DE4X5_STRLEN];
-    u_char pb, dev_fn;
     u_short vendor;
     u_int class = DE4X5_CLASS_CODE;
     u_int device;
-    struct pci_dev *pdev;
 
 #if !defined(__sparc_v9__) && !defined(__powerpc__)
+    char name[DE4X5_STRLEN];
     u_long iobase = 0x1000;
 
     for (i=1; i<MAX_EISA_SLOTS; i++, iobase+=EISA_SLOT_INC) {
@@ -5804,13 +5826,7 @@
 #endif
     if (!pcibios_present()) return j;
 
-    for (i=0; 
-	 (pcibios_find_class(class, i, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
-	 i++) {
-	for (pdev = pci_devices; pdev; pdev = pdev->next) {
-	    if ((pdev->bus->number==pb) && (pdev->devfn==dev_fn)) break;
-	}
-
+    for (i=0; (pdev=pci_find_class(class, pdev))!= NULL; i++) {
 	vendor = pdev->vendor;
 	device = pdev->device << 8;
 	if (is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x) j++;
diff -u --recursive --new-file v2.1.119/linux/drivers/net/mace.c linux/drivers/net/mace.c
--- v2.1.119/linux/drivers/net/mace.c	Tue Jul 28 14:21:08 1998
+++ linux/drivers/net/mace.c	Thu Aug 27 19:33:08 1998
@@ -91,85 +91,90 @@
 int
 mace_probe(struct device *dev)
 {
-    int j, rev;
-    struct mace_data *mp;
-    struct device_node *maces;
-    unsigned char *addr;
-
-    maces = find_devices("mace");
-    if (maces == 0)
-	return ENODEV;
-
-    do {
-	if (maces->n_addrs != 3 || maces->n_intrs != 3) {
-	    printk(KERN_ERR "can't use MACE %s: expect 3 addrs and 3 intrs\n",
-		   maces->full_name);
-	    continue;
+	int j, rev;
+	struct mace_data *mp;
+	struct device_node *mace;
+	unsigned char *addr;
+	static int maces_found = 0;
+	static struct device_node *next_mace;
+
+	if (!maces_found) {
+		next_mace = find_devices("mace");
+		maces_found = 1;
+	}
+	mace = next_mace;
+	if (mace == 0)
+		return -ENODEV;
+	next_mace = mace->next;
+
+	if (mace->n_addrs != 3 || mace->n_intrs != 3) {
+		printk(KERN_ERR "can't use MACE %s: expect 3 addrs and 3 intrs\n",
+		       mace->full_name);
+		return -ENODEV;
 	}
 
 	if (dev == NULL)
-	    dev = init_etherdev(0, PRIV_BYTES);
+		dev = init_etherdev(0, PRIV_BYTES);
 	else {
-	    /* XXX this doesn't look right (but it's never used :-) */
-	    dev->priv = kmalloc(PRIV_BYTES, GFP_KERNEL);
-	    if (dev->priv == 0)
-		return -ENOMEM;
+		dev->priv = kmalloc(PRIV_BYTES, GFP_KERNEL);
+		if (dev->priv == 0)
+			return -ENOMEM;
 	}
 
 	mp = (struct mace_data *) dev->priv;
-	dev->base_addr = maces->addrs[0].address;
+	dev->base_addr = mace->addrs[0].address;
 	mp->mace = (volatile struct mace *)
-		ioremap(maces->addrs[0].address, 0x1000);
-	dev->irq = maces->intrs[0].line;
+		ioremap(mace->addrs[0].address, 0x1000);
+	dev->irq = mace->intrs[0].line;
 
 	if (request_irq(dev->irq, mace_interrupt, 0, "MACE", dev)) {
-	    printk(KERN_ERR "MACE: can't get irq %d\n", dev->irq);
-	    return -EAGAIN;
+		printk(KERN_ERR "MACE: can't get irq %d\n", dev->irq);
+		return -EAGAIN;
 	}
-	if (request_irq(maces->intrs[1].line, mace_txdma_intr, 0, "MACE-txdma",
+	if (request_irq(mace->intrs[1].line, mace_txdma_intr, 0, "MACE-txdma",
 			dev)) {
-	    printk(KERN_ERR "MACE: can't get irq %d\n", maces->intrs[1].line);
-	    return -EAGAIN;
+		printk(KERN_ERR "MACE: can't get irq %d\n", mace->intrs[1].line);
+		return -EAGAIN;
 	}
-	if (request_irq(maces->intrs[2].line, mace_rxdma_intr, 0, "MACE-rxdma",
+	if (request_irq(mace->intrs[2].line, mace_rxdma_intr, 0, "MACE-rxdma",
 			dev)) {
-	    printk(KERN_ERR "MACE: can't get irq %d\n", maces->intrs[2].line);
-	    return -EAGAIN;
+		printk(KERN_ERR "MACE: can't get irq %d\n", mace->intrs[2].line);
+		return -EAGAIN;
 	}
 
-	addr = get_property(maces, "mac-address", NULL);
+	addr = get_property(mace, "mac-address", NULL);
 	if (addr == NULL) {
-	    addr = get_property(maces, "local-mac-address", NULL);
-	    if (addr == NULL) {
-		printk(KERN_ERR "Can't get mac-address for MACE at %lx\n",
-		       dev->base_addr);
-		return -EAGAIN;
-	    }
+		addr = get_property(mace, "local-mac-address", NULL);
+		if (addr == NULL) {
+			printk(KERN_ERR "Can't get mac-address for MACE at %lx\n",
+			       dev->base_addr);
+			return -EAGAIN;
+		}
 	}
 
 	printk(KERN_INFO "%s: MACE at", dev->name);
 	rev = addr[0] == 0 && addr[1] == 0xA0;
 	for (j = 0; j < 6; ++j) {
-	    dev->dev_addr[j] = rev? bitrev(addr[j]): addr[j];
-	    printk("%c%.2x", (j? ':': ' '), dev->dev_addr[j]);
+		dev->dev_addr[j] = rev? bitrev(addr[j]): addr[j];
+		printk("%c%.2x", (j? ':': ' '), dev->dev_addr[j]);
 	}
 	printk("\n");
 
 	mp = (struct mace_data *) dev->priv;
 	mp->maccc = ENXMT | ENRCV;
 	mp->tx_dma = (volatile struct dbdma_regs *)
-		ioremap(maces->addrs[1].address, 0x1000);
-	mp->tx_dma_intr = maces->intrs[1].line;
+		ioremap(mace->addrs[1].address, 0x1000);
+	mp->tx_dma_intr = mace->intrs[1].line;
 	mp->rx_dma = (volatile struct dbdma_regs *)
-		ioremap(maces->addrs[2].address, 0x1000);
-	mp->rx_dma_intr = maces->intrs[2].line;
+		ioremap(mace->addrs[2].address, 0x1000);
+	mp->rx_dma_intr = mace->intrs[2].line;
 
 	mp->tx_cmds = (volatile struct dbdma_cmd *) DBDMA_ALIGN(mp + 1);
 	mp->rx_cmds = mp->tx_cmds + NCMDS_TX * N_TX_RING + 1;
 
 	memset(&mp->stats, 0, sizeof(mp->stats));
 	memset((char *) mp->tx_cmds, 0,
-	      (NCMDS_TX*N_TX_RING + N_RX_RING + 2) * sizeof(struct dbdma_cmd));
+	       (NCMDS_TX*N_TX_RING + N_RX_RING + 2) * sizeof(struct dbdma_cmd));
 	init_timer(&mp->tx_timeout);
 	mp->timeout_active = 0;
 
@@ -182,9 +187,7 @@
 
 	ether_setup(dev);
 
-    } while ((maces = maces->next) != 0);
-
-    return 0;
+	return 0;
 }
 
 static void mace_reset(struct device *dev)
diff -u --recursive --new-file v2.1.119/linux/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c
--- v2.1.119/linux/drivers/net/pcnet32.c	Tue Jun  9 11:57:30 1998
+++ linux/drivers/net/pcnet32.c	Thu Sep  3 16:02:01 1998
@@ -13,7 +13,7 @@
  * 	This driver is for PCnet32 and PCnetPCI based ethercards
  */
 
-static const char *version = "pcnet32.c:v1.00 30.5.98 tsbogend@alpha.franken.de\n";
+static const char *version = "pcnet32.c:v1.02 3.9.98 tsbogend@alpha.franken.de\n";
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -98,6 +98,9 @@
  *         back port to 2.0.x
  * v1.00:  added some stuff from Donald Becker's 2.0.34 version
  *         added support for byte counters in net_dev_stats
+ * v1.01:  do ring dumps, only when debugging the driver
+ *         increased the transmit timeout
+ * v1.02:  fixed memory leak in pcnet32_init_ring()
  */
 
 
@@ -183,7 +186,7 @@
 int  pcnet32_probe(struct device *dev);
 static int  pcnet32_probe1(struct device *dev, unsigned int ioaddr, unsigned char irq_line, int shared);
 static int  pcnet32_open(struct device *dev);
-static void pcnet32_init_ring(struct device *dev);
+static int  pcnet32_init_ring(struct device *dev);
 static int  pcnet32_start_xmit(struct sk_buff *skb, struct device *dev);
 static int  pcnet32_rx(struct device *dev);
 static void pcnet32_interrupt(int irq, void *dev_id, struct pt_regs *regs);
@@ -440,7 +443,8 @@
 	lp->init_block.mode = 0x0000;
 	lp->init_block.filter[0] = 0x00000000;
 	lp->init_block.filter[1] = 0x00000000;
-	pcnet32_init_ring(dev);
+	if (pcnet32_init_ring(dev))
+		return -ENOMEM;
     
 	/* Re-initialize the PCNET32, and start it when done. */
 	outw(0x0001, ioaddr+PCNET32_ADDR);
@@ -505,28 +509,28 @@
 
 
 /* Initialize the PCNET32 Rx and Tx rings. */
-static void
+static int
 pcnet32_init_ring(struct device *dev)
 {
 	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
 	int i;
-        struct sk_buff *skb;
 
 	lp->lock = 0, lp->tx_full = 0;
 	lp->cur_rx = lp->cur_tx = 0;
 	lp->dirty_rx = lp->dirty_tx = 0;
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
-	    skb = dev_alloc_skb (PKT_BUF_SZ);
-	    if (skb) {
-		lp->rx_skbuff[i] = skb;
-		skb_reserve (skb, 2);
-		lp->rx_ring[i].base = (u32)le32_to_cpu(virt_to_bus(skb->tail));
-		lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ);
-	        lp->rx_ring[i].status = le16_to_cpu(0x8000);
+	    if (lp->rx_skbuff[i] == NULL) {
+		if (!(lp->rx_skbuff[i] = dev_alloc_skb (PKT_BUF_SZ))) {
+		    /* there is not much, we can do at this point */
+		    printk ("%s: pcnet32_init_ring dev_alloc_skb failed.\n",dev->name);
+		    return -1;
+		}
+		skb_reserve (lp->rx_skbuff[i], 2);
 	    }
-	    else
-		break;
+	    lp->rx_ring[i].base = (u32)le32_to_cpu(virt_to_bus(lp->rx_skbuff[i]->tail));
+	    lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ);
+	    lp->rx_ring[i].status = le16_to_cpu(0x8000);
 	}
 	/* The Tx buffer address is filled in as needed, but we do need to clear
 	   the upper ownership bit. */
@@ -540,6 +544,7 @@
 		lp->init_block.phys_addr[i] = dev->dev_addr[i];
 	lp->init_block.rx_ring = (u32)le32_to_cpu(virt_to_bus(lp->rx_ring));
 	lp->init_block.tx_ring = (u32)le32_to_cpu(virt_to_bus(lp->tx_ring));
+	return 0;
 }
 
 static void
@@ -549,7 +554,8 @@
 	unsigned int ioaddr = dev->base_addr;
     
 	pcnet32_purge_tx_ring(dev);
-	pcnet32_init_ring(dev);
+	if (pcnet32_init_ring(dev))
+		return;
     
 	outw(0x0000, ioaddr + PCNET32_ADDR);
         /* ReInit Ring */
@@ -573,15 +579,14 @@
 	/* Transmitter timeout, serious problems. */
 	if (dev->tbusy) {
 		int tickssofar = jiffies - dev->trans_start;
-		if (tickssofar < 20)
+		if (tickssofar < HZ/2)
 			return 1;
 		outw(0, ioaddr+PCNET32_ADDR);
 		printk("%s: transmit timed out, status %4.4x, resetting.\n",
 			   dev->name, inw(ioaddr+PCNET32_DATA));
 		outw(0x0004, ioaddr+PCNET32_DATA);
 		lp->stats.tx_errors++;
-#ifndef final_version
-		{
+		if (pcnet32_debug > 2) {
 			int i;
 			printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
 				   lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",
@@ -596,12 +601,11 @@
 					   lp->tx_ring[i].misc, (unsigned)lp->tx_ring[i].status);
 			printk("\n");
 		}
-#endif
 		pcnet32_restart(dev, 0x0042);
 
 		dev->tbusy = 0;
 		dev->trans_start = jiffies;
-
+		dev_kfree_skb(skb);
 		return 0;
 	}
 
diff -u --recursive --new-file v2.1.119/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v2.1.119/linux/drivers/net/plip.c	Tue Aug 18 22:02:04 1998
+++ linux/drivers/net/plip.c	Thu Aug 27 19:33:08 1998
@@ -244,8 +244,8 @@
 					 plip_wakeup, plip_interrupt, 
 					 PARPORT_DEV_LURK, dev);
 
-	printk(version);
-	printk("%s: Parallel port at %#3lx, using IRQ %d\n", dev->name,
+	printk(KERN_INFO "%s", version);
+	printk(KERN_INFO "%s: Parallel port at %#3lx, using IRQ %d\n", dev->name,
 	       dev->base_addr, dev->irq);
 
 	/* Fill in the generic fields of the device structure. */
@@ -537,7 +537,7 @@
 		/* Malloc up new buffer. */
 		rcv->skb = dev_alloc_skb(rcv->length.h);
 		if (rcv->skb == NULL) {
-			printk(KERN_WARNING "%s: Memory squeeze.\n", dev->name);
+			printk(KERN_ERR "%s: Memory squeeze.\n", dev->name);
 			return ERROR;
 		}
 		skb_put(rcv->skb,rcv->length.h);
@@ -662,7 +662,7 @@
 	unsigned int cx;
 
 	if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) {
-		printk(KERN_ERR "%s: send skb lost\n", dev->name);
+		printk(KERN_DEBUG "%s: send skb lost\n", dev->name);
 		snd->state = PLIP_PK_DONE;
 		snd->skb = NULL;
 		return ERROR;
@@ -817,7 +817,7 @@
 	unsigned char c0;
 
 	if (dev == NULL) {
-		printk(KERN_ERR "plip_interrupt: irq %d for unknown device.\n", irq);
+		printk(KERN_DEBUG "plip_interrupt: irq %d for unknown device.\n", irq);
 		return;
 	}
 
@@ -861,7 +861,7 @@
 
 	case PLIP_CN_ERROR:
 		spin_unlock_irq(&nl->lock);
-		printk(KERN_WARNING "%s: receive interrupt in error state\n", dev->name);
+		printk(KERN_ERR "%s: receive interrupt in error state\n", dev->name);
 		break;
 	}
 }
@@ -1083,7 +1083,8 @@
 	return r;
 }
 
-static int plip_config(struct device *dev, struct ifmap *map)
+static int
+plip_config(struct device *dev, struct ifmap *map)
 {
 	struct net_local *nl = (struct net_local *) dev->priv;
 	struct pardevice *pardev = nl->pardev;
@@ -1091,8 +1092,8 @@
 	if (dev->flags & IFF_UP)
 		return -EBUSY;
 
-	printk(KERN_INFO "plip: Warning, changing irq with ifconfig will be obsoleted.\n");
-	printk(KERN_INFO "plip: Next time, please set with /proc/parport/*/irq instead.\n");
+	printk(KERN_WARNING "plip: Warning, changing irq with ifconfig will be obsoleted.\n");
+	printk(KERN_WARNING "plip: Next time, please set with /proc/parport/*/irq instead.\n");
 
 	if (map->irq != (unsigned char)-1) {
 		pardev->port->irq = dev->irq = map->irq;
@@ -1177,7 +1178,7 @@
 			/* disable driver on "parport=" or "parport=0" */
 			parport[0] = -2;
 		} else {
-			printk(KERN_WARNING "warning: 'plip=0x%x' ignored\n", 
+			printk(KERN_WARNINGING "warning: 'plip=0x%x' ignored\n", 
 			       ints[1]);
 		}
 	}
diff -u --recursive --new-file v2.1.119/linux/drivers/net/sdla_fr.c linux/drivers/net/sdla_fr.c
--- v2.1.119/linux/drivers/net/sdla_fr.c	Wed Jun 24 22:54:06 1998
+++ linux/drivers/net/sdla_fr.c	Fri Sep  4 12:10:19 1998
@@ -158,7 +158,7 @@
 	unsigned long IB_addr;	/* physical address of Interface Byte */
 	unsigned long state_tick;	/* time of the last state change */
 	sdla_t *card;		/* -> owner */
-	struct enet_statistics ifstats;		/* interface statistics */
+	struct net_device_stats ifstats;		/* interface statistics */
 	unsigned long if_send_entry;
 	unsigned long if_send_skb_null;
 	unsigned long if_send_broadcast;
@@ -252,7 +252,7 @@
 	    unsigned short type, void *daddr, void *saddr, unsigned len);
 static int if_rebuild_hdr(struct sk_buff *skb);
 static int if_send(struct sk_buff *skb, struct device *dev);
-static struct enet_statistics *if_stats(struct device *dev);
+static struct net_device_stats *if_stats(struct device *dev);
 /* Interrupt handlers */
 static void fr502_isr(sdla_t * card);
 static void fr508_isr(sdla_t * card);
@@ -1225,12 +1225,15 @@
 
 /*============================================================================
  * Get Ethernet-style interface statistics.
- * Return a pointer to struct enet_statistics.
+ * Return a pointer to struct net_device_stats.
  */
 
 static struct net_device_stats *if_stats(struct device *dev)
 {
 	fr_channel_t *chan = dev->priv;
+	if(chan==NULL)
+		return NULL;
+		
 	return &chan->ifstats;
 }
 
diff -u --recursive --new-file v2.1.119/linux/drivers/net/sdla_ppp.c linux/drivers/net/sdla_ppp.c
--- v2.1.119/linux/drivers/net/sdla_ppp.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/net/sdla_ppp.c	Fri Sep  4 12:10:19 1998
@@ -848,7 +848,16 @@
 static struct enet_statistics *if_stats(struct device *dev)
 {
 	ppp_private_area_t *ppp_priv_area = dev->priv;
-	sdla_t *card = ppp_priv_area->card;
+	sdla_t *card;
+	
+	/*
+	 *	Device is down:No statistics
+	 */
+	 
+	if(ppp_priv_area==NULL)
+		return NULL;
+	
+	card = ppp_priv_area->card;
 	return &card->wandev.stats;
 }
 
diff -u --recursive --new-file v2.1.119/linux/drivers/net/sdla_x25.c linux/drivers/net/sdla_x25.c
--- v2.1.119/linux/drivers/net/sdla_x25.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/net/sdla_x25.c	Fri Sep  4 12:10:19 1998
@@ -96,7 +96,7 @@
 	struct sk_buff* tx_skb;		/* transmit socket buffer */
 	sdla_t* card;			/* -> owner */
 	int ch_idx;
-	struct enet_statistics ifstats;	/* interface statistics */
+	struct net_device_stats ifstats;	/* interface statistics */
 } x25_channel_t;
 
 typedef struct x25_call_info
@@ -829,6 +829,8 @@
 static struct net_device_stats* if_stats (struct device* dev)
 {
 	x25_channel_t* chan = dev->priv;
+	if(chan==NULL)
+		return chan;
 	return &chan->ifstats;
 }
 
diff -u --recursive --new-file v2.1.119/linux/drivers/net/sdladrv.c linux/drivers/net/sdladrv.c
--- v2.1.119/linux/drivers/net/sdladrv.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/net/sdladrv.c	Fri Sep  4 12:10:19 1998
@@ -421,7 +421,7 @@
  			return err;
 		}
 	}
-	else if (!get_option_index(dpmbase_opt, hw->dpmbase))
+	else if (!get_option_index(dpmbase_opt, virt_to_phys((void *)hw->dpmbase)))
 	{
 		printk(KERN_ERR "%s: memory address 0x%lX is illegal!\n",
 			modname, hw->dpmbase)
@@ -437,7 +437,7 @@
 		return -EINVAL;
 	} 
 	printk(KERN_INFO "%s: dual-port memory window is set at 0x%lX.\n",
-		modname, hw->dpmbase)
+		modname, virt_to_phys(hw->dpmbase))
 	;
 	printk(KERN_INFO "%s: found %luK bytes of on-board memory.\n",
 		modname, hw->memory / 1024)
@@ -495,7 +495,7 @@
 }
 
 /*============================================================================
- * Map shared memory window into SDLA address space.
+ * Map shared memory window into SDLA adress space.
  */
 
 EXPORT_SYMBOL(sdla_mapmem);
@@ -980,7 +980,7 @@
 
 	for (i = opt[0]; i && err; --i)
 	{
-		hw->dpmbase = opt[i];
+		hw->dpmbase = (unsigned long )(phys_to_virt(opt[i]));
 		err = sdla_setdpm(hw);
 	}
 	return err;
@@ -1333,7 +1333,7 @@
 	hw->regs[1] = 0xFF;
 
 	/* Verify configuration options */
-	i = get_option_index(s502a_dpmbase_options, hw->dpmbase);
+	i = get_option_index(s502a_dpmbase_options, virt_to_phys((void *)hw->dpmbase));
 	if (i == 0)
 		return -EINVAL
 	;
@@ -1372,7 +1372,7 @@
 	;
 
 	/* Verify configuration options */
-	i = get_option_index(s508_dpmbase_options, hw->dpmbase);
+	i = get_option_index(s508_dpmbase_options, virt_to_phys((void *)hw->dpmbase));
 	if (i == 0)
 		return -EINVAL
 	;
@@ -1416,7 +1416,7 @@
 	;
 
 	/* Verify configuration options */
-	i = get_option_index(s508_dpmbase_options, hw->dpmbase);
+	i = get_option_index(s508_dpmbase_options, virt_to_phys((void *)hw->dpmbase));
 	if (i == 0)
 		return -EINVAL
 	;
@@ -1458,7 +1458,7 @@
 	;
 
 	/* Verify configuration options */
-	i = get_option_index(s507_dpmbase_options, hw->dpmbase);
+	i = get_option_index(s507_dpmbase_options, virt_to_phys((void *)hw->dpmbase));
 	if (i == 0)
 		return -EINVAL
 	;
@@ -1515,7 +1515,7 @@
 	;
 
 	/* Verify configuration options */
-	i = get_option_index(s508_dpmbase_options, hw->dpmbase);
+	i = get_option_index(s508_dpmbase_options, virt_to_phys((void *)hw->dpmbase));
 	if (i == 0)
 		return -EINVAL
 	;
@@ -1776,7 +1776,7 @@
 	int i;
 
 	for (i = 1; i <= optlist[0]; ++i)
-		if (optlist[i] == optval) return i
+		if ( optlist[i] == optval) return i
 	;
 	return 0;
 }
diff -u --recursive --new-file v2.1.119/linux/drivers/net/sdlamain.c linux/drivers/net/sdlamain.c
--- v2.1.119/linux/drivers/net/sdlamain.c	Thu May  7 22:51:50 1998
+++ linux/drivers/net/sdlamain.c	Fri Sep  4 12:10:19 1998
@@ -45,6 +45,7 @@
 #include <linux/wanrouter.h>	/* WAN router definitions */
 #include <linux/wanpipe.h>	/* WANPIPE common user API definitions */
 #include <asm/uaccess.h>	/* kernel <-> user copy */
+#include <asm/io.h>		/* phys_to_virt() */
 
 
 /****** Defines & Macros ****************************************************/
@@ -162,8 +163,13 @@
 			break;
 		}
 	}
-	if (cnt) ncards = cnt;	/* adjust actual number of cards */
-	else kfree(card_array);
+	if (cnt)
+		ncards = cnt;	/* adjust actual number of cards */
+	else
+	{
+		kfree(card_array);
+		err = -ENODEV;
+	}
 	return err;
 }
 
@@ -202,6 +208,7 @@
  * configuration structure is in kernel memory (including extended data, if
  * any).
  */
+ 
 static int setup (wan_device_t* wandev, wandev_conf_t* conf)
 {
 	sdla_t* card;
@@ -210,33 +217,31 @@
 
 	/* Sanity checks */
 	if ((wandev == NULL) || (wandev->private == NULL) || (conf == NULL))
-		return -EFAULT
-	;
+		return -EFAULT;
+		
 	card = wandev->private;
 	if (wandev->state != WAN_UNCONFIGURED)
-		return -EBUSY		/* already configured */
-	;
+		return -EBUSY;		/* already configured */
+
 	if (!conf->data_size || (conf->data == NULL))
 	{
 		printk(KERN_ERR
 			"%s: firmware not found in configuration data!\n",
-			wandev->name)
-		;
+			wandev->name);
 		return -EINVAL;
 	}
 	if (conf->ioport <= 0)
 	{
 		printk(KERN_ERR
 			"%s: can't configure without I/O port address!\n",
-			wandev->name)
-		;
+			wandev->name);
 		return -EINVAL;
 	}
+	
 	if (conf->irq <= 0)
 	{
 		printk(KERN_ERR "%s: can't configure without IRQ!\n",
-			wandev->name)
-		;
+			wandev->name);
 		return -EINVAL;
 	}
 
@@ -245,8 +250,7 @@
 	{
 		printk(KERN_ERR "%s: I/O region 0x%X - 0x%X is in use!\n",
 			wandev->name, conf->ioport,
-			conf->ioport + SDLA_MAXIORANGE)
-		;
+			conf->ioport + SDLA_MAXIORANGE);
 		return -EINVAL;
 	}
 
@@ -255,8 +259,7 @@
 	if (request_irq(irq, sdla_isr, 0, wandev->name, card))
 	{
 		printk(KERN_ERR "%s: can't reserve IRQ %d!\n",
-			wandev->name, irq)
-		;
+			wandev->name, irq);
 		return -EINVAL;
 	}
 
@@ -264,7 +267,12 @@
 	memset(&card->hw, 0, sizeof(sdlahw_t));
 	card->hw.port    = conf->ioport;
 	card->hw.irq     = (conf->irq == 9) ? 2 : conf->irq;
-	card->hw.dpmbase = conf->maddr;
+	/* Compute the virtual address of the card in kernel space */
+	if(conf->maddr)
+		card->hw.dpmbase = (unsigned long)phys_to_virt(conf->maddr);
+	else	/* But 0 means NULL */
+		card->hw.dpmbase = conf->maddr;
+
 	card->hw.dpmsize = SDLA_WINDOWSIZE;
 	card->hw.type    = conf->hw_opt[0];
 	card->hw.pclk    = conf->hw_opt[1];
@@ -323,10 +331,12 @@
 		return err;
 	}
   	/* Reserve I/O region and schedule background task */
+/*	printk(KERN_INFO "about to request\n");*/
 	request_region(card->hw.port, card->hw.io_range, wandev->name);
+/*	printk(KERN_INFO "request done\n");*/
 	if (++active == 1)
-		queue_task(&sdla_tq, &tq_scheduler)
-	;
+		queue_task(&sdla_tq, &tq_scheduler);
+		
 	wandev->critical = 0;
 	return 0;
 }
@@ -345,20 +355,32 @@
 
 	/* sanity checks */
 	if ((wandev == NULL) || (wandev->private == NULL))
-		return -EFAULT
-	;
+		return -EFAULT;
+		
 	if (wandev->state == WAN_UNCONFIGURED)
-		return 0
-	;
+		return 0;
+		
+	/* If wee are in a critical section we lose */
 	if (test_and_set_bit(0, (void*)&wandev->critical))
-		return -EAGAIN
-	;
+		return -EAGAIN;
+		
 	card = wandev->private;
 	wandev->state = WAN_UNCONFIGURED;
-	if (--active == 0) schedule();	/* stop background thread */
+
+	if (--active == 0)
+		schedule();	/* stop background thread */
+	
+/*	printk(KERN_INFO "active now %d\n", active);
+
+	printk(KERN_INFO "About to call sdla_down\n");*/
 	sdla_down(&card->hw);
+/*	printk(KERN_INFO "sdla_down done\n");
+	printk(KERN_INFO "About to call free_irq\n");*/
 	free_irq(wandev->irq, card);
+/*	printk(KERN_INFO "free_irq done\n");
+	printk(KERN_INFO "About to call release_region\n");*/
 	release_region(card->hw.port, card->hw.io_range);
+/*	printk(KERN_INFO "release_region done\n");*/
 	wandev->critical = 0;
 	return 0;
 }
diff -u --recursive --new-file v2.1.119/linux/drivers/net/sunlance.c linux/drivers/net/sunlance.c
--- v2.1.119/linux/drivers/net/sunlance.c	Tue Jul 28 14:21:08 1998
+++ linux/drivers/net/sunlance.c	Thu Aug 27 19:33:08 1998
@@ -1,4 +1,4 @@
-/* $Id: sunlance.c,v 1.79 1998/06/04 09:54:58 jj Exp $
+/* $Id: sunlance.c,v 1.81 1998/08/10 09:08:23 jj Exp $
  * lance.c: Linux/Sparc/Lance driver
  *
  *	Written 1995, 1996 by Miguel de Icaza
@@ -1135,9 +1135,10 @@
 		return ENODEV;
 	called++;
 
-	if (idprom->id_machtype == (SM_SUN4|SM_4_330)) {
+	if ((idprom->id_machtype == (SM_SUN4|SM_4_330)) ||
+	    (idprom->id_machtype == (SM_SUN4|SM_4_470))) {
 		memset (&sdev, 0, sizeof(sdev));
-		sdev.reg_addrs[0].phys_addr = SUN4_300_ETH_PHYSADDR;
+		sdev.reg_addrs[0].phys_addr = sun4_eth_physaddr;
 		sdev.irqs[0] = 6;
 		return sparc_lance_init(dev, &sdev, 0, 0);
 	}
diff -u --recursive --new-file v2.1.119/linux/drivers/pnp/parport_probe.c linux/drivers/pnp/parport_probe.c
--- v2.1.119/linux/drivers/pnp/parport_probe.c	Tue Jul 21 00:15:31 1998
+++ linux/drivers/pnp/parport_probe.c	Fri Aug 28 14:04:03 1998
@@ -6,9 +6,6 @@
  */
 
 #include <linux/tasks.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
 #include <linux/parport.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
@@ -21,7 +18,10 @@
 
 #include <linux/lp.h>
 
+#include <asm/io.h>
+#include <asm/dma.h>
 #include <asm/uaccess.h>
+
 #undef DEBUG_PROBE
 
 static inline int read_nibble(struct parport *port) 
diff -u --recursive --new-file v2.1.119/linux/drivers/scsi/NCR5380.c linux/drivers/scsi/NCR5380.c
--- v2.1.119/linux/drivers/scsi/NCR5380.c	Tue Jun  9 11:57:30 1998
+++ linux/drivers/scsi/NCR5380.c	Fri Sep  4 12:11:42 1998
@@ -32,6 +32,12 @@
 /*
  * $Log: NCR5380.c,v $
 
+ * Revision 1.10 1998/9/2	Alan Cox
+ *				(alan@redhat.com)
+ * Fixed up the timer lockups reported so far. Things still suck. Looking 
+ * forward to 2.3 and per device request queues. Then it'll be possible to
+ * SMP thread this beast and improve life no end.
+ 
  * Revision 1.9  1997/7/27	Ronald van Cuijlenborg
  *				(ronald.van.cuijlenborg@tip.nl or nutty@dds.nl)
  * (hopefully) fixed and enhanced USLEEP
@@ -1524,8 +1530,11 @@
 							{
 								unsigned long timeout = jiffies + NCR_TIMEOUT;
 
+								spin_unlock_irq(&io_request_lock);
 								while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK
 								       && jiffies < timeout);
+								spin_lock_irq(&io_request_lock);
+								
 								if (jiffies >= timeout)
 									printk("scsi%d: timeout at NCR5380.c:%d\n",
 									       host->host_no, __LINE__);
@@ -1665,8 +1674,13 @@
 	{
 		unsigned long timeout = jiffies + 2 * NCR_TIMEOUT;
 
+		spin_unlock_irq(&io_request_lock);
+
 		while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
 		       && jiffies < timeout);
+
+		spin_lock_irq(&io_request_lock);
+		       
 		if (jiffies >= timeout) {
 			printk("scsi: arbitration timeout at %d\n", __LINE__);
 			NCR5380_write(MODE_REG, MR_BASE);
@@ -1825,8 +1839,10 @@
 	hostdata->selecting = 0; /* clear this pointer, because we passed the
 				waiting period */
 #else
+	spin_unlock_irq(&io_request_lock);
 	while ((jiffies < timeout) && !(NCR5380_read(STATUS_REG) &
 					(SR_BSY | SR_IO)));
+	spin_lock_irq(&io_request_lock);
 #endif
 	if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) ==
 	    (SR_SEL | SR_IO)) {
@@ -1894,8 +1910,10 @@
 	{
 		unsigned long timeout = jiffies + NCR_TIMEOUT;
 
+		spin_unlock_irq(&io_request_lock);
 		while (!(NCR5380_read(STATUS_REG) & SR_REQ) && jiffies < timeout);
-
+		spin_lock_irq(&io_request_lock);
+		
 		if (jiffies >= timeout) {
 			printk("scsi%d: timeout at NCR5380.c:%d\n", __LINE__);
 			NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
diff -u --recursive --new-file v2.1.119/linux/drivers/scsi/README.in2000 linux/drivers/scsi/README.in2000
--- v2.1.119/linux/drivers/scsi/README.in2000	Mon Apr  6 17:41:00 1998
+++ linux/drivers/scsi/README.in2000	Thu Aug 27 16:41:29 1998
@@ -1,4 +1,19 @@
 
+UPDATE NEWS: version 1.33 - 26 Aug 98
+
+   Interrupt management in this driver has become, over
+   time, increasingly odd and difficult to explain - this
+   has been mostly due to my own mental inadequacies. In
+   recent kernels, it has failed to function at all when
+   compiled for SMP. I've fixed that problem, and after
+   taking a fresh look at interrupts in general, greatly
+   reduced the number of places where they're fiddled
+   with. Done some heavy testing and it looks very good.
+   The driver now makes use of the __initfunc() and
+   __initdata macros to save about 4k of kernel memory.
+   Once again, the same code works for both 2.0.xx and
+   2.1.xx kernels.
+
 UPDATE NEWS: version 1.32 - 28 Mar 98
 
    Removed the check for legal IN2000 hardware versions:
diff -u --recursive --new-file v2.1.119/linux/drivers/scsi/in2000.c linux/drivers/scsi/in2000.c
--- v2.1.119/linux/drivers/scsi/in2000.c	Thu May 14 19:47:41 1998
+++ linux/drivers/scsi/in2000.c	Thu Aug 27 16:41:29 1998
@@ -117,34 +117,17 @@
 
 #include <linux/blk.h>
 #include <linux/stat.h>
-#include <asm/spinlock.h>
 
 #include "scsi.h"
 #include "sd.h"
 #include "hosts.h"
 
-
-#define IN2000_VERSION    "1.32"
-#define IN2000_DATE       "28/March/1998"
-
-/*
- * Note - the following defines have been moved to 'in2000.h':
- *
- *    PROC_INTERFACE
- *    PROC_STATISTICS
- *    SYNC_DEBUG
- *    DEBUGGING_ON
- *    DEBUG_DEFAULTS
- *    FAST_READ_IO
- *    FAST_WRITE_IO
- *
- */
-
+#define IN2000_VERSION    "1.33"
+#define IN2000_DATE       "26/August/1998"
 
 #include "in2000.h"
 
 
-
 /*
  * 'setup_strings' is a single string used to pass operating parameters and
  * settings from the kernel/module command-line to the driver. 'setup_args[]'
@@ -404,6 +387,10 @@
 
    cmd->SCp.Status = ILLEGAL_STATUS_BYTE;
 
+/* We need to disable interrupts before messing with the input
+ * queue and calling in2000_execute().
+ */
+
    save_flags(flags);
    cli();
 
@@ -443,20 +430,19 @@
  * already connected, we give up immediately. Otherwise, look through
  * the input_Q, using the first command we find that's intended
  * for a currently non-busy target/lun.
+ * Note that this function is always called with interrupts already
+ * disabled (either from in2000_queuecommand() or in2000_intr()).
  */
 static void in2000_execute (struct Scsi_Host *instance)
 {
 struct IN2000_hostdata *hostdata;
 Scsi_Cmnd *cmd, *prev;
-unsigned long flags;
 int i;
 unsigned short *sp;
 unsigned short f;
 unsigned short flushbuf[16];
 
 
-   save_flags(flags);
-   cli();
    hostdata = (struct IN2000_hostdata *)instance->hostdata;
 
 DB(DB_EXECUTE,printk("EX("))
@@ -465,7 +451,6 @@
 
 DB(DB_EXECUTE,printk(")EX-0 "))
 
-      restore_flags(flags);
       return;
       }
 
@@ -489,7 +474,6 @@
 
 DB(DB_EXECUTE,printk(")EX-1 "))
 
-      restore_flags(flags);
       return;
       }
 
@@ -717,7 +701,6 @@
       
 DB(DB_EXECUTE,printk("%s%ld)EX-2 ",(cmd->SCp.phase)?"d:":"",cmd->pid))
 
-   restore_flags(flags);
 }
 
 
@@ -842,15 +825,10 @@
 }
 
 
-/* It appears that the Linux interrupt dispatcher calls this
- * function in a non-reentrant fashion. What that means to us
- * is that we can use an SA_INTERRUPT type of interrupt (which
- * is faster), and do an sti() right away to let timer, serial,
- * etc. ints happen.
- *
- * WHOA! Wait a minute, pardner! Does this hold when more than
- * one card has been detected?? I doubt it. Maybe better
- * re-think the multiple card capability....
+/* We need to use spin_lock_irqsave() & spin_unlock_irqrestore() in this
+ * function in order to work in an SMP environment. (I'd be surprised
+ * if the driver is ever used by anyone on a real multi-CPU motherboard,
+ * but it _does_ need to be able to compile and run in an SMP kernel.)
  */
 
 static void in2000_intr (int irqnum, void * dev_id, struct pt_regs *ptregs)
@@ -863,6 +841,7 @@
 unsigned long length;
 unsigned short *sp;
 unsigned short f;
+unsigned long flags;
 
    for (instance = instance_list; instance; instance = instance->next) {
       if (instance->irq == irqnum)
@@ -874,6 +853,10 @@
       }
    hostdata = (struct IN2000_hostdata *)instance->hostdata;
 
+/* Get the spin_lock and disable further ints, for SMP */
+
+   CLISPIN_LOCK(flags);
+
 #ifdef PROC_STATISTICS
    hostdata->int_cnt++;
 #endif
@@ -1008,6 +991,9 @@
             }
 
       write1_io(0, IO_LED_OFF);
+
+/* release the SMP spin_lock and restore irq state */
+      CLISPIN_UNLOCK(flags);
       return;
       }
 
@@ -1023,6 +1009,9 @@
    if (!cmd && (sr != CSR_RESEL_AM && sr != CSR_TIMEOUT && sr != CSR_SELECT)) {
       printk("\nNR:wd-intr-1\n");
       write1_io(0, IO_LED_OFF);
+
+/* release the SMP spin_lock and restore irq state */
+      CLISPIN_UNLOCK(flags);
       return;
       }
 
@@ -1084,13 +1073,10 @@
 /* Respond to the specific WD3393 interrupt - there are quite a few! */
 
    switch (sr) {
-      unsigned long flags;
 
       case CSR_TIMEOUT:
 DB(DB_INTR,printk("TIMEOUT"))
 
-	 save_flags(flags);
-         cli();
          if (hostdata->state == S_RUNNING_LEVEL2)
             hostdata->connected = NULL;
          else {
@@ -1108,7 +1094,6 @@
  * are commands waiting to be executed.
  */
 
-         restore_flags(flags);
          in2000_execute(instance);
          break;
 
@@ -1116,7 +1101,6 @@
 /* Note: this interrupt should not occur in a LEVEL2 command */
 
       case CSR_SELECT:
-         cli();
 DB(DB_INTR,printk("SELECT"))
          hostdata->connected = cmd = (Scsi_Cmnd *)hostdata->selecting;
 CHECK_NULL(cmd,"csr_select")
@@ -1206,7 +1190,6 @@
       case CSR_SRV_REQ  |PHS_MESS_IN:
 DB(DB_INTR,printk("MSG_IN="))
 
-         cli();
          msg = read_1_byte(hostdata);
          sr = read_3393(hostdata,WD_SCSI_STATUS);  /* clear interrupt */
 
@@ -1355,8 +1338,6 @@
 /* Note: this interrupt will occur only after a LEVEL2 command */
 
       case CSR_SEL_XFER_DONE:
-         save_flags(flags);
-         cli();
 
 /* Make sure that reselection is enabled at this point - it may
  * have been turned off for the command that just completed.
@@ -1383,7 +1364,6 @@
  * there are commands waiting to be executed.
  */
 
-            restore_flags(flags);
             in2000_execute(instance);
             }
          else {
@@ -1442,8 +1422,6 @@
  * so we treat it as a normal command-complete-disconnect.
  */
 
-	 save_flags(flags);
-         cli();
 
 /* Make sure that reselection is enabled at this point - it may
  * have been turned off for the command that just completed.
@@ -1453,6 +1431,9 @@
          if (cmd == NULL) {
             printk(" - Already disconnected! ");
             hostdata->state = S_UNCONNECTED;
+
+/* release the SMP spin_lock and restore irq state */
+            CLISPIN_UNLOCK(flags);
             return;
             }
 DB(DB_INTR,printk("UNEXP_DISC-%ld",cmd->pid))
@@ -1469,14 +1450,11 @@
  * there are commands waiting to be executed.
  */
 
-         restore_flags(flags);
          in2000_execute(instance);
          break;
 
 
       case CSR_DISC:
-         save_flags(flags);
-         cli();
 
 /* Make sure that reselection is enabled at this point - it may
  * have been turned off for the command that just completed.
@@ -1521,7 +1499,6 @@
  * there are commands waiting to be executed.
  */
 
-         restore_flags(flags);
          in2000_execute(instance);
          break;
 
@@ -1529,8 +1506,6 @@
       case CSR_RESEL_AM:
 DB(DB_INTR,printk("RESEL"))
 
-         cli();
-
    /* First we have to make sure this reselection didn't */
    /* happen during Arbitration/Selection of some other device. */
    /* If yes, put losing command back on top of input_Q. */
@@ -1633,16 +1608,12 @@
 
 DB(DB_INTR,printk("} "))
 
+/* release the SMP spin_lock and restore irq state */
+   CLISPIN_UNLOCK(flags);
+
 }
 
-static void do_in2000_intr(int irq, void *dev_id, struct pt_regs *regs)
-{
-   unsigned long flags;
 
-   spin_lock_irqsave(&io_request_lock, flags);
-   in2000_intr(irq, dev_id, regs);
-   spin_unlock_irqrestore(&io_request_lock, flags);
-}
 
 #define RESET_CARD         0
 #define RESET_CARD_AND_BUS 1
@@ -1827,7 +1798,6 @@
       cmd->result = DID_ABORT << 16;
       cmd->scsi_done(cmd);
 
-/*      sti();*/
       in2000_execute (instance);
 
       restore_flags(flags);
@@ -1858,7 +1828,6 @@
  * broke.
  */
 
-/*   sti();*/
    in2000_execute (instance);
 
    restore_flags(flags);
@@ -1876,7 +1845,7 @@
 static char setup_used[MAX_SETUP_ARGS];
 static int done_setup = 0;
 
-void in2000_setup (char *str, int *ints)
+in2000__INITFUNC( void in2000_setup (char *str, int *ints) )
 {
 int i;
 char *p1,*p2;
@@ -1908,7 +1877,7 @@
 /* check_setup_args() returns index if key found, 0 if not
  */
 
-static int check_setup_args(char *key, int *flags, int *val, char *buf)
+in2000__INITFUNC( static int check_setup_args(char *key, int *flags, int *val, char *buf) )
 {
 int x;
 char *cp;
@@ -1940,21 +1909,21 @@
  * special macros declared in 'asm/io.h'. We use readb() and readl()
  * when reading from the card's BIOS area in in2000_detect().
  */
-static const unsigned int *bios_tab[] = {
+static const unsigned int *bios_tab[] in2000__INITDATA = {
    (unsigned int *)0xc8000,
    (unsigned int *)0xd0000,
    (unsigned int *)0xd8000,
    0
    };
 
-static const unsigned short base_tab[] = {
+static const unsigned short base_tab[] in2000__INITDATA = {
    0x220,
    0x200,
    0x110,
    0x100,
    };
 
-static const int int_tab[] = {
+static const int int_tab[] in2000__INITDATA = {
    15,
    14,
    11,
@@ -1962,7 +1931,7 @@
    };
 
 
-int in2000_detect(Scsi_Host_Template * tpnt)
+in2000__INITFUNC( int in2000_detect(Scsi_Host_Template * tpnt) )
 {
 struct Scsi_Host *instance;
 struct IN2000_hostdata *hostdata;
@@ -2068,7 +2037,7 @@
       write1_io(0,IO_FIFO_READ);             /* start fifo out in read mode */
       write1_io(0,IO_INTR_MASK);    /* allow all ints */
       x = int_tab[(switches & (SW_INT0 | SW_INT1)) >> SW_INT_SHIFT];
-      if (request_irq(x, do_in2000_intr, SA_INTERRUPT, "in2000", NULL)) {
+      if (request_irq(x, in2000_intr, SA_INTERRUPT, "in2000", NULL)) {
          printk("in2000_detect: Unable to allocate IRQ.\n");
          detect_count--;
          continue;
diff -u --recursive --new-file v2.1.119/linux/drivers/scsi/in2000.h linux/drivers/scsi/in2000.h
--- v2.1.119/linux/drivers/scsi/in2000.h	Fri Jul 31 17:07:58 1998
+++ linux/drivers/scsi/in2000.h	Fri Sep  4 19:22:13 1998
@@ -2,7 +2,7 @@
  *    in2000.h -  Linux device driver definitions for the
  *                Always IN2000 ISA SCSI card.
  *
- *    IMPORTANT: This file is for version 1.32 - 28/Mar/1998
+ *    IMPORTANT: This file is for version 1.33 - 26/Aug/1998
  *
  * Copyright (c) 1996 John Shifflett, GeoLog Consulting
  *    john@geolog.com
@@ -377,10 +377,29 @@
 #define PR_STOP      1<<7
 
 
-int in2000_detect(Scsi_Host_Template *);
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < 0x020100   /* 2.0.xx */
+# define in2000__INITFUNC(function) function
+# define in2000__INIT
+# define in2000__INITDATA
+# define CLISPIN_LOCK(flags)   do { save_flags(flags); cli(); } while(0)
+# define CLISPIN_UNLOCK(flags) restore_flags(flags)
+#else                               /* 2.1.xxx */
+# include <linux/init.h>
+# include <asm/spinlock.h>
+# define in2000__INITFUNC(function) __initfunc(function)
+# define in2000__INIT __init
+# define in2000__INITDATA __initdata
+# define CLISPIN_LOCK(flags)   spin_lock_irqsave(&io_request_lock, flags)
+# define CLISPIN_UNLOCK(flags) spin_unlock_irqrestore(&io_request_lock, flags)
+#endif
+
+
+int in2000_detect(Scsi_Host_Template *) in2000__INIT;
 int in2000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int in2000_abort(Scsi_Cmnd *);
-void in2000_setup(char *, int *);
+void in2000_setup(char *, int *) in2000__INIT;
 int in2000_proc_info(char *, char **, off_t, int, int, int);
 struct proc_dir_entry proc_scsi_in2000;
 int in2000_biosparam(struct scsi_disk *, kdev_t, int *);
@@ -392,6 +411,33 @@
 #define IN2000_CPL      2
 #define IN2000_HOST_ID  7
 
+#if LINUX_VERSION_CODE < 0x020100   /* 2.0.xx */
+
+#define IN2000 {  NULL,                /* link pointer for modules */ \
+                  NULL,                /* usage_count for modules */ \
+                  &proc_scsi_in2000,   /* pointer to /proc/scsi directory entry */ \
+                  in2000_proc_info,    /* pointer to proc info function */ \
+                  "Always IN2000",     /* device name */ \
+                  in2000_detect,       /* returns number of in2000's found */ \
+                  NULL,                /* optional unload function for modules */ \
+                  NULL,                /* optional misc info function */ \
+                  NULL,                /* send scsi command, wait for completion */ \
+                  in2000_queuecommand, /* queue scsi command, don't wait */ \
+                  in2000_abort,        /* abort current command */ \
+                  in2000_reset,        /* reset scsi bus */ \
+                  NULL,                /* slave_attach - unused */ \
+                  in2000_biosparam,    /* figures out BIOS parameters for lilo, etc */ \
+                  IN2000_CAN_Q,        /* max commands we can queue up */ \
+                  IN2000_HOST_ID,      /* host-adapter scsi id */ \
+                  IN2000_SG,           /* scatter-gather table size */ \
+                  IN2000_CPL,          /* commands per lun */ \
+                  0,                   /* board counter */ \
+                  0,                   /* unchecked dma */ \
+                  DISABLE_CLUSTERING \
+               }
+
+#else       /* 2.1.xxx */
+
 #define IN2000 {  proc_dir:        &proc_scsi_in2000,   /* pointer to /proc/scsi directory entry */ \
                   proc_info:       in2000_proc_info,    /* pointer to proc info function */ \
                   name:            "Always IN2000",     /* device name */ \
@@ -407,6 +453,8 @@
                   use_clustering:  DISABLE_CLUSTERING,  /* ENABLE_CLUSTERING may speed things up */ \
                   use_new_eh_code: 0                    /* new error code - not using it yet */ \
                 }
+
+#endif
 
 
 #endif /* IN2000_H */
diff -u --recursive --new-file v2.1.119/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c
--- v2.1.119/linux/drivers/scsi/seagate.c	Wed Aug 26 11:37:38 1998
+++ linux/drivers/scsi/seagate.c	Mon Aug 31 10:32:19 1998
@@ -87,7 +87,6 @@
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/string.h>
-#include <linux/config.h>
 #include <linux/proc_fs.h>
 #include <linux/init.h>
 
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.1.119/linux/drivers/sound/Config.in	Wed Aug 26 11:37:39 1998
+++ linux/drivers/sound/Config.in	Fri Sep  4 15:32:26 1998
@@ -35,6 +35,18 @@
    int 'MSND Pinnacle IRQ 5,7,9,10,11,12' CONFIG_MSNDPIN_IRQ 5
    hex 'MSND Pinnacle Memory B0000,C8000,D0000,D8000,E0000,E8000' CONFIG_MSNDPIN_MEM D0000
    hex 'MSND Pinnacle I/O 210,220,230,240,250,260,290,3E0' CONFIG_MSNDPIN_IO 290
+   bool 'MSND Pinnacle Non-PnP Mode' CONFIG_MSNDPIN_NONPNP
+   if [ "$CONFIG_MSNDPIN_NONPNP" = "y" ]; then
+     comment 'MSND Pinnacle DSP section will be configured to above parameters.'
+     hex 'MSDN Pinnacle Config Port 250,260,270' CONFIG_MSNDPIN_CFG 250
+     comment 'Pinnacle-specific Device Configuration (0 disables)'
+     hex 'MSDN Pinnacle MPU I/O (e.g. 330)' CONFIG_MSNDPIN_MPU_IO 0
+     int 'MSDN Pinnacle MPU IRQ (e.g. 9)' CONFIG_MSNDPIN_MPU_IRQ 0
+     hex 'MSDN Pinnacle IDE I/O 0 (e.g. 170)' CONFIG_MSNDPIN_IDE_IO0 0
+     hex 'MSDN Pinnacle IDE I/O 1 (e.g. 376)' CONFIG_MSNDPIN_IDE_IO1 0
+     int 'MSDN Pinnacle IDE IRQ (e.g. 15)' CONFIG_MSNDPIN_IDE_IRQ 0
+     hex 'MSDN Pinnacle Joystick I/O (e.g. 200)' CONFIG_MSNDPIN_JOYSTICK_IO 0
+   fi
 fi
 
 dep_tristate 'OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/README.FIRST linux/drivers/sound/README.FIRST
--- v2.1.119/linux/drivers/sound/README.FIRST	Sat Nov 29 10:33:20 1997
+++ linux/drivers/sound/README.FIRST	Wed Sep  2 13:22:29 1998
@@ -1,4 +1,4 @@
-The modular sound driver patches where funded by Red Hat Software 
+The modular sound driver patches were funded by Red Hat Software 
 (www.redhat.com). The sound driver here is thus a modified version of 
 Hannu's code. Please bear that in mind when considering the appropriate
 forums for bug reporting. 
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/audio.c linux/drivers/sound/audio.c
--- v2.1.119/linux/drivers/sound/audio.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/audio.c	Fri Sep  4 15:32:26 1998
@@ -17,6 +17,7 @@
  *                   (which is grossly misnamed btw.) because they have the same
  *                   lifetime as the rest in there and dynamic allocation saves
  *                   12k or so
+ * Thomas Sailer   : use more logical O_NONBLOCK semantics
  */
 
 #include <linux/config.h>
@@ -99,7 +100,6 @@
 		set_format(dev, bits);
 
 	audio_devs[dev]->audio_mode = AM_NONE;
-	audio_devs[dev]->dev_nblock = 0;
 
 
 	return ret;
@@ -203,10 +203,10 @@
 	
 	while (c)
 	{
-		if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, audio_devs[dev]->dev_nblock)) < 0)
+		if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, !!(file->f_flags & O_NONBLOCK))) < 0)
 		{
 			    /* Handle nonblocking mode */
-			if (audio_devs[dev]->dev_nblock && err == -EAGAIN)
+			if ((file->f_flags & O_NONBLOCK) && err == -EAGAIN)
 				return p;	/* No more space. Return # of accepted bytes */
 			return err;
 		}
@@ -273,14 +273,13 @@
 
 	while(c)
 	{
-		if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l,
-			audio_devs[dev]->dev_nblock)) < 0)
+		if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, !!(file->f_flags & O_NONBLOCK))) < 0)
 		{
 			/*
 			 *	Nonblocking mode handling. Return current # of bytes
 			 */
 
-			if (audio_devs[dev]->dev_nblock && buf_no == -EAGAIN)
+			if (file->f_flags & O_NONBLOCK && buf_no == -EAGAIN)
 				return p;
 
 			if (p > 0) 		/* Avoid throwing away data */
@@ -388,7 +387,7 @@
 			return dma_ioctl(dev, cmd, arg);
 		
 		case SNDCTL_DSP_NONBLOCK:
-  			audio_devs[dev]->dev_nblock = 1;
+			file->f_flags |= O_NONBLOCK;
 			return 0;
 
 		case SNDCTL_DSP_GETCAPS:
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/dev_table.h linux/drivers/sound/dev_table.h
--- v2.1.119/linux/drivers/sound/dev_table.h	Tue Jul 21 00:15:31 1998
+++ linux/drivers/sound/dev_table.h	Fri Sep  4 15:32:26 1998
@@ -230,8 +230,6 @@
 
 	/* fields formerly in audio.c */
 	int audio_mode;
-	/* why don't we use file->f_flags & O_NONBLOCK for the following? - ts */
-	int dev_nblock;	/* 1 if in nonblocking mode */
 
 #define		AM_NONE		0
 #define		AM_WRITE	OPEN_WRITE
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
--- v2.1.119/linux/drivers/sound/dmabuf.c	Thu Jul 16 18:09:26 1998
+++ linux/drivers/sound/dmabuf.c	Wed Sep  2 13:23:22 1998
@@ -206,6 +206,7 @@
 		dmap->dma_mode = DMODE_NONE;
 	dmap->flags &= ~DMA_BUSY;
 	disable_dma(dmap->dma);
+	sound_free_dmap(dmap);
 }
 
 
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/es1370.c linux/drivers/sound/es1370.c
--- v2.1.119/linux/drivers/sound/es1370.c	Wed Aug 26 11:37:39 1998
+++ linux/drivers/sound/es1370.c	Fri Sep  4 15:32:26 1998
@@ -1,7 +1,7 @@
 /*****************************************************************************/
 
 /*
- *      es1370.c  --  Ensoniq ES1370/Ashai Kasei AK4531 audio driver.
+ *      es1370.c  --  Ensoniq ES1370/Asahi Kasei AK4531 audio driver.
  *
  *      Copyright (C) 1998  Thomas Sailer (sailer@ife.ee.ethz.ch)
  *
@@ -73,6 +73,9 @@
  *                     On module startup, set DAC2 to 11kSPS instead of 5.5kSPS,
  *                     as it produces an annoying ssssh in the lower sampling rate
  *                     Do not include modversions.h
+ *    22.08.98   0.12  Mixer registers actually have 5 instead of 4 bits
+ *                     pointed out by Itai Nahshon
+ *    31.08.98   0.13  Fix realplayer problems - dac.count issues
  *
  * some important things missing in Ensoniq documentation:
  *
@@ -455,7 +458,7 @@
 	unsigned fragremain, fshift;
 
 	spin_lock_irqsave(&s->lock, flags);
-	if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < s->dma_adc.dmasize - 2*s->dma_adc.fragsize)
+	if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize))
 	    && s->dma_adc.ready) {
 		s->ctrl |= CTRL_ADC_EN;
 		s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN;
@@ -486,7 +489,6 @@
 		for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
 			clear_bit(PG_reserved, &mem_map[map].flags);	
 		free_pages((unsigned long)db->rawbuf, db->buforder);
-		printk(KERN_DEBUG "es: freeing dmabuf %8.8lx order %d\n", (unsigned long)db->rawbuf, db->buforder);
 	}
 	db->rawbuf = NULL;
 	db->mapped = db->ready = 0;
@@ -511,7 +513,6 @@
 		mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
 		for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
 			set_bit(PG_reserved, &mem_map[map].flags);
-		printk(KERN_DEBUG "es: allocating dmabuf %8.8lx order %d\n", (unsigned long)db->rawbuf, db->buforder);
 	}
 	fmt &= ES1370_FMT_MASK;
 	bytepersec = rate << sample_shift[fmt];
@@ -598,10 +599,10 @@
 		s->dma_adc.total_bytes += diff;
 		s->dma_adc.count += diff;
 		if (s->dma_adc.mapped) {
-			if (s->dma_adc.count >= s->dma_adc.fragsize) 
+			if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) 
 				wake_up(&s->dma_adc.wait);
 		} else {
-			if (s->dma_adc.count > s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1)) {
+			if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
 				s->ctrl &= ~CTRL_ADC_EN;
 				outl(s->ctrl, s->io+ES1370_REG_CONTROL);
 				s->dma_adc.error++;
@@ -616,7 +617,7 @@
 		s->dma_dac1.total_bytes += diff;
 		if (s->dma_dac1.mapped) {
 			s->dma_dac1.count += diff;
-			if (s->dma_dac1.count >= s->dma_dac1.fragsize)
+			if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize)
 				wake_up(&s->dma_dac1.wait);
 		} else {
 			s->dma_dac1.count -= diff;
@@ -624,12 +625,12 @@
 				s->ctrl &= ~CTRL_DAC1_EN;
 				outl(s->ctrl, s->io+ES1370_REG_CONTROL);
 				s->dma_dac1.error++;
-			} else if (s->dma_dac1.count <= s->dma_dac1.fragsize && !s->dma_dac1.endcleared) {
+			} else if (s->dma_dac1.count <= (signed)s->dma_dac1.fragsize && !s->dma_dac1.endcleared) {
 				clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, 
 					      s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80);
 				s->dma_dac1.endcleared = 1;
 			}
-			if (s->dma_dac1.count < s->dma_dac1.dmasize)
+			if (s->dma_dac1.count < (signed)s->dma_dac1.dmasize)
 				wake_up(&s->dma_dac1.wait);
 		}
 	}
@@ -639,7 +640,7 @@
 		s->dma_dac2.total_bytes += diff;
 		if (s->dma_dac2.mapped) {
 			s->dma_dac2.count += diff;
-			if (s->dma_dac2.count >= s->dma_dac2.fragsize)
+			if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize)
 				wake_up(&s->dma_dac2.wait);
 		} else {
 			s->dma_dac2.count -= diff;
@@ -647,12 +648,12 @@
 				s->ctrl &= ~CTRL_DAC2_EN;
 				outl(s->ctrl, s->io+ES1370_REG_CONTROL);
 				s->dma_dac2.error++;
-			} else if (s->dma_dac2.count <= s->dma_dac2.fragsize && !s->dma_dac2.endcleared) {
+			} else if (s->dma_dac2.count <= (signed)s->dma_dac2.fragsize && !s->dma_dac2.endcleared) {
 				clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, 
 					      s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80);
 				s->dma_dac2.endcleared = 1;
 			}
-			if (s->dma_dac2.count < s->dma_dac2.dmasize)
+			if (s->dma_dac2.count < (signed)s->dma_dac2.dmasize)
 				wake_up(&s->dma_dac2.wait);
 		}
 	}
@@ -757,7 +758,6 @@
 	unsigned char l, r, rl, rr;
 
 	VALIDATE_STATE(s);
-
 	if (cmd == SOUND_MIXER_PRIVATE1) {
 		/* enable/disable/query mixer preamp */
 		get_user_ret(val, (int *)arg, -EFAULT);
@@ -886,19 +886,19 @@
 			r = (val >> 8) & 0xff;
 			if (r > 100)
 				r = 100;
-			if (l < 10) {
+			if (l < 7) {
 				rl = 0x80;
 				l = 0;
 			} else {
-				rl = 15 - ((l - 10) / 6);
-				l = (15 - rl) * 6 + 10;
+				rl = 31 - ((l - 7) / 3);
+				l = (31 - rl) * 3 + 7;
 			}
-			if (r < 10) {
+			if (r < 7) {
 				rr = 0x80;
 				r = 0;
 			} else {
-				rr =  15 - ((r - 10) / 6);
-				r = (15 - rr) * 6 + 10;
+				rr =  31 - ((r - 7) / 3);
+				r = (31 - rr) * 3 + 7;
 			}
 			wrcodec(s, mixtable[i].right, rr);
 		} else { 
@@ -911,12 +911,12 @@
 					r = l = (7 - rl) * 14 + 2;
 				}
 			} else {
-				if (l < 10) {
+				if (l < 7) {
 					rl = 0x80;
 					r = l = 0;
 				} else {
-					rl = 15 - ((l - 10) / 6);
-					r = l = (15 - rl) * 6 + 10;
+					rl = 31 - ((l - 7) / 3);
+					r = l = (31 - rl) * 3 + 7;
 				}
 			}
 		}
@@ -982,10 +982,8 @@
 	NULL,  /* fsync */
 	NULL,  /* fasync */
 	NULL,  /* check_media_change */
-#if 0
 	NULL,  /* revalidate */
 	NULL,  /* lock */
-#endif
 };
 
 /* --------------------------------------------------------------------- */
@@ -1190,7 +1188,7 @@
 	es1370_update_ptr(s);
 	if (file->f_mode & FMODE_READ) {
 		if (s->dma_adc.mapped) {
-			if (s->dma_adc.count >= s->dma_adc.fragsize)
+			if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
 				mask |= POLLIN | POLLRDNORM;
 		} else {
 			if (s->dma_adc.count > 0)
@@ -1199,10 +1197,10 @@
 	}
 	if (file->f_mode & FMODE_WRITE) {
 		if (s->dma_dac2.mapped) {
-			if (s->dma_dac2.count >= s->dma_dac2.fragsize) 
+			if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) 
 				mask |= POLLOUT | POLLWRNORM;
 		} else {
-			if (s->dma_dac2.dmasize > s->dma_dac2.count)
+			if ((signed)s->dma_dac2.dmasize > s->dma_dac2.count)
 				mask |= POLLOUT | POLLWRNORM;
 		}
 	}
@@ -1704,10 +1702,10 @@
 	spin_lock_irqsave(&s->lock, flags);
 	es1370_update_ptr(s);
 	if (s->dma_dac1.mapped) {
-		if (s->dma_dac1.count >= s->dma_dac1.fragsize)
+		if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize)
 			mask |= POLLOUT | POLLWRNORM;
 	} else {
-		if (s->dma_dac1.dmasize > s->dma_dac1.count)
+		if ((signed)s->dma_dac1.dmasize > s->dma_dac1.count)
 			mask |= POLLOUT | POLLWRNORM;
 	}
 	spin_unlock_irqrestore(&s->lock, flags);
@@ -2280,7 +2278,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "es1370: version v0.10 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "es1370: version v0.13 time " __TIME__ " " __DATE__ "\n");
 	while (index < NR_DEVICE && 
 	       (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) {
 		if (pcidev->base_address[0] == 0 || 
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/es1371.c linux/drivers/sound/es1371.c
--- v2.1.119/linux/drivers/sound/es1371.c	Wed Aug 26 11:37:39 1998
+++ linux/drivers/sound/es1371.c	Fri Sep  4 15:32:27 1998
@@ -48,6 +48,7 @@
  *    03.08.98   0.3   Do not include modversions.h
  *                     Now mixer behaviour can basically be selected between
  *                     "OSS documented" and "OSS actual" behaviour
+ *    31.08.98   0.4   Fix realplayer problems - dac.count issues
  *
  */
 
@@ -693,7 +694,7 @@
 	unsigned fragremain, fshift;
 
 	spin_lock_irqsave(&s->lock, flags);
-	if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < s->dma_adc.dmasize - 2*s->dma_adc.fragsize)
+	if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize))
 	    && s->dma_adc.ready) {
 		s->ctrl |= CTRL_ADC_EN;
 		s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN;
@@ -838,7 +839,7 @@
 			if (s->dma_adc.count >= s->dma_adc.fragsize) 
 				wake_up(&s->dma_adc.wait);
 		} else {
-			if (s->dma_adc.count > s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1)) {
+			if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
 				s->ctrl &= ~CTRL_ADC_EN;
 				outl(s->ctrl, s->io+ES1371_REG_CONTROL);
 				s->dma_adc.error++;
@@ -853,7 +854,7 @@
 		s->dma_dac1.total_bytes += diff;
 		if (s->dma_dac1.mapped) {
 			s->dma_dac1.count += diff;
-			if (s->dma_dac1.count >= s->dma_dac1.fragsize)
+			if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize)
 				wake_up(&s->dma_dac1.wait);
 		} else {
 			s->dma_dac1.count -= diff;
@@ -861,12 +862,12 @@
 				s->ctrl &= ~CTRL_DAC1_EN;
 				outl(s->ctrl, s->io+ES1371_REG_CONTROL);
 				s->dma_dac1.error++;
-			} else if (s->dma_dac1.count <= s->dma_dac1.fragsize && !s->dma_dac1.endcleared) {
+			} else if (s->dma_dac1.count <= (signed)s->dma_dac1.fragsize && !s->dma_dac1.endcleared) {
 				clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, 
 					      s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80);
 				s->dma_dac1.endcleared = 1;
 			}
-			if (s->dma_dac1.count < s->dma_dac1.dmasize)
+			if (s->dma_dac1.count < (signed)s->dma_dac1.dmasize)
 				wake_up(&s->dma_dac1.wait);
 		}
 	}
@@ -876,7 +877,7 @@
 		s->dma_dac2.total_bytes += diff;
 		if (s->dma_dac2.mapped) {
 			s->dma_dac2.count += diff;
-			if (s->dma_dac2.count >= s->dma_dac2.fragsize)
+			if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize)
 				wake_up(&s->dma_dac2.wait);
 		} else {
 			s->dma_dac2.count -= diff;
@@ -884,12 +885,12 @@
 				s->ctrl &= ~CTRL_DAC2_EN;
 				outl(s->ctrl, s->io+ES1371_REG_CONTROL);
 				s->dma_dac2.error++;
-			} else if (s->dma_dac2.count <= s->dma_dac2.fragsize && !s->dma_dac2.endcleared) {
+			} else if (s->dma_dac2.count <= (signed)s->dma_dac2.fragsize && !s->dma_dac2.endcleared) {
 				clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, 
 					      s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80);
 				s->dma_dac2.endcleared = 1;
 			}
-			if (s->dma_dac2.count < s->dma_dac2.dmasize)
+			if (s->dma_dac2.count < (signed)s->dma_dac2.dmasize)
 				wake_up(&s->dma_dac2.wait);
 		}
 	}
@@ -1636,7 +1637,7 @@
 	es1371_update_ptr(s);
 	if (file->f_flags & FMODE_READ) {
 		if (s->dma_adc.mapped) {
-			if (s->dma_adc.count >= s->dma_adc.fragsize)
+			if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
 				mask |= POLLIN | POLLRDNORM;
 		} else {
 			if (s->dma_adc.count > 0)
@@ -1645,10 +1646,10 @@
 	}
 	if (file->f_flags & FMODE_WRITE) {
 		if (s->dma_dac2.mapped) {
-			if (s->dma_dac2.count >= s->dma_dac2.fragsize) 
+			if (s->dma_dac2.count >= (signed)s->dma_dac2.fragsize) 
 				mask |= POLLOUT | POLLWRNORM;
 		} else {
-			if (s->dma_dac2.dmasize > s->dma_dac2.count)
+			if ((signed)s->dma_dac2.dmasize > s->dma_dac2.count)
 				mask |= POLLOUT | POLLWRNORM;
 		}
 	}
@@ -2150,10 +2151,10 @@
 	spin_lock_irqsave(&s->lock, flags);
 	es1371_update_ptr(s);
 	if (s->dma_dac1.mapped) {
-		if (s->dma_dac1.count >= s->dma_dac1.fragsize)
+		if (s->dma_dac1.count >= (signed)s->dma_dac1.fragsize)
 			mask |= POLLOUT | POLLWRNORM;
 	} else {
-		if (s->dma_dac1.dmasize > s->dma_dac1.count)
+		if ((signed)s->dma_dac1.dmasize > s->dma_dac1.count)
 			mask |= POLLOUT | POLLWRNORM;
 	}
 	spin_unlock_irqrestore(&s->lock, flags);
@@ -2716,7 +2717,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "es1371: version v0.3 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "es1371: version v0.4 time " __TIME__ " " __DATE__ "\n");
 	while (index < NR_DEVICE && 
 	       (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) {
 		if (pcidev->base_address[0] == 0 || 
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/lowlevel/awe_compat.h linux/drivers/sound/lowlevel/awe_compat.h
--- v2.1.119/linux/drivers/sound/lowlevel/awe_compat.h	Tue Oct 28 02:10:50 1997
+++ linux/drivers/sound/lowlevel/awe_compat.h	Fri Aug 28 10:44:06 1998
@@ -92,7 +92,7 @@
 	get_user(target, (unsigned char*)&((addr)[offs]))
 #define GET_SHORT_FROM_USER(target,addr,offs) \
 	get_user(target, (unsigned short*)&((addr)[offs]))
-#ifdef AWE_OSS38
+#ifdef AWE_OSS38_AND_IM_A_BANANA
 #define IOCTL_TO_USER(target,offs,source,count) \
 	memcpy(target, (source)+(offs), count)
 #define IO_WRITE_CHECK(cmd)	(_SIOC_DIR(cmd) & _IOC_WRITE)
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/msnd.c linux/drivers/sound/msnd.c
--- v2.1.119/linux/drivers/sound/msnd.c	Tue Jul 28 14:21:08 1998
+++ linux/drivers/sound/msnd.c	Fri Sep  4 15:32:27 1998
@@ -20,7 +20,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd.c,v 1.5 1998/07/18 00:12:15 andrewtv Exp $
+ * $Id: msnd.c,v 1.9 1998/09/04 18:41:27 andrewtv Exp $
  *
  ********************************************************************/
 
@@ -168,16 +168,8 @@
 		}
 
 		if (user) {
-#ifdef LINUX20
-			if (verify_area(VERIFY_READ, buf , nwritten))
-				return nwritten;
-			
-			memcpy_fromfs(f->data + f->tail, buf, nwritten);
-#else
 			if (copy_from_user(f->data + f->tail, buf, nwritten))
 				return -EFAULT;
-#endif
-
 		} else
 			memcpy(f->data + f->tail, buf, nwritten);
 
@@ -214,15 +206,8 @@
 		}
 		
 		if (user) {
-#ifdef LINUX20
-			if (verify_area(VERIFY_WRITE, buf, nread))
-				return nread;
-			
-			memcpy_tofs(buf, f->data + f->head, nread);
-#else
 			if (copy_to_user(buf, f->data + f->head, nread))
 				return -EFAULT;
-#endif
 		} else
 			memcpy(buf, f->data + f->head, nread);
 		
@@ -356,8 +341,10 @@
 	if (--dev->irq_ref > 0)
 		return 0;
 
-	if (dev->irq_ref < 0)
-		dev->irq_ref = 0;
+	if (dev->irq_ref < 0) {
+		printk(KERN_WARNING LOGNAME ": IRQ ref count is %d\n", dev->irq_ref);
+/*		dev->irq_ref = 0; */
+	}
 
 	printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n");
 	
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/msnd.h linux/drivers/sound/msnd.h
--- v2.1.119/linux/drivers/sound/msnd.h	Thu Aug 20 17:05:16 1998
+++ linux/drivers/sound/msnd.h	Fri Sep  4 15:32:27 1998
@@ -24,16 +24,16 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd.h,v 1.9 1998/08/06 21:06:14 andrewtv Exp $
+ * $Id: msnd.h,v 1.18 1998/09/04 18:43:40 andrewtv Exp $
  *
  ********************************************************************/
 #ifndef __MSND_H
 #define __MSND_H
 
-#define VERSION			"0.7.2"
+#define VERSION			"0.7.13"
 
 #define DEFSAMPLERATE		DSP_DEFAULT_SPEED
-#define DEFSAMPLESIZE		8
+#define DEFSAMPLESIZE		AFMT_U8
 #define DEFCHANNELS		1
 
 #define DEFFIFOSIZE		64
@@ -142,9 +142,9 @@
 #define	HDEX_MIDI_OUT_STOP	(9 + HDEX_BASE)
 #define	HDEX_AUX_REQ		(10 + HDEX_BASE)
 
-#define HIWORD(l)		((WORD)((((DWORD)(l)) >> 16) & 0xFFFF ))
+#define HIWORD(l)		((WORD)((((DWORD)(l)) >> 16) & 0xFFFF))
 #define LOWORD(l)		((WORD)(DWORD)(l))
-#define HIBYTE(w)		((BYTE)(((WORD)(w) >> 8 ) & 0xFF ))
+#define HIBYTE(w)		((BYTE)(((WORD)(w) >> 8) & 0xFF))
 #define LOBYTE(w)		((BYTE)(w))
 #define MAKELONG(low,hi)	((long)(((WORD)(low))|(((DWORD)((WORD)(hi)))<<16)))
 #define MAKEWORD(low,hi)	((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8)))
@@ -166,33 +166,31 @@
 #  define spin_unlock_irqrestore(junk,flags)	do { restore_flags(flags); } while (0)
 #endif
 
-typedef unsigned char		BYTE;
-typedef unsigned short		USHORT;
-typedef unsigned short		WORD;
-typedef unsigned int		DWORD;
-typedef 
-struct DAQueueDataStruct *	LPDAQD;
-
-#define GCC_PACKED		__attribute__ ((packed))
-
-struct JobQueueStruct {
-	WORD wStart;
-	WORD wSize;
-	WORD wHead;
-	WORD wTail;
-} GCC_PACKED;
-
-struct DAQueueDataStruct {
-	WORD wStart;
-	WORD wSize;
-	WORD wFormat;
-	WORD wSampleSize;
-	WORD wChannels;
-	WORD wSampleRate;
-	WORD wIntMsg;
-	WORD wFlags;
-} GCC_PACKED;
+/* JobQueueStruct */
+#define JQS_wStart		0x00
+#define JQS_wSize		0x02
+#define JQS_wHead		0x04
+#define JQS_wTail		0x06
+#define JQS__size		0x08
+
+/* DAQueueDataStruct */
+#define DAQDS_wStart		0x00
+#define DAQDS_wSize		0x02
+#define DAQDS_wFormat		0x04
+#define DAQDS_wSampleSize	0x06
+#define DAQDS_wChannels		0x08
+#define DAQDS_wSampleRate	0x0A
+#define DAQDS_wIntMsg		0x0C
+#define DAQDS_wFlags		0x0E
+#define DAQDS__size		0x10
+
+typedef u8			BYTE;
+typedef u16			USHORT;
+typedef u16			WORD;
+typedef u32			DWORD;
+typedef volatile BYTE *		LPDAQD;
 
+/* Generic FIFO */
 typedef struct {
 	size_t n, len;
 	char *data;
@@ -200,12 +198,12 @@
 } msnd_fifo;
 
 typedef struct multisound_dev {
-
+	/* Linux device info */
 	char *name;
 	int dsp_minor, mixer_minor;
 
 	/* Hardware resources */
-	unsigned int io, numio;
+	int io, numio;
 	int memid, irqid;
 	int irq, irq_ref;
 	unsigned char info;
@@ -214,16 +212,14 @@
 	spinlock_t lock;
 #endif
 
-	/* MultiSound DDK variables */
-	enum { msndClassic, msndPinnacle } type;
-	struct SMA0_CommonData *SMA;	/* diff. structure for classic vs. pinnacle */
-	struct DAQueueDataStruct *CurDAQD;
-	struct DAQueueDataStruct *CurDARQD;
-	volatile WORD *pwDSPQData , *pwMIDQData , *pwMODQData;
-	volatile struct JobQueueStruct *DAPQ , *DARQ , *MODQ , *MIDQ , *DSPQ;
-	BYTE bCurrentMidiPatch;
+	/* Motorola 56k DSP SMA */
+	volatile BYTE *SMA;
+	volatile BYTE *CurDAQD, *CurDARQD;
+	volatile BYTE *DAPQ, *DARQ, *MODQ, *MIDQ, *DSPQ;
+	volatile WORD *pwDSPQData, *pwMIDQData, *pwMODQData;
 
 	/* State variables */
+	enum { msndClassic, msndPinnacle } type;
 	mode_t mode;
 	unsigned long flags;
 #define F_BANKONE			0
@@ -237,7 +233,6 @@
 #define F_INT_MIDI_INUSE		8
 #define F_WRITEFLUSH			9
 #define F_HAVEDIGITAL			10
-
 	struct wait_queue *writeblock, *readblock;
 	struct wait_queue *writeflush;
 	unsigned long recsrc;
@@ -248,17 +243,17 @@
 	int sample_size;
 	int sample_rate;
 	int channels;
+	BYTE bCurrentMidiPatch;
 	void (*inc_ref)(void);
 	void (*dec_ref)(void);
 
 	/* Digital audio FIFOs */
-	int fifosize;
 	msnd_fifo DAPF, DARF;
+	int fifosize;
 	int lastbank;
 
 	/* MIDI in callback */
 	void (*midi_in_interrupt)(struct multisound_dev *);
-
 } multisound_dev_t;
 
 #ifndef mdelay
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/msnd_classic.h linux/drivers/sound/msnd_classic.h
--- v2.1.119/linux/drivers/sound/msnd_classic.h	Tue Jul 28 14:21:08 1998
+++ linux/drivers/sound/msnd_classic.h	Fri Sep  4 15:32:27 1998
@@ -24,7 +24,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  * 
- * $Id: msnd_classic.h,v 1.5 1998/07/18 00:12:15 andrewtv Exp $
+ * $Id: msnd_classic.h,v 1.7 1998/09/03 06:39:47 andrewtv Exp $
  *
  ********************************************************************/
 #ifndef __MSND_CLASSIC_H
@@ -32,133 +32,133 @@
 
 #include <linux/config.h>
 
-#define DSP_NUMIO		0x10
+#define DSP_NUMIO				0x10
 
-#define	HP_MEMM			0x08
+#define	HP_MEMM					0x08
 
-#define	HP_BITM			0x0E
-#define	HP_WAIT			0x0D
-#define	HP_DSPR			0x0A
-#define	HP_PROR			0x0B
-#define	HP_BLKS			0x0C
+#define	HP_BITM					0x0E
+#define	HP_WAIT					0x0D
+#define	HP_DSPR					0x0A
+#define	HP_PROR					0x0B
+#define	HP_BLKS					0x0C
 
-#define	HPPRORESET_OFF		0
-#define HPPRORESET_ON		1
+#define	HPPRORESET_OFF				0
+#define HPPRORESET_ON				1
 
-#define HPDSPRESET_OFF		0
-#define HPDSPRESET_ON		1
+#define HPDSPRESET_OFF				0
+#define HPDSPRESET_ON				1
 
-#define HPBLKSEL_0		0
-#define HPBLKSEL_1		1
+#define HPBLKSEL_0				0
+#define HPBLKSEL_1				1
 
-#define HPWAITSTATE_0		0
-#define HPWAITSTATE_1		1
+#define HPWAITSTATE_0				0
+#define HPWAITSTATE_1				1
 
-#define HPBITMODE_16		0
-#define HPBITMODE_8		1
+#define HPBITMODE_16				0
+#define HPBITMODE_8				1
 
-#define	HIDSP_INT_PLAY_UNDER	0x00
-#define	HIDSP_INT_RECORD_OVER	0x01
-#define	HIDSP_INPUT_CLIPPING	0x02
-#define	HIDSP_MIDI_IN_OVER	0x10
+#define	HIDSP_INT_PLAY_UNDER			0x00
+#define	HIDSP_INT_RECORD_OVER			0x01
+#define	HIDSP_INPUT_CLIPPING			0x02
+#define	HIDSP_MIDI_IN_OVER			0x10
 #define	HIDSP_MIDI_OVERRUN_ERR  0x13
 
-#define	HDEXAR_CLEAR_PEAKS	1
-#define	HDEXAR_IN_SET_POTS	2
-#define	HDEXAR_AUX_SET_POTS	3
-#define	HDEXAR_CAL_A_TO_D	4
-#define	HDEXAR_RD_EXT_DSP_BITS	5
-
-#define TIME_PRO_RESET_DONE	0x028A
-#define TIME_PRO_SYSEX		0x0040
-#define TIME_PRO_RESET		0x0032
-
-#define AGND			0x01
-#define SIGNAL			0x02
-
-#define EXT_DSP_BIT_DCAL	0x0001
-#define EXT_DSP_BIT_MIDI_CON	0x0002
-
-#define BUFFSIZE		0x8000
-#define HOSTQ_SIZE		0x40
-
-#define SRAM_CNTL_START		0x7F00
-#define SMA_STRUCT_START	0x7F40
-
-#define DAP_BUFF_SIZE		0x2400
-#define DAR_BUFF_SIZE		0x2000
-
-#define DAPQ_STRUCT_SIZE	0x10
-#define DARQ_STRUCT_SIZE	0x10
-#define DAPQ_BUFF_SIZE		(3 * 0x10)
-#define DARQ_BUFF_SIZE		(3 * 0x10)
-#define MODQ_BUFF_SIZE		0x400
-#define MIDQ_BUFF_SIZE		0x200
-#define DSPQ_BUFF_SIZE		0x40
-
-#define DAPQ_DATA_BUFF		0x6C00
-#define DARQ_DATA_BUFF		0x6C30
-#define MODQ_DATA_BUFF		0x6C60
-#define MIDQ_DATA_BUFF		0x7060
-#define DSPQ_DATA_BUFF		0x7260
-
-#define DAPQ_OFFSET		SRAM_CNTL_START
-#define DARQ_OFFSET		(SRAM_CNTL_START + 0x08)
-#define MODQ_OFFSET		(SRAM_CNTL_START + 0x10)
-#define MIDQ_OFFSET		(SRAM_CNTL_START + 0x18)
-#define DSPQ_OFFSET		(SRAM_CNTL_START + 0x20)
-
-#define MOP_PROTEUS		0x10
-#define MOP_EXTOUT		0x32
-#define MOP_EXTTHRU		0x02
-#define MOP_OUTMASK		0x01
-
-#define MIP_EXTIN		0x01
-#define MIP_PROTEUS		0x00
-#define MIP_INMASK		0x32
-
-struct SMA0_CommonData {
-	WORD wCurrPlayBytes;
-	WORD wCurrRecordBytes;
-	WORD wCurrPlayVolLeft;
-	WORD wCurrPlayVolRight;
-	WORD wCurrInVolLeft;
-	WORD wCurrInVolRight;
-	WORD wUser_3;
-	WORD wUser_4;
-	DWORD dwUser_5;
-	DWORD dwUser_6;
-	WORD wUser_7;
-	WORD wReserved_A;
-	WORD wReserved_B;
-	WORD wReserved_C;
-	WORD wReserved_D;
-	WORD wReserved_E;
-	WORD wReserved_F;
-	WORD wReserved_G;
-	WORD wReserved_H;
-	WORD wCurrDSPStatusFlags;
-	WORD wCurrHostStatusFlags;
-	WORD wCurrInputTagBits;
-	WORD wCurrLeftPeak;
-	WORD wCurrRightPeak;
-	WORD wExtDSPbits;
-	BYTE bExtHostbits;
-	BYTE bBoardLevel;
-	BYTE bInPotPosRight;
-	BYTE bInPotPosLeft;
-	BYTE bAuxPotPosRight;
-	BYTE bAuxPotPosLeft;
-	WORD wCurrMastVolLeft;
-	WORD wCurrMastVolRight;
-	BYTE bUser_12;
-	BYTE bUser_13;
-	WORD wUser_14;
-	WORD wUser_15;
-	WORD wCalFreqAtoD;
-	WORD wUser_16;
-	WORD wUser_17;
-} GCC_PACKED;
+#define	HDEXAR_CLEAR_PEAKS			1
+#define	HDEXAR_IN_SET_POTS			2
+#define	HDEXAR_AUX_SET_POTS			3
+#define	HDEXAR_CAL_A_TO_D			4
+#define	HDEXAR_RD_EXT_DSP_BITS			5
+
+#define TIME_PRO_RESET_DONE			0x028A
+#define TIME_PRO_SYSEX				0x0040
+#define TIME_PRO_RESET				0x0032
+
+#define AGND					0x01
+#define SIGNAL					0x02
+
+#define EXT_DSP_BIT_DCAL			0x0001
+#define EXT_DSP_BIT_MIDI_CON			0x0002
+
+#define BUFFSIZE				0x8000
+#define HOSTQ_SIZE				0x40
+
+#define SRAM_CNTL_START				0x7F00
+#define SMA_STRUCT_START			0x7F40
+
+#define DAP_BUFF_SIZE				0x2400
+#define DAR_BUFF_SIZE				0x2000
+
+#define DAPQ_STRUCT_SIZE			0x10
+#define DARQ_STRUCT_SIZE			0x10
+#define DAPQ_BUFF_SIZE				(3 * 0x10)
+#define DARQ_BUFF_SIZE				(3 * 0x10)
+#define MODQ_BUFF_SIZE				0x400
+#define MIDQ_BUFF_SIZE				0x200
+#define DSPQ_BUFF_SIZE				0x40
+
+#define DAPQ_DATA_BUFF				0x6C00
+#define DARQ_DATA_BUFF				0x6C30
+#define MODQ_DATA_BUFF				0x6C60
+#define MIDQ_DATA_BUFF				0x7060
+#define DSPQ_DATA_BUFF				0x7260
+
+#define DAPQ_OFFSET				SRAM_CNTL_START
+#define DARQ_OFFSET				(SRAM_CNTL_START + 0x08)
+#define MODQ_OFFSET				(SRAM_CNTL_START + 0x10)
+#define MIDQ_OFFSET				(SRAM_CNTL_START + 0x18)
+#define DSPQ_OFFSET				(SRAM_CNTL_START + 0x20)
+
+#define MOP_PROTEUS				0x10
+#define MOP_EXTOUT				0x32
+#define MOP_EXTTHRU				0x02
+#define MOP_OUTMASK				0x01
+
+#define MIP_EXTIN				0x01
+#define MIP_PROTEUS				0x00
+#define MIP_INMASK				0x32
+
+/* Classic SMA Common Data */
+#define SMA_wCurrPlayBytes			0x0000
+#define SMA_wCurrRecordBytes			0x0002
+#define SMA_wCurrPlayVolLeft			0x0004
+#define SMA_wCurrPlayVolRight			0x0006
+#define SMA_wCurrInVolLeft			0x0008
+#define SMA_wCurrInVolRight			0x000a
+#define SMA_wUser_3				0x000c
+#define SMA_wUser_4				0x000e
+#define SMA_dwUser_5				0x0010
+#define SMA_dwUser_6				0x0014
+#define SMA_wUser_7				0x0018
+#define SMA_wReserved_A				0x001a
+#define SMA_wReserved_B				0x001c
+#define SMA_wReserved_C				0x001e
+#define SMA_wReserved_D				0x0020
+#define SMA_wReserved_E				0x0022
+#define SMA_wReserved_F				0x0024
+#define SMA_wReserved_G				0x0026
+#define SMA_wReserved_H				0x0028
+#define SMA_wCurrDSPStatusFlags			0x002a
+#define SMA_wCurrHostStatusFlags		0x002c
+#define SMA_wCurrInputTagBits			0x002e
+#define SMA_wCurrLeftPeak			0x0030
+#define SMA_wCurrRightPeak			0x0032
+#define SMA_wExtDSPbits				0x0034
+#define SMA_bExtHostbits			0x0036
+#define SMA_bBoardLevel				0x0037
+#define SMA_bInPotPosRight			0x0038
+#define SMA_bInPotPosLeft			0x0039
+#define SMA_bAuxPotPosRight			0x003a
+#define SMA_bAuxPotPosLeft			0x003b
+#define SMA_wCurrMastVolLeft			0x003c
+#define SMA_wCurrMastVolRight			0x003e
+#define SMA_bUser_12				0x0040
+#define SMA_bUser_13				0x0041
+#define SMA_wUser_14				0x0042
+#define SMA_wUser_15				0x0044
+#define SMA_wCalFreqAtoD			0x0046
+#define SMA_wUser_16				0x0048
+#define SMA_wUser_17				0x004a
+#define SMA__size				0x004c
 
 #ifdef HAVE_DSPCODEH
 #  include "msndperm.c"
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/msnd_pinnacle.c linux/drivers/sound/msnd_pinnacle.c
--- v2.1.119/linux/drivers/sound/msnd_pinnacle.c	Wed Aug 26 11:37:39 1998
+++ linux/drivers/sound/msnd_pinnacle.c	Fri Sep  4 15:32:27 1998
@@ -29,7 +29,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd_pinnacle.c,v 1.8 1998/08/06 21:06:14 andrewtv Exp $
+ * $Id: msnd_pinnacle.c,v 1.17 1998/09/04 18:41:27 andrewtv Exp $
  *
  ********************************************************************/
 
@@ -59,11 +59,7 @@
 #  define LOGNAME			"msnd_pinnacle"
 #endif
 
-#define DEVNAME				dev.name
-#define MIXERMINOR			dev.mixer_minor
-#define DSPMINOR			dev.dsp_minor
-
-multisound_dev_t			dev;
+static multisound_dev_t			dev;
 
 #ifndef HAVE_DSPCODEH
 static char				*dspini, *permini;
@@ -76,25 +72,22 @@
 	LPDAQD lpDAQ;
 
 	msnd_fifo_make_empty(&dev.DAPF);
-	dev.DAPQ->wHead = 0;
-	dev.DAPQ->wTail = PCTODSP_OFFSET(2 * DAPQ_STRUCT_SIZE);
+	writew(0, dev.DAPQ + JQS_wHead);
+	writew(PCTODSP_OFFSET(2 * DAPQ_STRUCT_SIZE), dev.DAPQ + JQS_wTail);
 	dev.CurDAQD = (LPDAQD)(dev.base + 1 * DAPQ_DATA_BUFF);
 	outb(HPBLKSEL_0, dev.io + HP_BLKS);
 	memset_io(dev.base, 0, DAP_BUFF_SIZE * 3);
 
-	for (n = 0, lpDAQ = dev.CurDAQD; n < 3; ++n, ++lpDAQ) {
-               
-		writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), &lpDAQ->wStart);
-		writew(DAP_BUFF_SIZE, &lpDAQ->wSize);
-		writew(1, &lpDAQ->wFormat);
-		writew(dev.sample_size, &lpDAQ->wSampleSize);
-		writew(dev.channels, &lpDAQ->wChannels);
-		writew(dev.sample_rate, &lpDAQ->wSampleRate);
-		writew(HIMT_PLAY_DONE * 0x100 + n, &lpDAQ->wIntMsg);
-		writew(n + 1, &lpDAQ->wFlags);
-
+	for (n = 0, lpDAQ = dev.CurDAQD; n < 3; ++n, lpDAQ += DAQDS__size) {
+		writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), lpDAQ + DAQDS_wStart);
+		writew(DAP_BUFF_SIZE, lpDAQ + DAQDS_wSize);
+		writew(1, lpDAQ + DAQDS_wFormat);
+		writew(dev.sample_size, lpDAQ + DAQDS_wSampleSize);
+		writew(dev.channels, lpDAQ + DAQDS_wChannels);
+		writew(dev.sample_rate, lpDAQ + DAQDS_wSampleRate);
+		writew(HIMT_PLAY_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
+		writew(n + 1, lpDAQ + DAQDS_wFlags);
 	}
-
 	dev.lastbank = -1;
 }
 
@@ -104,34 +97,62 @@
 	LPDAQD lpDAQ;
 
 	msnd_fifo_make_empty(&dev.DARF);
-	dev.DARQ->wHead = 0;
-	dev.DARQ->wTail = PCTODSP_OFFSET(2 * DARQ_STRUCT_SIZE);
+	writew(0, dev.DARQ + JQS_wHead);
+	writew(PCTODSP_OFFSET(2 * DARQ_STRUCT_SIZE), dev.DARQ + JQS_wTail);
 	dev.CurDARQD = (LPDAQD)(dev.base + 1 * DARQ_DATA_BUFF);
 	outb(HPBLKSEL_1, dev.io + HP_BLKS);
 	memset_io(dev.base, 0, DAR_BUFF_SIZE * 3);
 	outb(HPBLKSEL_0, dev.io + HP_BLKS);
 
-	for (n = 0, lpDAQ = dev.CurDARQD; n < 3; ++n, ++lpDAQ) {
-
-		writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, &lpDAQ->wStart);
-		writew(DAR_BUFF_SIZE, &lpDAQ->wSize);
-		writew(1, &lpDAQ->wFormat);
-		writew(dev.sample_size, &lpDAQ->wSampleSize);
-		writew(dev.channels, &lpDAQ->wChannels);
-		writew(dev.sample_rate, &lpDAQ->wSampleRate);
-		writew(HIMT_RECORD_DONE * 0x100 + n, &lpDAQ->wIntMsg);
-		writew(n + 1, &lpDAQ->wFlags);
-
+	for (n = 0, lpDAQ = dev.CurDARQD; n < 3; ++n, lpDAQ += DAQDS__size) {
+		writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, lpDAQ + DAQDS_wStart);
+		writew(DAR_BUFF_SIZE, lpDAQ + DAQDS_wSize);
+		writew(1, lpDAQ + DAQDS_wFormat);
+		writew(dev.sample_size, lpDAQ + DAQDS_wSampleSize);
+		writew(dev.channels, lpDAQ + DAQDS_wChannels);
+		writew(dev.sample_rate, lpDAQ + DAQDS_wSampleRate);
+		writew(HIMT_RECORD_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
+		writew(n + 1, lpDAQ + DAQDS_wFlags);
 	}
 }
 
 static void reset_queues(void)
 {
-	dev.DSPQ->wHead = dev.DSPQ->wTail = 0;
+	writew(0, dev.DSPQ + JQS_wHead);
+	writew(0, dev.DSPQ + JQS_wTail);
 	reset_play_queue();
 	reset_record_queue();
 }
 
+static int dsp_set_format(int val)
+{
+	int data, i;
+	LPDAQD lpDAQ, lpDARQ;
+
+	lpDAQ = (LPDAQD)(dev.base + DAPQ_DATA_BUFF);
+	lpDARQ = (LPDAQD)(dev.base + DARQ_DATA_BUFF);
+
+	switch (val) {
+	case AFMT_U8:
+	case AFMT_S16_LE:
+		data = val;
+		break;
+	default:
+		data = DEFSAMPLESIZE;
+		break;
+	}
+
+	for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
+
+		writew(data, lpDAQ + DAQDS_wSampleSize);
+		writew(data, lpDARQ + DAQDS_wSampleSize);
+	}
+		
+	dev.sample_size = data;
+
+	return data;
+}
+
 static int dsp_ioctl(unsigned int cmd, unsigned long arg)
 {
 	int val, i, data, tmp;
@@ -154,64 +175,43 @@
 
 	case SNDCTL_DSP_SYNC:
 	case SNDCTL_DSP_RESET:
-
 		reset_play_queue();
 		reset_record_queue();
-
 		return 0;
 		
 	case SNDCTL_DSP_GETBLKSIZE:
-
 		tmp = dev.fifosize / 4;
 		if (put_user(tmp, (int *)arg))
                         return -EFAULT;
-
-		return 0;
-
-	case SNDCTL_DSP_NONBLOCK:
-
-		dev.mode |= O_NONBLOCK;
-
 		return 0;
 
-	case SNDCTL_DSP_GETCAPS:
-
-		val = DSP_CAP_DUPLEX | DSP_CAP_BATCH;
+	case SNDCTL_DSP_GETFMTS:
+		val = AFMT_S16_LE | AFMT_U8;
 		if (put_user(val, (int *)arg))
 			return -EFAULT;
-
 		return 0;
 
-	case SNDCTL_DSP_SAMPLESIZE:
-			
+	case SNDCTL_DSP_SETFMT:
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
 
-		switch (val) {
-		case 16:
-		case 8:
-			data = val;
-			break;
-		default:
-			data = DEFSAMPLESIZE;
-			break;
-		}
-
-		for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) {
+		data = (val == AFMT_QUERY) ? dev.sample_size : dsp_set_format(val);
 
-			lpDAQ->wSampleSize = data;
-			lpDARQ->wSampleSize = data;
-		}
-		
-		dev.sample_size = data;
-		
 		if (put_user(data, (int *)arg))
 			return -EFAULT;
+		return 0;
+
+	case SNDCTL_DSP_NONBLOCK:
+		dev.mode |= O_NONBLOCK;
+		return 0;
 
+	case SNDCTL_DSP_GETCAPS:
+		val = DSP_CAP_DUPLEX | DSP_CAP_BATCH;
+		if (put_user(val, (int *)arg))
+			return -EFAULT;
 		return 0;
 
 	case SNDCTL_DSP_SPEED:
-			
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
 
@@ -223,21 +223,19 @@
 
 		data = val;
 
-		for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) {
-
-			lpDAQ->wSampleRate = data;
-			lpDARQ->wSampleRate = data;
+		for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
+			
+			writew(data, lpDAQ + DAQDS_wSampleRate);
+			writew(data, lpDARQ + DAQDS_wSampleRate);
 		}
 		
 		dev.sample_rate = data;
 
 		if (put_user(data, (int *)arg))
 			return -EFAULT;
-
 		return 0;
 
 	case SNDCTL_DSP_CHANNELS:
-			
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
 			
@@ -251,21 +249,19 @@
 			break;
 		}
 									
-		for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) {
+		for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
 
-			lpDAQ->wChannels = data;
-			lpDARQ->wChannels = data;
+			writew(data, lpDAQ + DAQDS_wChannels);
+			writew(data, lpDARQ + DAQDS_wChannels);
 		}
 
 		dev.channels = data;
 
 		if (put_user(val, (int *)arg))
 			return -EFAULT;
-
 		return 0;
 
 	case SNDCTL_DSP_STEREO:
-			
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
 			
@@ -280,17 +276,16 @@
 			break;
 		}
 									
-		for (i = 0; i < 3; ++i, ++lpDAQ, ++lpDARQ) {
+		for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
 
-			lpDAQ->wChannels = data;
-			lpDARQ->wChannels = data;
+			writew(data, lpDAQ + DAQDS_wChannels);
+			writew(data, lpDARQ + DAQDS_wChannels);
 		}
 
 		dev.channels = data;
 
 		if (put_user(val, (int *)arg))
 			return -EFAULT;
-
 		return 0;
 	}
 
@@ -319,11 +314,11 @@
 	}
 }
 
-#define update_vol(a,b,s)								\
-	writew(dev.left_levels[a] * readw(&dev.SMA->wCurrMastVolLeft) / 0xffff / s,	\
-	       &dev.SMA->b##Left);							\
-	writew(dev.right_levels[a] * readw(&dev.SMA->wCurrMastVolRight) / 0xffff / s,	\
-	       &dev.SMA->b##Right);
+#define update_vol(a,b,s)									\
+	writew(dev.left_levels[a] * readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff / s,		\
+	       dev.SMA + SMA_##b##Left);							\
+	writew(dev.right_levels[a] * readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff / s,	\
+	       dev.SMA + SMA_##b##Right);
 
 static int mixer_set(int d, int value)
 {
@@ -346,30 +341,30 @@
 
 	switch (d) {
 	case SOUND_MIXER_VOLUME:		/* master volume */
-		writew(wLeft / 2, &dev.SMA->wCurrMastVolLeft);
-		writew(wRight / 2, &dev.SMA->wCurrMastVolRight);
+		writew(wLeft / 2, dev.SMA + SMA_wCurrMastVolLeft);
+		writew(wRight / 2, dev.SMA + SMA_wCurrMastVolRight);
 		break;
 
 		/* pot controls */
 	case SOUND_MIXER_LINE:			/* aux pot control */
-		writeb(bLeft, &dev.SMA->bInPotPosLeft);
-		writeb(bRight, &dev.SMA->bInPotPosRight);
+		writeb(bLeft, dev.SMA + SMA_bInPotPosLeft);
+		writeb(bRight, dev.SMA + SMA_bInPotPosRight);
 		if (msnd_send_word(&dev, 0, 0, HDEXAR_IN_SET_POTS) == 0)
 			msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ);
 		break;
 
 #ifndef MSND_CLASSIC
 	case SOUND_MIXER_MIC:			/* mic pot control */
-		writeb(bLeft, &dev.SMA->bMicPotPosLeft);
-		writeb(bRight, &dev.SMA->bMicPotPosRight);
+		writeb(bLeft, dev.SMA + SMA_bMicPotPosLeft);
+		writeb(bRight, dev.SMA + SMA_bMicPotPosRight);
 		if (msnd_send_word(&dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0)
 			msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ);
 		break;
 #endif
 
 	case SOUND_MIXER_LINE1:			/* line pot control */
-		writeb(bLeft, &dev.SMA->bAuxPotPosLeft);
-		writeb(bRight, &dev.SMA->bAuxPotPosRight);
+		writeb(bLeft, dev.SMA + SMA_bAuxPotPosLeft);
+		writeb(bRight, dev.SMA + SMA_bAuxPotPosRight);
 		if (msnd_send_word(&dev, 0, 0, HDEXAR_AUX_SET_POTS) == 0)
 			msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ);
 		break;
@@ -407,19 +402,14 @@
 
 #ifndef MSND_CLASSIC
 	if (dev.recsrc & SOUND_MASK_LINE) {
-
 		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0)
 			msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ);
-
 	}
 	else if (dev.recsrc & SOUND_MASK_SYNTH) {
-
 		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_SYNTH_IN) == 0)
 			msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ);
-
 	}
 	else if ((dev.recsrc & SOUND_MASK_DIGITAL1) && test_bit(F_HAVEDIGITAL, &dev.flags)) {
-
 		if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_DAT_IN) == 0) {
 			udelay(50);
       			msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ);
@@ -457,13 +447,14 @@
 		set_mixer_info();
 		return copy_to_user((void *)arg, &info, sizeof(info));
 	}
-	else if (cmd == OSS_GETVERSION)
-		return put_user(SOUND_VERSION, (int *)arg);
+	else if (cmd == OSS_GETVERSION) {
+		int sound_version = SOUND_VERSION;
+		return put_user(sound_version, (int *)arg);
+	}
 	else if (((cmd >> 8) & 0xff) == 'M') {
 		int val = 0;
 		
 		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
-
 			switch (cmd & 0xff) {
 			case SOUND_MIXER_RECSRC:
 				if (get_user(val, (int *)arg))
@@ -477,9 +468,7 @@
 				val = mixer_set(cmd & 0xff, val);
 				break;
 			}
-
 			++dev.mixer_mod_count;
-
 			return put_user(val, (int *)arg);
 		}
 		else {
@@ -532,9 +521,9 @@
 {
 	int minor = MINOR(inode->i_rdev);
 
-	if (minor == DSPMINOR)
+	if (minor == dev.dsp_minor)
 		return dsp_ioctl(cmd, arg);
-	else if (minor == MIXERMINOR)
+	else if (minor == dev.mixer_minor)
 		return mixer_ioctl(cmd, arg);
 
 	return -EINVAL;
@@ -597,14 +586,14 @@
 	int minor = MINOR(inode->i_rdev);
 	int err = 0;
 
-	if (minor == DSPMINOR) {
+	if (minor == dev.dsp_minor) {
 
 		if (test_bit(F_AUDIO_INUSE, &dev.flags))
 			return -EBUSY;
 
 		err = dsp_open(file);
 	}
-	else if (minor == MIXERMINOR) {
+	else if (minor == dev.mixer_minor) {
 		/* nothing */
 	} else
 		err = -EINVAL;
@@ -626,13 +615,13 @@
 	int err = 0;
 #endif
 
-	if (minor == DSPMINOR) {
+	if (minor == dev.dsp_minor) {
 #ifndef LINUX20
 		err = 
 #endif
 			dsp_close();
 	}
-	else if (minor == MIXERMINOR) {
+	else if (minor == dev.mixer_minor) {
 		/* nothing */
 	}
 #ifndef LINUX20
@@ -770,7 +759,7 @@
 	int minor = MINOR(file->f_dentry->d_inode->i_rdev);
 #endif
 
-	if (minor == DSPMINOR) {
+	if (minor == dev.dsp_minor) {
 
 		return dsp_read(buf, count);
 
@@ -788,7 +777,7 @@
 	int minor = MINOR(file->f_dentry->d_inode->i_rdev);
 #endif
 
-	if (minor == DSPMINOR) {
+	if (minor == dev.dsp_minor) {
 
 		return dsp_write(buf, count);
 
@@ -798,32 +787,32 @@
 
 static void eval_dsp_msg(WORD wMessage)
 {
+	WORD wTmp;
+
 	switch (HIBYTE(wMessage)) {
 	case HIMT_PLAY_DONE:
-		
 		if (dev.lastbank == LOBYTE(wMessage))
 			break;
 		
 		dev.lastbank = LOBYTE(wMessage);
 
-		dev.CurDAQD->wSize = DAP_BUFF_SIZE;
+		writew(DAP_BUFF_SIZE, dev.CurDAQD + DAQDS_wSize);
 
-		if ((dev.DAPQ->wTail += PCTODSP_OFFSET(DAPQ_STRUCT_SIZE)) > dev.DAPQ->wSize)
-			dev.DAPQ->wTail = 0;
+		wTmp = readw(dev.DAPQ + JQS_wTail) + PCTODSP_OFFSET(DAPQ_STRUCT_SIZE);
+		if (wTmp > readw(dev.DAPQ + JQS_wSize))
+			writew(0, dev.DAPQ + JQS_wTail);
+		else
+			writew(wTmp, dev.DAPQ + JQS_wTail);
 
-		if (++dev.CurDAQD > (LPDAQD)(dev.base + DAPQ_DATA_BUFF + 2 * DAPQ_STRUCT_SIZE))
+		if ((dev.CurDAQD += DAQDS__size) > (LPDAQD)(dev.base + DAPQ_DATA_BUFF + 2 * DAPQ_STRUCT_SIZE))
 			dev.CurDAQD = (LPDAQD)(dev.base + DAPQ_DATA_BUFF);
 
 		if (dev.lastbank < 3) {
-
 			if (DAPF_to_bank(dev.lastbank) > 0) {
-
 				mdelay(1);
 				msnd_send_dsp_cmd(&dev, HDEX_PLAY_START);
-
 			} 
 			else if (!test_bit(F_WRITEBLOCK, &dev.flags)) {
-
 				clear_bit(F_WRITING, &dev.flags);
 #ifdef LINUX20
 				if (test_bit(F_WRITEFLUSH, &dev.flags)) {
@@ -834,44 +823,33 @@
 				if (test_and_clear_bit(F_WRITEFLUSH, &dev.flags))
 					wake_up_interruptible(&dev.writeflush);
 #endif
-				msnd_disable_irq(&dev);
-
 			}
 		}
 
 		if (test_bit(F_WRITEBLOCK, &dev.flags))
 			wake_up_interruptible(&dev.writeblock);
-		
 		break;
 
-	case HIMT_RECORD_DONE: {
-
-		WORD wTemp;
-		
-		wTemp = dev.DARQ->wTail + (DARQ_STRUCT_SIZE / 2);
+	case HIMT_RECORD_DONE:
+		wTmp = readw(dev.DARQ + JQS_wTail) + DARQ_STRUCT_SIZE / 2;
 
-		if (wTemp > dev.DARQ->wSize)
-			wTemp = 0;
+		if (wTmp > readw(dev.DARQ + JQS_wSize))
+			wTmp = 0;
 
-		while (wTemp == dev.DARQ->wHead);
+		while (wTmp == readw(dev.DARQ + JQS_wHead));
 
-		dev.DARQ->wTail = wTemp;
+		writew(wTmp, dev.DARQ + JQS_wTail);
 
 		outb(HPBLKSEL_1, dev.io + HP_BLKS);
-		if (bank_to_DARF(LOBYTE(wMessage)) == 0 &&
-		    !test_bit(F_READBLOCK, &dev.flags)) {
-
+		if (bank_to_DARF(LOBYTE(wMessage)) == 0 && !test_bit(F_READBLOCK, &dev.flags)) {
 			memset_io(dev.base, 0, DAR_BUFF_SIZE * 3);
 			clear_bit(F_READING, &dev.flags);
-			msnd_disable_irq(&dev);
-
 		}
 		outb(HPBLKSEL_0, dev.io + HP_BLKS);
 
 		if (test_bit(F_READBLOCK, &dev.flags))
 			wake_up_interruptible(&dev.readblock);
-
-		} break;
+		break;
 
 	case HIMT_DSP:
 		switch (LOBYTE(wMessage)) {
@@ -879,12 +857,12 @@
 		case HIDSP_PLAY_UNDER:
 #endif
 		case HIDSP_INT_PLAY_UNDER:
-			printk(KERN_INFO LOGNAME ": Write underflow\n");
+/*			printk(KERN_INFO LOGNAME ": Write underflow\n"); */
 			reset_play_queue();
 			break;
 
 		case HIDSP_INT_RECORD_OVER:
-			printk(KERN_INFO LOGNAME ": Read overflow\n");
+/*			printk(KERN_INFO LOGNAME ": Read overflow\n"); */
 			reset_record_queue();
 			break;
 
@@ -921,12 +899,16 @@
 
 	inb(dev.io + HP_RXL);
  
-	while (dev.DSPQ->wTail != dev.DSPQ->wHead) {
-
-		eval_dsp_msg(*(dev.pwDSPQData + dev.DSPQ->wHead));
+	while (readw(dev.DSPQ + JQS_wTail) != readw(dev.DSPQ + JQS_wHead)) {
+		WORD wTmp;
 
-		if (++dev.DSPQ->wHead > dev.DSPQ->wSize)
-			dev.DSPQ->wHead = 0;
+		eval_dsp_msg(*(dev.pwDSPQData + readw(dev.DSPQ + JQS_wHead)));
+		
+		wTmp = readw(dev.DSPQ + JQS_wHead) + 1;
+		if (wTmp > readw(dev.DSPQ + JQS_wSize))
+			writew(0, dev.DSPQ + JQS_wHead);
+		else
+			writew(wTmp, dev.DSPQ + JQS_wHead);
 	}
 
 	if (test_bit(F_BANKONE, &dev.flags))
@@ -944,13 +926,17 @@
 	dev_ioctl,
 	NULL,
 	dev_open,
-	NULL,		/* flush */
+#ifndef LINUX20
+#  if LINUX_VERSION_CODE >= 0x020100 + 118
+	NULL,
+#  endif /* >= 2.1.118 */
+#endif
 	dev_close,
 };
 
 __initfunc(static int reset_dsp(void))
 {
-	int timeout = 20000;
+	int timeout = 100;
 		
 	outb(HPDSPRESET_ON, dev.io + HP_DSPR);
 	
@@ -984,7 +970,6 @@
 #endif
 
 	if (check_region(dev.io, dev.numio)) {
-		
 		printk(KERN_ERR LOGNAME ": I/O port conflict\n");
 		return -ENODEV;
 	}
@@ -992,7 +977,6 @@
 	request_region(dev.io, dev.numio, "probing");
 
 	if (reset_dsp() < 0) {
-		
 		release_region(dev.io, dev.numio);
 		return -ENODEV;
 	}
@@ -1056,13 +1040,12 @@
 	
 	outb(HPBLKSEL_0, dev.io + HP_BLKS);
 
-	dev.DAPQ = (struct JobQueueStruct *)(dev.base + DAPQ_OFFSET);
-	dev.DARQ = (struct JobQueueStruct *)(dev.base + DARQ_OFFSET);
-	dev.MODQ = (struct JobQueueStruct *)(dev.base + MODQ_OFFSET);
-	dev.MIDQ = (struct JobQueueStruct *)(dev.base + MIDQ_OFFSET);
-	dev.DSPQ = (struct JobQueueStruct *)(dev.base + DSPQ_OFFSET);
-
-	dev.SMA = (struct SMA0_CommonData *)(dev.base + SMA_STRUCT_START);
+	dev.DAPQ = (BYTE *)(dev.base + DAPQ_OFFSET);
+	dev.DARQ = (BYTE *)(dev.base + DARQ_OFFSET);
+	dev.MODQ = (BYTE *)(dev.base + MODQ_OFFSET);
+	dev.MIDQ = (BYTE *)(dev.base + MIDQ_OFFSET);
+	dev.DSPQ = (BYTE *)(dev.base + DSPQ_OFFSET);
+	dev.SMA = (BYTE *)(dev.base + SMA_STRUCT_START);
 
 	dev.CurDAQD = (LPDAQD)(dev.base + DAPQ_DATA_BUFF);
 	dev.CurDARQD = (LPDAQD)(dev.base + DARQ_DATA_BUFF);
@@ -1071,28 +1054,28 @@
 	dev.sample_rate = DEFSAMPLERATE;
 	dev.channels = DEFCHANNELS;
 
-	for (n = 0, lpDAQ = dev.CurDAQD; n < 3; ++n, ++lpDAQ) {
+	for (n = 0, lpDAQ = dev.CurDAQD; n < 3; ++n, lpDAQ += DAQDS__size) {
 		
-		writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), &lpDAQ->wStart);
-		writew(DAP_BUFF_SIZE, &lpDAQ->wSize);
-		writew(1, &lpDAQ->wFormat);
-		writew(dev.sample_size, &lpDAQ->wSampleSize);
-		writew(dev.channels, &lpDAQ->wChannels);
-		writew(dev.sample_rate, &lpDAQ->wSampleRate);
-		writew(HIMT_PLAY_DONE * 0x100 + n, &lpDAQ->wIntMsg);
-		writew(n + 1, &lpDAQ->wFlags);
-	}
-
-	for (n = 0, lpDAQ = dev.CurDARQD; n < 3; ++n, ++lpDAQ) {
-
-		writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, &lpDAQ->wStart);
-		writew(DAR_BUFF_SIZE, &lpDAQ->wSize);
-		writew(1, &lpDAQ->wFormat);
-		writew(dev.sample_size, &lpDAQ->wSampleSize);
-		writew(dev.channels, &lpDAQ->wChannels);
-		writew(dev.sample_rate, &lpDAQ->wSampleRate);
-		writew(HIMT_RECORD_DONE * 0x100 + n, &lpDAQ->wIntMsg);
-		writew(n + 1, &lpDAQ->wFlags);
+		writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), lpDAQ + DAQDS_wStart);
+		writew(DAP_BUFF_SIZE, lpDAQ + DAQDS_wSize);
+		writew(1, lpDAQ + DAQDS_wFormat);
+		writew(dev.sample_size, lpDAQ + DAQDS_wSampleSize);
+		writew(dev.channels, lpDAQ + DAQDS_wChannels);
+		writew(dev.sample_rate, lpDAQ + DAQDS_wSampleRate);
+		writew(HIMT_PLAY_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
+		writew(n + 1, lpDAQ + DAQDS_wFlags);
+	}
+
+	for (n = 0, lpDAQ = dev.CurDARQD; n < 3; ++n, lpDAQ += DAQDS__size) {
+
+		writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, lpDAQ + DAQDS_wStart);
+		writew(DAR_BUFF_SIZE, lpDAQ + DAQDS_wSize);
+		writew(1, lpDAQ + DAQDS_wFormat);
+		writew(dev.sample_size, lpDAQ + DAQDS_wSampleSize);
+		writew(dev.channels, lpDAQ + DAQDS_wChannels);
+		writew(dev.sample_rate, lpDAQ + DAQDS_wSampleRate);
+		writew(HIMT_RECORD_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
+		writew(n + 1, lpDAQ + DAQDS_wFlags);
 
 	}	
 
@@ -1100,68 +1083,68 @@
 	dev.pwMODQData = (WORD *)(dev.base + MODQ_DATA_BUFF);
 	dev.pwMIDQData = (WORD *)(dev.base + MIDQ_DATA_BUFF);
 
-	writew(PCTODSP_BASED(MIDQ_DATA_BUFF), &dev.MIDQ->wStart);
-	writew(PCTODSP_OFFSET(MIDQ_BUFF_SIZE) - 1, &dev.MIDQ->wSize);
-	writew(0, &dev.MIDQ->wHead);
-	writew(0, &dev.MIDQ->wTail);
-
-	writew(PCTODSP_BASED(MODQ_DATA_BUFF), &dev.MODQ->wStart);
-	writew(PCTODSP_OFFSET(MODQ_BUFF_SIZE) - 1, &dev.MODQ->wSize);
-	writew(0, &dev.MODQ->wHead);
-	writew(0, &dev.MODQ->wTail);
-
-	writew(PCTODSP_BASED(DAPQ_DATA_BUFF), &dev.DAPQ->wStart);
-	writew(PCTODSP_OFFSET(DAPQ_BUFF_SIZE) - 1, &dev.DAPQ->wSize);
-	writew(0, &dev.DAPQ->wHead);
-	writew(0, &dev.DAPQ->wTail);
-
-	writew(PCTODSP_BASED(DARQ_DATA_BUFF), &dev.DARQ->wStart);
-	writew(PCTODSP_OFFSET(DARQ_BUFF_SIZE) - 1, &dev.DARQ->wSize);
-	writew(0, &dev.DARQ->wHead);
-	writew(0, &dev.DARQ->wTail);
-
-	writew(PCTODSP_BASED(DSPQ_DATA_BUFF), &dev.DSPQ->wStart);
-	writew(PCTODSP_OFFSET(DSPQ_BUFF_SIZE) - 1, &dev.DSPQ->wSize);
-	writew(0, &dev.DSPQ->wHead);
-	writew(0, &dev.DSPQ->wTail);
-
-	writew(0, &dev.SMA->wCurrPlayBytes);
-	writew(0, &dev.SMA->wCurrRecordBytes);
+	writew(PCTODSP_BASED(MIDQ_DATA_BUFF), dev.MIDQ + JQS_wStart);
+	writew(PCTODSP_OFFSET(MIDQ_BUFF_SIZE) - 1, dev.MIDQ + JQS_wSize);
+	writew(0, dev.MIDQ + JQS_wHead);
+	writew(0, dev.MIDQ + JQS_wTail);
+
+	writew(PCTODSP_BASED(MODQ_DATA_BUFF), dev.MODQ + JQS_wStart);
+	writew(PCTODSP_OFFSET(MODQ_BUFF_SIZE) - 1, dev.MODQ + JQS_wSize);
+	writew(0, dev.MODQ + JQS_wHead);
+	writew(0, dev.MODQ + JQS_wTail);
+
+	writew(PCTODSP_BASED(DAPQ_DATA_BUFF), dev.DAPQ + JQS_wStart);
+	writew(PCTODSP_OFFSET(DAPQ_BUFF_SIZE) - 1, dev.DAPQ + JQS_wSize);
+	writew(0, dev.DAPQ + JQS_wHead);
+	writew(0, dev.DAPQ + JQS_wTail);
+
+	writew(PCTODSP_BASED(DARQ_DATA_BUFF), dev.DARQ + JQS_wStart);
+	writew(PCTODSP_OFFSET(DARQ_BUFF_SIZE) - 1, dev.DARQ + JQS_wSize);
+	writew(0, dev.DARQ + JQS_wHead);
+	writew(0, dev.DARQ + JQS_wTail);
+
+	writew(PCTODSP_BASED(DSPQ_DATA_BUFF), dev.DSPQ + JQS_wStart);
+	writew(PCTODSP_OFFSET(DSPQ_BUFF_SIZE) - 1, dev.DSPQ + JQS_wSize);
+	writew(0, dev.DSPQ + JQS_wHead);
+	writew(0, dev.DSPQ + JQS_wTail);
+
+	writew(0, dev.SMA + SMA_wCurrPlayBytes);
+	writew(0, dev.SMA + SMA_wCurrRecordBytes);
 
-	writew(0, &dev.SMA->wCurrPlayVolLeft);
-	writew(0, &dev.SMA->wCurrPlayVolRight);
+	writew(0, dev.SMA + SMA_wCurrPlayVolLeft);
+	writew(0, dev.SMA + SMA_wCurrPlayVolRight);
 
-	writew(0, &dev.SMA->wCurrInVolLeft);
-	writew(0, &dev.SMA->wCurrInVolRight);
+	writew(0, dev.SMA + SMA_wCurrInVolLeft);
+	writew(0, dev.SMA + SMA_wCurrInVolRight);
 
-	writew(0, &dev.SMA->wCurrMastVolLeft);
-	writew(0, &dev.SMA->wCurrMastVolRight);
+	writew(0, dev.SMA + SMA_wCurrMastVolLeft);
+	writew(0, dev.SMA + SMA_wCurrMastVolRight);
 
 #ifndef MSND_CLASSIC
-	writel(0x00010000, &dev.SMA->dwCurrPlayPitch);
-	writel(0x00000001, &dev.SMA->dwCurrPlayRate);
+	writel(0x00010000, dev.SMA + SMA_dwCurrPlayPitch);
+	writel(0x00000001, dev.SMA + SMA_dwCurrPlayRate);
 #endif
 
-	writew(0x0000, &dev.SMA->wCurrDSPStatusFlags);
-	writew(0x0000, &dev.SMA->wCurrHostStatusFlags);
+	writew(0x0000, dev.SMA + SMA_wCurrDSPStatusFlags);
+	writew(0x0000, dev.SMA + SMA_wCurrHostStatusFlags);
 
-	writew(0x303, &dev.SMA->wCurrInputTagBits);
-	writew(0, &dev.SMA->wCurrLeftPeak);
-	writew(0, &dev.SMA->wCurrRightPeak);
+	writew(0x303, dev.SMA + SMA_wCurrInputTagBits);
+	writew(0, dev.SMA + SMA_wCurrLeftPeak);
+	writew(0, dev.SMA + SMA_wCurrRightPeak);
 
-	writeb(0, &dev.SMA->bInPotPosRight);
-	writeb(0, &dev.SMA->bInPotPosLeft);
+	writeb(0, dev.SMA + SMA_bInPotPosRight);
+	writeb(0, dev.SMA + SMA_bInPotPosLeft);
 
-	writeb(0, &dev.SMA->bAuxPotPosRight);
-	writeb(0, &dev.SMA->bAuxPotPosLeft);
+	writeb(0, dev.SMA + SMA_bAuxPotPosRight);
+	writeb(0, dev.SMA + SMA_bAuxPotPosLeft);
 
 #ifndef MSND_CLASSIC
-	writew(1, &dev.SMA->wCurrPlayFormat);
-	writew(dev.sample_size, &dev.SMA->wCurrPlaySampleSize);
-	writew(dev.channels, &dev.SMA->wCurrPlayChannels);
-	writew(dev.sample_rate, &dev.SMA->wCurrPlaySampleRate);
+	writew(1, dev.SMA + SMA_wCurrPlayFormat);
+	writew(dev.sample_size, dev.SMA + SMA_wCurrPlaySampleSize);
+	writew(dev.channels, dev.SMA + SMA_wCurrPlayChannels);
+	writew(dev.sample_rate, dev.SMA + SMA_wCurrPlaySampleRate);
 #endif
-	writew(dev.sample_rate, &dev.SMA->wCalFreqAtoD);
+	writew(dev.sample_rate, dev.SMA + SMA_wCalFreqAtoD);
 
 	return 0;
 }
@@ -1169,25 +1152,21 @@
 __initfunc(static int calibrate_adc(WORD srate))
 {
 	if (!dev.calibrate_signal) {
-
 		printk(KERN_INFO LOGNAME ": ADC calibration to board ground ");
-		writew(readw(&dev.SMA->wCurrHostStatusFlags)
-		       | 0x0001, &dev.SMA->wCurrHostStatusFlags);
-	}
-	else {
-
+		writew(readw(dev.SMA + SMA_wCurrHostStatusFlags)
+		       | 0x0001, dev.SMA + SMA_wCurrHostStatusFlags);
+	} else {
 		printk(KERN_INFO LOGNAME ": ADC calibration to signal ground ");
-		writew(readw(&dev.SMA->wCurrHostStatusFlags)
-		       & ~0x0001, &dev.SMA->wCurrHostStatusFlags);
+		writew(readw(dev.SMA + SMA_wCurrHostStatusFlags)
+		       & ~0x0001, dev.SMA + SMA_wCurrHostStatusFlags);
 	}
 	
-	writew(srate, &dev.SMA->wCalFreqAtoD);
+	writew(srate, dev.SMA + SMA_wCalFreqAtoD);
 
 	if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 &&
 	    msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) {
-
 		current->state = TASK_INTERRUPTIBLE;
-		current->timeout = jiffies + HZ;
+		current->timeout = jiffies + HZ / 3;
 		schedule();
 		current->timeout = 0;
 		printk("successful\n");
@@ -1221,9 +1200,7 @@
 	}
 #endif
 	memcpy_toio(dev.base, PERMCODE, PERMCODESIZE);
-
 	if (msnd_upload_host(&dev, INITCODE, INITCODESIZE) < 0) {
-
 		printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n");
 		return -ENODEV;
 	}
@@ -1258,7 +1235,6 @@
 #endif
 
 	if ((err = init_sma()) < 0) {
-
 		printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n");
 		return err;
 	}
@@ -1267,17 +1243,15 @@
 		return err;
 	
 	if ((err = upload_dsp_code()) < 0) {
-
 		printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n");
 		return err;
 
 	} else
 		printk(KERN_INFO LOGNAME ": DSP upload successful\n");
 
-	timeout = 2000;
+	timeout = 200;
 
 	while (readw(dev.base)) {
-		
 		mdelay(1);
 		if (--timeout < 0)
 			return -EIO;
@@ -1292,17 +1266,15 @@
 
 	printk(KERN_DEBUG LOGNAME ": Intializing DSP\n");
 
-	if ((err = request_irq(dev.irq, intr, SA_SHIRQ, DEVNAME, &dev)) < 0) {
-		
+	if ((err = request_irq(dev.irq, intr, SA_SHIRQ, dev.name, &dev)) < 0) {
 		printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", dev.irq);
 		return err;
 	
 	}
 
-	request_region(dev.io, dev.numio, DEVNAME);
+	request_region(dev.io, dev.numio, dev.name);
 
         if ((err = initialize()) < 0) {
-
 		printk(KERN_WARNING LOGNAME ": Initialization failure\n");
 		release_region(dev.io, dev.numio);
 		free_irq(dev.irq, &dev);
@@ -1311,41 +1283,34 @@
 	}
 
 	if ((err = msnd_register(&dev)) < 0) {
-
 		printk(KERN_ERR LOGNAME ": Unable to register MultiSound\n");
 		release_region(dev.io, dev.numio);
 		free_irq(dev.irq, &dev);
 		return err;
 	}
 
-	if ((DSPMINOR = register_sound_dsp(&dev_fileops)) < 0) {
-
+	if ((dev.dsp_minor = register_sound_dsp(&dev_fileops)) < 0) {
 		printk(KERN_ERR LOGNAME ": Unable to register DSP operations\n");
 		msnd_unregister(&dev);
 		release_region(dev.io, dev.numio);
 		free_irq(dev.irq, &dev);
-		return DSPMINOR;
+		return dev.dsp_minor;
 	}
 
-	if ((MIXERMINOR = register_sound_mixer(&dev_fileops)) < 0) {
-
+	if ((dev.mixer_minor = register_sound_mixer(&dev_fileops)) < 0) {
 		printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n");
-		unregister_sound_mixer(MIXERMINOR);
+		unregister_sound_mixer(dev.mixer_minor);
 		msnd_unregister(&dev);
 		release_region(dev.io, dev.numio);
 		free_irq(dev.irq, &dev);
-		return MIXERMINOR;
+		return dev.mixer_minor;
 	}
-	printk(KERN_INFO LOGNAME ": Using DSP minor %d, mixer minor %d\n", MIXERMINOR, DSPMINOR);
+	printk(KERN_INFO LOGNAME ": Using DSP minor %d, mixer minor %d\n", dev.dsp_minor, dev.mixer_minor);
 
 	calibrate_adc(dev.sample_rate);
 #ifndef MSND_CLASSIC
-	printk(KERN_INFO LOGNAME ": Setting recording source to Line In\n");
-	if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) != 0 ||
-	    msnd_send_dsp_cmd(&dev, HDEX_AUX_REQ) != 0) {
-		printk(KERN_DEBUG LOGNAME ": Error setting Line In as recording source\n");
-	}
-	dev.recsrc = SOUND_MASK_LINE;
+	printk(KERN_INFO LOGNAME ": Setting initial recording source to Line In\n");
+	set_recsrc(SOUND_MASK_LINE);
 #endif
 	
 	return 0;
@@ -1355,8 +1320,8 @@
 {
 	release_region(dev.io, dev.numio);
 	free_irq(dev.irq, &dev);
-	unregister_sound_mixer(MIXERMINOR);
-	unregister_sound_dsp(DSPMINOR);
+	unregister_sound_mixer(dev.mixer_minor);
+	unregister_sound_dsp(dev.dsp_minor);
 	msnd_unregister(&dev);
 }
 
@@ -1370,6 +1335,155 @@
 	MOD_DEC_USE_COUNT;
 }
 
+#ifndef MSND_CLASSIC
+
+/* Pinnacle/Fiji Logical Device Configuration */
+
+__initfunc(static int msnd_write_cfg(int cfg, int reg, int value))
+{
+	outb(reg, cfg);
+	outb(value, cfg + 1);
+	if (value != inb(cfg + 1)) {
+		printk(KERN_ERR LOGNAME ": msnd_write_cfg: I/O error\n");
+		return -EIO;
+	}
+	return 0;
+}
+
+__initfunc(static int msnd_write_cfg_io0(int cfg, int num, WORD io))
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io)))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io)))
+		return -EIO;
+	return 0;
+}
+
+__initfunc(static int msnd_write_cfg_io1(int cfg, int num, WORD io))
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io)))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io)))
+		return -EIO;
+	return 0;
+}
+
+__initfunc(static int msnd_write_cfg_irq(int cfg, int num, WORD irq))
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq)))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE))
+		return -EIO;
+	return 0;
+}
+
+__initfunc(static int msnd_write_cfg_mem(int cfg, int num, int mem))
+{
+	WORD wmem;
+
+	mem >>= 8;
+	mem &= 0xfff;
+	wmem = (WORD)mem;
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem)))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem)))
+		return -EIO;
+	if (wmem && msnd_write_cfg(cfg, IREG_MEMCONTROL, (MEMTYPE_HIADDR | MEMTYPE_16BIT)))
+		return -EIO;
+	return 0;
+}
+
+__initfunc(static int msnd_activate_logical(int cfg, int num))
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE))
+		return -EIO;
+	return 0;
+}
+
+__initfunc(static int msnd_write_cfg_logical(int cfg, int num, WORD io0, WORD io1, WORD irq, int mem))
+{
+	if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+		return -EIO;
+	if (msnd_write_cfg_io0(cfg, num, io0))
+		return -EIO;
+	if (msnd_write_cfg_io1(cfg, num, io1))
+		return -EIO;
+	if (msnd_write_cfg_irq(cfg, num, irq))
+		return -EIO;
+	if (msnd_write_cfg_mem(cfg, num, mem))
+		return -EIO;
+	if (msnd_activate_logical(cfg, num))
+		return -EIO;
+	return 0;
+}
+
+typedef struct msnd_pinnacle_cfg_device {
+	WORD io0, io1, irq;
+	int mem;
+} msnd_pinnacle_cfg_t[4];
+
+__initfunc(static int msnd_pinnacle_cfg_devices(int cfg, int reset, msnd_pinnacle_cfg_t device))
+{
+	int i;
+
+	/* Reset devices if told to */
+	if (reset) {
+		printk(KERN_INFO LOGNAME ": Resetting all devices\n");
+		for (i = 0; i < 4; ++i)
+			if (msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0))
+				return -EIO;
+	}
+
+	/* Configure specified devices */
+	for (i = 0; i < 4; ++i) {
+		
+		switch (i) {
+		case 0:		/* DSP */
+			if (!(device[i].io0 && device[i].irq && device[i].mem))
+				continue;
+			break;
+		case 1:		/* MPU */
+			if (!(device[i].io0 && device[i].irq))
+				continue;
+			printk(KERN_INFO LOGNAME
+			       ": Configuring MPU to I/O 0x%x IRQ %d\n",
+			       device[i].io0, device[i].irq);
+			break;
+		case 2:		/* IDE */
+			if (!(device[i].io0 && device[i].io1 && device[i].irq))
+				continue;
+			printk(KERN_INFO LOGNAME
+			       ": Configuring IDE to I/O 0x%x, 0x%x IRQ %d\n",
+			       device[i].io0, device[i].io1, device[i].irq);
+			break;
+		case 3:		/* Joystick */
+			if (!(device[i].io0))
+				continue;
+			printk(KERN_INFO LOGNAME
+			       ": Configuring joystick to I/O 0x%x\n",
+			       device[i].io0);
+			break;
+		}
+
+		/* Configure the device */
+		if (msnd_write_cfg_logical(cfg, i, device[i].io0, device[i].io1, device[i].irq, device[i].mem))
+			return -EIO;
+	}
+
+	return 0;
+}
+#endif
+
 #ifdef MODULE
 MODULE_AUTHOR				("Andrew Veliath <andrewtv@usa.net>");
 MODULE_DESCRIPTION			("Turtle Beach " LONGNAME " Linux Driver");
@@ -1381,39 +1495,118 @@
 MODULE_PARM				(calibrate_signal, "i");
 #ifndef MSND_CLASSIC
 MODULE_PARM				(digital, "i");
+MODULE_PARM				(cfg, "i");
+MODULE_PARM				(reset, "i");
+MODULE_PARM				(mpu_io, "i");
+MODULE_PARM				(mpu_irq, "i");
+MODULE_PARM				(ide_io0, "i");
+MODULE_PARM				(ide_io1, "i");
+MODULE_PARM				(ide_irq, "i");
+MODULE_PARM				(joystick_io, "i");
 #endif
 
 static int io __initdata =		-1;
 static int irq __initdata =		-1;
 static int mem __initdata =		-1;
+
 #ifndef MSND_CLASSIC
+/* Pinnacle/Fiji non-PnP Config Port */
+static int cfg __initdata =		-1;
+
+/* Extra Peripheral Configuration */
+static int reset __initdata;
+static int mpu_io __initdata;
+static int mpu_irq __initdata;
+static int ide_io0 __initdata;
+static int ide_io1 __initdata;
+static int ide_irq __initdata;
+static int joystick_io __initdata;
+
+/* If we have the digital daugherboard... */
 static int digital __initdata;
 #endif
+
 static int fifosize __initdata =	DEFFIFOSIZE;
 static int calibrate_signal __initdata;
 
+/* If we're a module, this is just init_module */
+
 int init_module(void)
+
 #else /* not a module */
+
 #ifdef MSND_CLASSIC
 static int io __initdata =		CONFIG_MSNDCLAS_IO;
 static int irq __initdata =		CONFIG_MSNDCLAS_IRQ;
 static int mem __initdata =		CONFIG_MSNDCLAS_MEM;
 #else /* Pinnacle/Fiji */
+
 static int io __initdata =		CONFIG_MSNDPIN_IO;
 static int irq __initdata =		CONFIG_MSNDPIN_IRQ;
 static int mem __initdata =		CONFIG_MSNDPIN_MEM;
+
+/* Pinnacle/Fiji non-PnP Config Port */
+#ifdef CONFIG_MSNDPIN_NONPNP
+#  ifndef CONFIG_MSNDPIN_CFG
+#    define CONFIG_MSNDPIN_CFG		0x250
+#  endif
+#else
+#  ifdef CONFIG_MSNDPIN_CFG
+#    undef CONFIG_MSNDPIN_CFG
+#  endif
+#  define CONFIG_MSNDPIN_CFG		-1
+#endif
+static int cfg __initdata =		CONFIG_MSNDPIN_CFG;
+/* If not a module, we don't need to bother with reset=1 */
+static int reset __initdata;
+
+/* Extra Peripheral Configuration (Default: Disable) */
+#ifndef CONFIG_MSNDPIN_MPU_IO
+#  define CONFIG_MSNDPIN_MPU_IO		0
+#endif
+static int mpu_io __initdata =		CONFIG_MSNDPIN_MPU_IO;
+
+#ifndef CONFIG_MSNDPIN_MPU_IRQ
+#  define CONFIG_MSNDPIN_MPU_IRQ	0
+#endif
+static int mpu_irq __initdata =		CONFIG_MSNDPIN_MPU_IRQ;
+
+#ifndef CONFIG_MSNDPIN_IDE_IO0
+#  define CONFIG_MSNDPIN_IDE_IO0	0
+#endif
+static int ide_io0 __initdata =		CONFIG_MSNDPIN_IDE_IO0;
+
+#ifndef CONFIG_MSNDPIN_IDE_IO1
+#  define CONFIG_MSNDPIN_IDE_IO1	0
+#endif
+static int ide_io1 __initdata =		CONFIG_MSNDPIN_IDE_IO1;
+
+#ifndef CONFIG_MSNDPIN_IDE_IRQ
+#  define CONFIG_MSNDPIN_IDE_IRQ	0
+#endif
+static int ide_irq __initdata =		CONFIG_MSNDPIN_IDE_IRQ;
+
+#ifndef CONFIG_MSNDPIN_JOYSTICK_IO
+#  define CONFIG_MSNDPIN_JOYSTICK_IO	0
+#endif
+static int joystick_io __initdata =	CONFIG_MSNDPIN_JOYSTICK_IO;
+
+/* Have SPDIF (Digital) Daughterboard */
 #ifndef CONFIG_MSNDPIN_DIGITAL
 #  define CONFIG_MSNDPIN_DIGITAL	0
 #endif
 static int digital __initdata =		CONFIG_MSNDPIN_DIGITAL;
+
 #endif /* MSND_CLASSIC */
+
 #ifndef CONFIG_MSND_FIFOSIZE
 #  define CONFIG_MSND_FIFOSIZE		DEFFIFOSIZE
-#endif /* CONFIG_MSND_FIFOSIZE */
+#endif
 static int fifosize __initdata =	CONFIG_MSND_FIFOSIZE;
+
 #ifndef CONFIG_MSND_CALSIGNAL
 #  define CONFIG_MSND_CALSIGNAL		0
-#endif /* CONFIG_MSND_CALSIGNAL */
+#endif
 static int
 calibrate_signal __initdata =		CONFIG_MSND_CALSIGNAL;
 
@@ -1422,9 +1615,13 @@
 #else
 __initfunc(int msnd_pinnacle_init(void))
 #endif /* MSND_CLASSIC */
-#endif
+
+#endif /* MODULE */
 {
 	int err;
+#ifndef MSND_CLASSIC
+	static msnd_pinnacle_cfg_t pinnacle_devs;
+#endif /* MSND_CLASSIC */
 
 	printk(KERN_INFO LOGNAME ": Turtle Beach " LONGNAME " Linux Driver Version "
 	       VERSION ", Copyright (C) 1998 Andrew Veliath\n");
@@ -1433,7 +1630,7 @@
 
 		printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n");
 	}
-		
+
 	if (io == -1 ||
 	    !(io == 0x290 ||
 	      io == 0x260 ||
@@ -1444,7 +1641,7 @@
 	      io == 0x210 ||
 	      io == 0x3e0)) {
 
-		printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set\n");
+		printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, or 0x3E0\n");
 		return -EINVAL;
 	}
 	
@@ -1490,6 +1687,47 @@
 	case 0xd8000: dev.memid = HPMEM_D800; break;
 	case 0xe0000: dev.memid = HPMEM_E000; break;
 	case 0xe8000: dev.memid = HPMEM_E800; break;
+	}
+#else
+	if (cfg == -1) {
+		printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
+	} else if (cfg != 0x250 && cfg != 0x260 && cfg != 0x270) {
+		printk(KERN_INFO LOGNAME ": Config port must be 0x250, 0x260 or 0x270 (or unspecified for PnP mode)\n");
+		return -EINVAL;
+	} else {
+		printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%x\n", cfg);
+
+		/* DSP */
+		pinnacle_devs[0].io0 = io;
+		pinnacle_devs[0].irq = irq;
+		pinnacle_devs[0].mem = mem;
+
+		/* The following are Pinnacle specific */
+		
+		/* MPU */
+		pinnacle_devs[1].io0 = mpu_io;
+		pinnacle_devs[1].irq = mpu_irq;
+
+		/* IDE */
+		pinnacle_devs[2].io0 = ide_io0;
+		pinnacle_devs[2].io1 = ide_io1;
+		pinnacle_devs[2].irq = ide_irq;
+
+		/* Joystick */
+		pinnacle_devs[3].io0 = joystick_io;
+
+		if (check_region(cfg, 2)) {
+			printk(KERN_ERR LOGNAME ": Config port 0x%x conflict\n", cfg);
+			return -EIO;
+		}
+
+		request_region(cfg, 2, "Pinnacle/Fiji Config");
+		if (msnd_pinnacle_cfg_devices(cfg, reset, pinnacle_devs)) {
+			printk(KERN_ERR LOGNAME ": Device configuration error\n");
+			release_region(cfg, 2);
+			return -EIO;
+		}
+		release_region(cfg, 2);
 	}
 #endif /* MSND_CLASSIC */
 
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/msnd_pinnacle.h linux/drivers/sound/msnd_pinnacle.h
--- v2.1.119/linux/drivers/sound/msnd_pinnacle.h	Tue Jul 28 14:21:08 1998
+++ linux/drivers/sound/msnd_pinnacle.h	Fri Sep  4 15:32:27 1998
@@ -24,7 +24,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: msnd_pinnacle.h,v 1.5 1998/07/18 00:12:16 andrewtv Exp $
+ * $Id: msnd_pinnacle.h,v 1.8 1998/09/03 06:39:47 andrewtv Exp $
  *
  ********************************************************************/
 #ifndef __MSND_PINNACLE_H
@@ -32,189 +32,196 @@
 
 #include <linux/config.h>
 
-#define DSP_NUMIO		0x08
+#define DSP_NUMIO				0x08
 
-#define	HP_DSPR			0x04
-#define	HP_BLKS			0x04
-
-#define HPDSPRESET_OFF		2
-#define HPDSPRESET_ON		0
-
-#define HPBLKSEL_0		2
-#define HPBLKSEL_1		3
-
-#define	HIMT_DAT_OFF		0x03
-
-#define	HIDSP_PLAY_UNDER	0x00
-#define	HIDSP_INT_PLAY_UNDER	0x01
-#define	HIDSP_SSI_TX_UNDER  	0x02
-#define HIDSP_RECQ_OVERFLOW	0x08
-#define HIDSP_INT_RECORD_OVER	0x09
-#define HIDSP_SSI_RX_OVERFLOW	0x0a
-
-#define	HIDSP_MIDI_IN_OVER	0x10
-
-#define	HIDSP_MIDI_FRAME_ERR	0x11
-#define	HIDSP_MIDI_PARITY_ERR	0x12
-#define	HIDSP_MIDI_OVERRUN_ERR	0x13
-
-#define HIDSP_INPUT_CLIPPING	0x20
-#define	HIDSP_MIX_CLIPPING	0x30
-#define HIDSP_DAT_IN_OFF	0x21
-
-#define	HDEXAR_SET_ANA_IN	0
-#define	HDEXAR_CLEAR_PEAKS	1
-#define	HDEXAR_IN_SET_POTS	2
-#define	HDEXAR_AUX_SET_POTS	3
-#define	HDEXAR_CAL_A_TO_D	4
-#define	HDEXAR_RD_EXT_DSP_BITS	5
-
-#define	HDEXAR_SET_SYNTH_IN     4
-#define	HDEXAR_READ_DAT_IN      5
-#define	HDEXAR_MIC_SET_POTS     6
-#define	HDEXAR_SET_DAT_IN       7
-
-#define HDEXAR_SET_SYNTH_48     8
-#define HDEXAR_SET_SYNTH_44     9
-
-#define TIME_PRO_RESET_DONE	0x028A
-#define TIME_PRO_SYSEX		0x001E
-#define TIME_PRO_RESET		0x0032
-
-#define AGND			0x01
-#define SIGNAL			0x02
-
-#define EXT_DSP_BIT_DCAL	0x0001
-#define EXT_DSP_BIT_MIDI_CON	0x0002
-
-#define BUFFSIZE		0x8000
-#define HOSTQ_SIZE		0x40
-
-#define SRAM_CNTL_START		0x7F00
-#define SMA_STRUCT_START	0x7F40
-
-#define DAP_BUFF_SIZE		0x2400
-#define DAR_BUFF_SIZE		0x2000
-
-#define DAPQ_STRUCT_SIZE	0x10
-#define DARQ_STRUCT_SIZE	0x10
-#define DAPQ_BUFF_SIZE		(3 * 0x10)
-#define DARQ_BUFF_SIZE		(3 * 0x10)
-#define MODQ_BUFF_SIZE		0x400
-#define MIDQ_BUFF_SIZE		0x800
-#define DSPQ_BUFF_SIZE		0x5A0
-
-#define DAPQ_DATA_BUFF		0x6C00
-#define DARQ_DATA_BUFF		0x6C30
-#define MODQ_DATA_BUFF		0x6C60
-#define MIDQ_DATA_BUFF		0x7060
-#define DSPQ_DATA_BUFF		0x7860
-
-#define DAPQ_OFFSET		SRAM_CNTL_START
-#define DARQ_OFFSET		(SRAM_CNTL_START + 0x08)
-#define MODQ_OFFSET		(SRAM_CNTL_START + 0x10)
-#define MIDQ_OFFSET		(SRAM_CNTL_START + 0x18)
-#define DSPQ_OFFSET		(SRAM_CNTL_START + 0x20)
-
-#define WAVEHDR_MOP		0
-#define EXTOUT_MOP		1
-#define HWINIT_MOP		0xFE
-#define NO_MOP			0xFF
-
-#define MAX_MOP			1
-
-#define EXTIN_MIP		0
-#define WAVEHDR_MIP		1
-#define HWINIT_MIP		0xFE
-
-#define MAX_MIP			1
-
-struct SMA0_CommonData {
-	WORD wCurrPlayBytes;
-	WORD wCurrRecordBytes;
-	WORD wCurrPlayVolLeft;
-	WORD wCurrPlayVolRight;
-
-	WORD wCurrInVolLeft;
-	WORD wCurrInVolRight;
-	WORD wCurrMHdrVolLeft;
-	WORD wCurrMHdrVolRight;
-
-	DWORD dwCurrPlayPitch;
-	DWORD dwCurrPlayRate;
-
-	WORD wCurrMIDIIOPatch;
-
-	WORD wCurrPlayFormat;
-	WORD wCurrPlaySampleSize;
-	WORD wCurrPlayChannels;
-	WORD wCurrPlaySampleRate;
-
-	WORD wCurrRecordFormat;
-	WORD wCurrRecordSampleSize;
-	WORD wCurrRecordChannels;
-	WORD wCurrRecordSampleRate;
-
-	WORD wCurrDSPStatusFlags;
-	WORD wCurrHostStatusFlags;
-
-	WORD wCurrInputTagBits;
-	WORD wCurrLeftPeak;
-	WORD wCurrRightPeak;
-
-	BYTE bMicPotPosLeft;
-	BYTE bMicPotPosRight;
-
-	BYTE bMicPotMaxLeft;
-	BYTE bMicPotMaxRight;
-
-	BYTE bInPotPosLeft;
-	BYTE bInPotPosRight;
-	
-	BYTE bAuxPotPosLeft;
-	BYTE bAuxPotPosRight;
-	
-	BYTE bInPotMaxLeft;
-	BYTE bInPotMaxRight;
-	BYTE bAuxPotMaxLeft;
-	BYTE bAuxPotMaxRight;
-	BYTE bInPotMaxMethod;
-	BYTE bAuxPotMaxMethod;
-
-	WORD wCurrMastVolLeft;
-	WORD wCurrMastVolRight;
-
-	WORD wCalFreqAtoD;
-
-	WORD wCurrAuxVolLeft;
-	WORD wCurrAuxVolRight;
-
-	WORD wCurrPlay1VolLeft;
-	WORD wCurrPlay1VolRight;
-	WORD wCurrPlay2VolLeft;
-	WORD wCurrPlay2VolRight;
-	WORD wCurrPlay3VolLeft;
-	WORD wCurrPlay3VolRight;
-	WORD wCurrPlay4VolLeft;
-	WORD wCurrPlay4VolRight;
-	WORD wCurrPlay1PeakLeft;
-	WORD wCurrPlay1PeakRight;
-	WORD wCurrPlay2PeakLeft;
-	WORD wCurrPlay2PeakRight;
-	WORD wCurrPlay3PeakLeft;
-	WORD wCurrPlay3PeakRight;
-	WORD wCurrPlay4PeakLeft;
-	WORD wCurrPlay4PeakRight;
-	WORD wCurrPlayPeakLeft;
-	WORD wCurrPlayPeakRight;
-
-	WORD wCurrDATSR;
-	WORD wCurrDATRXCHNL;
-	WORD wCurrDATTXCHNL;
-	WORD wCurrDATRXRate;
-
-	DWORD dwDSPPlayCount;
-} GCC_PACKED;
+#define IREG_LOGDEVICE				0x07
+#define IREG_ACTIVATE				0x30
+#define LD_ACTIVATE				0x01
+#define LD_DISACTIVATE				0x00
+#define IREG_EECONTROL				0x3F
+#define IREG_MEMBASEHI				0x40
+#define IREG_MEMBASELO				0x41
+#define IREG_MEMCONTROL				0x42
+#define IREG_MEMRANGEHI				0x43
+#define IREG_MEMRANGELO				0x44
+#define MEMTYPE_8BIT				0x00
+#define MEMTYPE_16BIT				0x02
+#define MEMTYPE_RANGE				0x00
+#define MEMTYPE_HIADDR				0x01
+#define IREG_IO0_BASEHI				0x60
+#define IREG_IO0_BASELO				0x61
+#define IREG_IO1_BASEHI				0x62
+#define IREG_IO1_BASELO				0x63
+#define IREG_IRQ_NUMBER				0x70
+#define IREG_IRQ_TYPE				0x71
+#define IRQTYPE_HIGH				0x02
+#define IRQTYPE_LOW				0x00
+#define IRQTYPE_LEVEL				0x01
+#define IRQTYPE_EDGE				0x00
+
+#define	HP_DSPR					0x04
+#define	HP_BLKS					0x04
+
+#define HPDSPRESET_OFF				2
+#define HPDSPRESET_ON				0
+
+#define HPBLKSEL_0				2
+#define HPBLKSEL_1				3
+
+#define	HIMT_DAT_OFF				0x03
+
+#define	HIDSP_PLAY_UNDER			0x00
+#define	HIDSP_INT_PLAY_UNDER			0x01
+#define	HIDSP_SSI_TX_UNDER  			0x02
+#define HIDSP_RECQ_OVERFLOW			0x08
+#define HIDSP_INT_RECORD_OVER			0x09
+#define HIDSP_SSI_RX_OVERFLOW			0x0a
+
+#define	HIDSP_MIDI_IN_OVER			0x10
+
+#define	HIDSP_MIDI_FRAME_ERR			0x11
+#define	HIDSP_MIDI_PARITY_ERR			0x12
+#define	HIDSP_MIDI_OVERRUN_ERR			0x13
+
+#define HIDSP_INPUT_CLIPPING			0x20
+#define	HIDSP_MIX_CLIPPING			0x30
+#define HIDSP_DAT_IN_OFF			0x21
+
+#define	HDEXAR_SET_ANA_IN			0
+#define	HDEXAR_CLEAR_PEAKS			1
+#define	HDEXAR_IN_SET_POTS			2
+#define	HDEXAR_AUX_SET_POTS			3
+#define	HDEXAR_CAL_A_TO_D			4
+#define	HDEXAR_RD_EXT_DSP_BITS			5
+
+#define	HDEXAR_SET_SYNTH_IN			4
+#define	HDEXAR_READ_DAT_IN			5
+#define	HDEXAR_MIC_SET_POTS			6
+#define	HDEXAR_SET_DAT_IN			7
+
+#define HDEXAR_SET_SYNTH_48			8
+#define HDEXAR_SET_SYNTH_44			9
+
+#define TIME_PRO_RESET_DONE			0x028A
+#define TIME_PRO_SYSEX				0x001E
+#define TIME_PRO_RESET				0x0032
+
+#define AGND					0x01
+#define SIGNAL					0x02
+
+#define EXT_DSP_BIT_DCAL			0x0001
+#define EXT_DSP_BIT_MIDI_CON			0x0002
+
+#define BUFFSIZE				0x8000
+#define HOSTQ_SIZE				0x40
+
+#define SRAM_CNTL_START				0x7F00
+#define SMA_STRUCT_START			0x7F40
+
+#define DAP_BUFF_SIZE				0x2400
+#define DAR_BUFF_SIZE				0x2000
+
+#define DAPQ_STRUCT_SIZE			0x10
+#define DARQ_STRUCT_SIZE			0x10
+#define DAPQ_BUFF_SIZE				(3 * 0x10)
+#define DARQ_BUFF_SIZE				(3 * 0x10)
+#define MODQ_BUFF_SIZE				0x400
+#define MIDQ_BUFF_SIZE				0x800
+#define DSPQ_BUFF_SIZE				0x5A0
+
+#define DAPQ_DATA_BUFF				0x6C00
+#define DARQ_DATA_BUFF				0x6C30
+#define MODQ_DATA_BUFF				0x6C60
+#define MIDQ_DATA_BUFF				0x7060
+#define DSPQ_DATA_BUFF				0x7860
+
+#define DAPQ_OFFSET				SRAM_CNTL_START
+#define DARQ_OFFSET				(SRAM_CNTL_START + 0x08)
+#define MODQ_OFFSET				(SRAM_CNTL_START + 0x10)
+#define MIDQ_OFFSET				(SRAM_CNTL_START + 0x18)
+#define DSPQ_OFFSET				(SRAM_CNTL_START + 0x20)
+
+#define WAVEHDR_MOP				0
+#define EXTOUT_MOP				1
+#define HWINIT_MOP				0xFE
+#define NO_MOP					0xFF
+
+#define MAX_MOP					1
+
+#define EXTIN_MIP				0
+#define WAVEHDR_MIP				1
+#define HWINIT_MIP				0xFE
+
+#define MAX_MIP					1
+
+/* Pinnacle/Fiji SMA Common Data */
+#define SMA_wCurrPlayBytes			0x0000
+#define SMA_wCurrRecordBytes			0x0002
+#define SMA_wCurrPlayVolLeft			0x0004
+#define SMA_wCurrPlayVolRight			0x0006
+#define SMA_wCurrInVolLeft			0x0008
+#define SMA_wCurrInVolRight			0x000a
+#define SMA_wCurrMHdrVolLeft			0x000c
+#define SMA_wCurrMHdrVolRight			0x000e
+#define SMA_dwCurrPlayPitch			0x0010
+#define SMA_dwCurrPlayRate			0x0014
+#define SMA_wCurrMIDIIOPatch			0x0018
+#define SMA_wCurrPlayFormat			0x001a
+#define SMA_wCurrPlaySampleSize			0x001c
+#define SMA_wCurrPlayChannels			0x001e
+#define SMA_wCurrPlaySampleRate			0x0020
+#define SMA_wCurrRecordFormat			0x0022
+#define SMA_wCurrRecordSampleSize		0x0024
+#define SMA_wCurrRecordChannels			0x0026
+#define SMA_wCurrRecordSampleRate		0x0028
+#define SMA_wCurrDSPStatusFlags			0x002a
+#define SMA_wCurrHostStatusFlags		0x002c
+#define SMA_wCurrInputTagBits			0x002e
+#define SMA_wCurrLeftPeak			0x0030
+#define SMA_wCurrRightPeak			0x0032
+#define SMA_bMicPotPosLeft			0x0034
+#define SMA_bMicPotPosRight			0x0035
+#define SMA_bMicPotMaxLeft			0x0036
+#define SMA_bMicPotMaxRight			0x0037
+#define SMA_bInPotPosLeft			0x0038
+#define SMA_bInPotPosRight			0x0039
+#define SMA_bAuxPotPosLeft			0x003a
+#define SMA_bAuxPotPosRight			0x003b
+#define SMA_bInPotMaxLeft			0x003c
+#define SMA_bInPotMaxRight			0x003d
+#define SMA_bAuxPotMaxLeft			0x003e
+#define SMA_bAuxPotMaxRight			0x003f
+#define SMA_bInPotMaxMethod			0x0040
+#define SMA_bAuxPotMaxMethod			0x0041
+#define SMA_wCurrMastVolLeft			0x0042
+#define SMA_wCurrMastVolRight			0x0044
+#define SMA_wCalFreqAtoD			0x0046
+#define SMA_wCurrAuxVolLeft			0x0048
+#define SMA_wCurrAuxVolRight			0x004a
+#define SMA_wCurrPlay1VolLeft			0x004c
+#define SMA_wCurrPlay1VolRight			0x004e
+#define SMA_wCurrPlay2VolLeft			0x0050
+#define SMA_wCurrPlay2VolRight			0x0052
+#define SMA_wCurrPlay3VolLeft			0x0054
+#define SMA_wCurrPlay3VolRight			0x0056
+#define SMA_wCurrPlay4VolLeft			0x0058
+#define SMA_wCurrPlay4VolRight			0x005a
+#define SMA_wCurrPlay1PeakLeft			0x005c
+#define SMA_wCurrPlay1PeakRight			0x005e
+#define SMA_wCurrPlay2PeakLeft			0x0060
+#define SMA_wCurrPlay2PeakRight			0x0062
+#define SMA_wCurrPlay3PeakLeft			0x0064
+#define SMA_wCurrPlay3PeakRight			0x0066
+#define SMA_wCurrPlay4PeakLeft			0x0068
+#define SMA_wCurrPlay4PeakRight			0x006a
+#define SMA_wCurrPlayPeakLeft			0x006c
+#define SMA_wCurrPlayPeakRight			0x006e
+#define SMA_wCurrDATSR				0x0070
+#define SMA_wCurrDATRXCHNL			0x0072
+#define SMA_wCurrDATTXCHNL			0x0074
+#define SMA_wCurrDATRXRate			0x0076
+#define SMA_dwDSPPlayCount			0x0078
+#define SMA__size				0x007c
 
 #ifdef HAVE_DSPCODEH
 #  include "pndsperm.c"
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/sonicvibes.c linux/drivers/sound/sonicvibes.c
--- v2.1.119/linux/drivers/sound/sonicvibes.c	Wed Aug 26 11:37:39 1998
+++ linux/drivers/sound/sonicvibes.c	Fri Sep  4 15:32:27 1998
@@ -46,6 +46,7 @@
  *    03.08.98   0.6   Do not include modversions.h
  *                     Now mixer behaviour can basically be selected between
  *                     "OSS documented" and "OSS actual" behaviour
+ *    31.08.98   0.7   Fix realplayer problems - dac.count issues
  *
  */
 
@@ -619,7 +620,7 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&s->lock, flags);
-	if ((s->dma_adc.mapped || s->dma_adc.count < s->dma_adc.dmasize - 2*s->dma_adc.fragsize) 
+	if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) 
 	    && s->dma_adc.ready) {
 		s->enable |= SV_CENABLE_RE;
 		wrindir(s, SV_CIENABLE, s->enable);
@@ -763,10 +764,10 @@
 		s->dma_adc.total_bytes += diff;
 		s->dma_adc.count += diff;
 		if (s->dma_adc.mapped) {
-			if (s->dma_adc.count >= s->dma_adc.fragsize) 
+			if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) 
 				wake_up(&s->dma_adc.wait);
 		} else {
-			if (s->dma_adc.count > s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1)) {
+			if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
 				s->enable &= ~SV_CENABLE_RE;
 				wrindir(s, SV_CIENABLE, s->enable);
 				s->dma_adc.error++;
@@ -783,7 +784,7 @@
 		s->dma_dac.total_bytes += diff;
 		if (s->dma_dac.mapped) {
 			s->dma_dac.count += diff;
-			if (s->dma_dac.count >= s->dma_dac.fragsize)
+			if (s->dma_dac.count >= (signed)s->dma_dac.fragsize)
 				wake_up(&s->dma_dac.wait);
 		} else {
 			s->dma_dac.count -= diff;
@@ -791,11 +792,11 @@
 				s->enable &= ~SV_CENABLE_PE;
 				wrindir(s, SV_CIENABLE, s->enable);
 				s->dma_dac.error++;
-			} else if (s->dma_dac.count <= s->dma_dac.fragsize && !s->dma_dac.endcleared) {
+			} else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) {
 				clear_advance(s);
 				s->dma_dac.endcleared = 1;
 			}
-			if (s->dma_dac.count < s->dma_dac.dmasize)
+			if (s->dma_dac.count < (signed)s->dma_dac.dmasize)
 				wake_up(&s->dma_dac.wait);
 		}
 	}
@@ -1391,7 +1392,7 @@
 	sv_update_ptr(s);
 	if (file->f_flags & FMODE_READ) {
 		if (s->dma_adc.mapped) {
-			if (s->dma_adc.count >= s->dma_adc.fragsize)
+			if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
 				mask |= POLLIN | POLLRDNORM;
 		} else {
 			if (s->dma_adc.count > 0)
@@ -1400,10 +1401,10 @@
 	}
 	if (file->f_flags & FMODE_WRITE) {
 		if (s->dma_dac.mapped) {
-			if (s->dma_dac.count >= s->dma_dac.fragsize) 
+			if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) 
 				mask |= POLLOUT | POLLWRNORM;
 		} else {
-			if (s->dma_dac.dmasize > s->dma_dac.count)
+			if ((signed)s->dma_dac.dmasize > s->dma_dac.count)
 				mask |= POLLOUT | POLLWRNORM;
 		}
 	}
@@ -2282,7 +2283,7 @@
 
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "sv: version v0.6 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "sv: version v0.7 time " __TIME__ " " __DATE__ "\n");
 #if 0
 	if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
 		printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
diff -u --recursive --new-file v2.1.119/linux/drivers/sound/sound_core.c linux/drivers/sound/sound_core.c
--- v2.1.119/linux/drivers/sound/sound_core.c	Wed Aug 26 11:37:39 1998
+++ linux/drivers/sound/sound_core.c	Fri Sep  4 15:32:27 1998
@@ -168,7 +168,7 @@
  *	6	--		sndstat (obsolete)
  *	7	*16		unused
  *	8	--		alternate sequencer (see above)
- *	9	*16		unused
+ *	9	*16		raw synthesizer access
  *	10	*16		unused
  *	11	*16		unused
  *	12	*16		unused
@@ -212,6 +212,13 @@
 
 EXPORT_SYMBOL(register_sound_dsp);
 
+int register_sound_synth(struct file_operations *fops)
+{
+	return sound_insert_unit(&chains[9], fops, 9, 137);
+}
+
+EXPORT_SYMBOL(register_sound_synth);
+
 void unregister_sound_special(int unit)
 {
 	sound_remove_unit(&chains[unit&15], unit);
@@ -240,6 +247,12 @@
 
 EXPORT_SYMBOL(unregister_sound_dsp);
 
+void unregister_sound_synth(int unit)
+{
+	return sound_remove_unit(&chains[9], unit);
+}
+
+EXPORT_SYMBOL(unregister_sound_synth);
 
 /*
  *	Now our file operations
diff -u --recursive --new-file v2.1.119/linux/fs/Config.in linux/fs/Config.in
--- v2.1.119/linux/fs/Config.in	Thu Aug  6 14:06:33 1998
+++ linux/fs/Config.in	Mon Aug 31 13:01:35 1998
@@ -77,6 +77,10 @@
 fi
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   tristate 'ADFS filesystem support (read only) (EXPERIMENTAL)' CONFIG_ADFS_FS
+  tristate 'QNX filesystem support (EXPERIMENTAL)' CONFIG_QNX4FS_FS
+  if [ "$CONFIG_QNX4FS_FS" != "n" ]; then
+    bool '   QNXFS read-write support (FOR TESTING ONLY)' CONFIG_QNX4FS_RW
+  fi    
 fi
 bool 'Macintosh partition map support' CONFIG_MAC_PARTITION
 endmenu
diff -u --recursive --new-file v2.1.119/linux/fs/Makefile linux/fs/Makefile
--- v2.1.119/linux/fs/Makefile	Wed Jun 24 22:54:08 1998
+++ linux/fs/Makefile	Mon Aug 31 13:01:35 1998
@@ -18,7 +18,7 @@
 MOD_LIST_NAME := FS_MODULES
 ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \
 		hpfs sysv smbfs ncpfs ufs affs romfs autofs hfs lockd nfsd \
-		nls devpts adfs
+		nls devpts adfs qnx4
 
 ifeq ($(CONFIG_QUOTA),y)
 O_OBJS += dquot.o
@@ -204,6 +204,14 @@
 else
   ifeq ($(CONFIG_ROMFS_FS),m)
   MOD_SUB_DIRS += romfs
+  endif
+endif
+
+ifeq ($(CONFIG_QNX4FS_FS),y)
+SUB_DIRS += qnx4
+else
+  ifeq ($(CONFIG_QNX4FS_FS),m)
+  MOD_SUB_DIRS += qnx4
   endif
 endif
 
diff -u --recursive --new-file v2.1.119/linux/fs/binfmt_aout.c linux/fs/binfmt_aout.c
--- v2.1.119/linux/fs/binfmt_aout.c	Wed Aug 26 11:37:40 1998
+++ linux/fs/binfmt_aout.c	Wed Sep  2 09:42:25 1998
@@ -397,6 +397,8 @@
 			MAP_FIXED|MAP_PRIVATE, 0);
 		read_exec(bprm->dentry, 32, (char *) 0, ex.a_text+ex.a_data, 0);
 #endif
+		flush_icache_range((unsigned long) 0,
+				   (unsigned long) ex.a_text+ex.a_data);
 	} else {
 		if ((ex.a_text & 0xfff || ex.a_data & 0xfff) &&
 		    (N_MAGIC(ex) != NMAGIC))
@@ -414,6 +416,9 @@
 				MAP_FIXED|MAP_PRIVATE, 0);
 			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),
+					   (unsigned long) N_TXTADDR(ex) +
+					   ex.a_text+ex.a_data);
 			goto beyond_if;
 		}
 
diff -u --recursive --new-file v2.1.119/linux/fs/coda/cnode.c linux/fs/coda/cnode.c
--- v2.1.119/linux/fs/coda/cnode.c	Thu Jul 16 18:09:28 1998
+++ linux/fs/coda/cnode.c	Mon Aug 31 15:46:10 1998
@@ -14,7 +14,6 @@
 extern int coda_print_entry;
 
 /* cnode.c */
-
 static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr)
 {
         CDEBUG(D_SUPER, "ino: %ld\n", inode->i_ino);
@@ -117,7 +116,6 @@
         return 0;
 }
 
-
 inline int coda_fideq(ViceFid *fid1, ViceFid *fid2)
 {
 	int eq;
@@ -126,6 +124,27 @@
 		 (fid1->Unique == fid2->Unique) );
 	return eq;
 }
+
+void coda_replace_fid(struct inode *inode, struct ViceFid *oldfid, 
+		      struct ViceFid *newfid)
+{
+	struct coda_inode_info *cnp;
+	struct coda_sb_info *sbi= coda_sbp(inode->i_sb);
+	
+	cnp = ITOC(inode);
+
+	if ( ! coda_fideq(&cnp->c_fid, oldfid) )
+		printk("What? oldfid != cnp->c_fid. Call 911.\n");
+
+	cnp->c_fid = *newfid;
+
+	list_del(&cnp->c_volrootlist);
+	if ( !coda_fid_is_weird(newfid) ) 
+		list_add(&cnp->c_volrootlist, &sbi->sbi_volroothead);
+
+	return;
+}
+
 
  
 
diff -u --recursive --new-file v2.1.119/linux/fs/coda/dir.c linux/fs/coda/dir.c
--- v2.1.119/linux/fs/coda/dir.c	Wed Aug 26 11:37:40 1998
+++ linux/fs/coda/dir.c	Mon Aug 31 15:46:10 1998
@@ -96,7 +96,7 @@
         NULL,                   /* ioctl */
         NULL,                   /* mmap */
         coda_open,              /* open */
-	NULL,			/* flush */
+	NULL,
         coda_release,           /* release */
 	coda_fsync,             /* fsync */
         NULL,                   
diff -u --recursive --new-file v2.1.119/linux/fs/coda/file.c linux/fs/coda/file.c
--- v2.1.119/linux/fs/coda/file.c	Wed Aug 26 11:37:40 1998
+++ linux/fs/coda/file.c	Mon Aug 31 15:46:10 1998
@@ -66,7 +66,7 @@
 	NULL,		        /* ioctl */
 	coda_file_mmap,         /* mmap */
 	coda_open,              /* open */
-	NULL,			/* flush */
+	NULL,
 	coda_release,           /* release */
 	coda_fsync,		/* fsync */
 	NULL,                   /* fasync */
diff -u --recursive --new-file v2.1.119/linux/fs/coda/pioctl.c linux/fs/coda/pioctl.c
--- v2.1.119/linux/fs/coda/pioctl.c	Wed Aug 26 11:37:40 1998
+++ linux/fs/coda/pioctl.c	Mon Aug 31 15:46:10 1998
@@ -65,7 +65,7 @@
 	coda_pioctl,	        /* ioctl */
 	NULL,                   /* mmap */
 	coda_ioctl_open,        /* open */
-	NULL,			/* flush */
+	NULL,
 	coda_ioctl_release,     /* release */
 	NULL,		        /* fsync */
 };
diff -u --recursive --new-file v2.1.119/linux/fs/coda/psdev.c linux/fs/coda/psdev.c
--- v2.1.119/linux/fs/coda/psdev.c	Wed Aug 26 11:37:40 1998
+++ linux/fs/coda/psdev.c	Tue Sep  1 11:03:36 1998
@@ -47,13 +47,7 @@
 #include <linux/coda_cache.h>
 #include <linux/coda_proc.h>
 
-
-/*
- * Where is the prototype?
- */
-
-int proc_register_dynamic(struct proc_dir_entry * dir,
-			  struct proc_dir_entry * dp);
+extern struct proc_dir_entry proc_sys_root;
 
 /* 
  * Coda stuff
@@ -391,7 +385,7 @@
       NULL,                  /* ioctl */
       NULL,		     /* coda_psdev_mmap */
       coda_psdev_open,       /* open */
-      NULL,                  /* flush */
+      NULL,
       coda_psdev_release,    /* release */
       NULL,                  /* fsync */
       NULL,                  /* fasync */
@@ -403,7 +397,6 @@
 
 #ifdef CONFIG_PROC_FS
 
-extern struct proc_dir_entry proc_sys_root;
 
 struct proc_dir_entry proc_sys_coda = {
         0, 4, "coda",
@@ -490,6 +483,7 @@
 
 #endif
 
+
 __initfunc(int init_coda(void)) 
 {
 	int status;
@@ -547,14 +541,12 @@
 
 #ifdef MODULE
 
-EXPORT_NO_SYMBOLS;
-
 MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>");
 
 int init_module(void)
 {
 	int status;
-	printk(KERN_INFO "Coda Kernel/Venus communications (module), v4.6.0, braam@cs.cmu.edu\n");
+	printk(KERN_INFO "Coda Kernel/Venus communications (module), v4.7.1, braam@cs.cmu.edu.\n");
 
 	status = init_coda_psdev();
 	if ( status ) {
diff -u --recursive --new-file v2.1.119/linux/fs/coda/upcall.c linux/fs/coda/upcall.c
--- v2.1.119/linux/fs/coda/upcall.c	Mon Aug  3 12:45:47 1998
+++ linux/fs/coda/upcall.c	Tue Sep  1 11:03:36 1998
@@ -39,8 +39,6 @@
 #include <linux/coda_cache.h>
 #include <linux/coda_proc.h> 
 
-extern void coda_purge_dentries(struct inode *inode);
-extern void coda_purge_children(struct inode *inode);
 
 static int  coda_upcall(struct coda_sb_info *mntinfo, int inSize, int *outSize, 
 		       union inputArgs *buffer);
@@ -537,16 +535,12 @@
         inp->cfs_ioctl.data = (char *)(INSIZE(ioctl));
      
         /* get the data out of user space */
-#ifdef L20
-        memcpy_fromfs((char*)inp + (int)inp->cfs_ioctl.data,
-                      data->vi.in, data->vi.in_size);
-#else
         if ( copy_from_user((char*)inp + (int)inp->cfs_ioctl.data,
 			    data->vi.in, data->vi.in_size) ) {
 	        error = EINVAL;
 	        goto exit;
 	}
-#endif
+
         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
         
         if (error) {
@@ -565,18 +559,13 @@
 		error = verify_area(VERIFY_WRITE, data->vi.out, 
                                     data->vi.out_size);
 		if ( error ) goto exit;
-#ifdef L20
-                memcpy_tofs(data->vi.out, 
-                            (char *)outp + (int)outp->cfs_ioctl.data, 
-                            data->vi.out_size);                
-#else
+
 		if (copy_to_user(data->vi.out, 
 				 (char *)outp + (int)outp->cfs_ioctl.data, 
 				 data->vi.out_size)) {
 		        error = EINVAL;
 			goto exit;
 		}
-#endif
         }
 
  exit:
@@ -891,18 +880,19 @@
 
 	  case CFS_REPLACE : {
 	          struct inode *inode;
-		  ViceFid *fid = &out->cfs_replace.OldFid;
+		  ViceFid *oldfid = &out->cfs_replace.OldFid;
+		  ViceFid *newfid = &out->cfs_replace.NewFid;
 		  clstats(CFS_REPLACE);
 		  CDEBUG(D_DOWNCALL, "CFS_REPLACE\n");
-		  inode = coda_fid_to_inode(fid, sb);
+		  inode = coda_fid_to_inode(oldfid, sb);
 		  if ( inode ) { 
 			  CDEBUG(D_DOWNCALL, "replacefid: inode = %ld\n", inode->i_ino);
-			  coda_purge_children(inode);
-                          coda_purge_dentries(inode);
+			  coda_replace_fid(inode, oldfid, newfid);
 		  }else 
 			  CDEBUG(D_DOWNCALL, "purgefid: no inode\n");
+		  
 		  return 0;
-	  }			   
+	  }
 	  }
 	  return 0;
 }
diff -u --recursive --new-file v2.1.119/linux/fs/exec.c linux/fs/exec.c
--- v2.1.119/linux/fs/exec.c	Wed Aug 26 11:37:40 1998
+++ linux/fs/exec.c	Thu Aug 27 14:30:50 1998
@@ -569,6 +569,15 @@
 	return retval;
 }
 
+/*
+ * We mustn't allow tracing of suid binaries, unless
+ * the tracer has the capability to trace anything..
+ */
+static inline int must_not_trace_exec(struct task_struct * p)
+{
+	return (p->flags & PF_PTRACED) && !cap_raised(p->p_pptr->cap_effective, CAP_SYS_PTRACE);
+}
+
 /* 
  * Fill the binprm structure from the inode. 
  * Check permissions, then read the first 512 bytes
@@ -657,15 +666,12 @@
 		}
 	}
 
-
-
-
 	if (id_change || cap_raised) {
 		/* We can't suid-execute if we're sharing parts of the executable */
 		/* or if we're being traced (or if suid execs are not allowed)    */
 		/* (current->mm->count > 1 is ok, as we'll get a new mm anyway)   */
 		if (IS_NOSUID(inode)
-		    || (current->flags & PF_PTRACED)
+		    || must_not_trace_exec(current)
 		    || (atomic_read(&current->fs->count) > 1)
 		    || (atomic_read(&current->sig->count) > 1)
 		    || (atomic_read(&current->files->count) > 1)) {
diff -u --recursive --new-file v2.1.119/linux/fs/filesystems.c linux/fs/filesystems.c
--- v2.1.119/linux/fs/filesystems.c	Wed Aug 26 11:37:41 1998
+++ linux/fs/filesystems.c	Mon Aug 31 13:01:35 1998
@@ -24,6 +24,7 @@
 #include <linux/ufs_fs.h>
 #include <linux/romfs_fs.h>
 #include <linux/auto_fs.h>
+#include <linux/qnx4_fs.h>
 #include <linux/ntfs_fs.h>
 #include <linux/hfs_fs.h>
 #include <linux/devpts_fs.h>
@@ -148,6 +149,10 @@
 	init_devpts_fs();
 #endif
 
+#ifdef CONFIG_QNX4FS_FS
+	init_qnx4_fs();
+#endif
+   
 #ifdef CONFIG_NLS
 	init_nls();
 #endif
diff -u --recursive --new-file v2.1.119/linux/fs/hpfs/hpfs_fs.c linux/fs/hpfs/hpfs_fs.c
--- v2.1.119/linux/fs/hpfs/hpfs_fs.c	Wed May 20 19:10:40 1998
+++ linux/fs/hpfs/hpfs_fs.c	Fri Aug 28 10:34:09 1998
@@ -159,6 +159,7 @@
 	NULL,				/* ioctl - default */
 	generic_file_mmap,		/* mmap */
 	NULL,				/* no special open is needed */
+	NULL,				/* flush */
 	NULL,				/* release */
 	file_fsync,			/* fsync */
 };
@@ -203,6 +204,7 @@
 	NULL,				/* ioctl - default */
 	NULL,				/* mmap */
 	NULL,				/* no special open code */
+	NULL,				/* flush */
 	NULL,				/* no special release code */
 	file_fsync,			/* fsync */
 };
diff -u --recursive --new-file v2.1.119/linux/fs/inode.c linux/fs/inode.c
--- v2.1.119/linux/fs/inode.c	Thu Aug 20 17:05:17 1998
+++ linux/fs/inode.c	Mon Aug 31 11:16:05 1998
@@ -770,7 +770,13 @@
 		inode = file->f_dentry->d_inode;
 		if (!inode || inode->i_sb != sb)
 			continue;
-		if (S_ISREG(inode->i_mode) && file->f_mode & FMODE_WRITE)
+
+		/* File with pending delete? */
+		if (inode->i_nlink == 0)
+			return 0;
+
+		/* Writable file? */
+		if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE))
 			return 0;
 	}
 	return 1; /* Tis' cool bro. */
diff -u --recursive --new-file v2.1.119/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.1.119/linux/fs/isofs/inode.c	Thu Aug 27 19:56:29 1998
+++ linux/fs/isofs/inode.c	Tue Sep  1 12:53:54 1998
@@ -130,18 +130,26 @@
 
 static int strnicmp(const char *s1, const char *s2, int len)
 {
-	int n = 0;
-	while (*s1 && *s2 && (tolower(*s1) == tolower(*s2))) {
-		s1++; s2++; n++;
-		if (n == len) return 0;
-	}
-	if (*s1 == 0 && *s2 == 0) return 0;
-	if (*s1 && *s2) {
-		if (*s1 > *s2) return 1;
-		return -1;
+	/* Yes, Virginia, it had better be unsigned */
+	unsigned char c1, c2;
+
+	c1 = 0;	c2 = 0;
+	while (len > 0) {
+		c1 = *s1; c2 = *s2;
+		s1++; s2++;
+		if (!c1)
+			break;
+		if (!c2)
+			break;
+		if (c1 == c2)
+			continue;
+		c1 = tolower(c1);
+		c2 = tolower(c2);
+		if (c1 != c2)
+			break;
+		len--;
 	}
-	if (*s1) return 1;
-	return -1;
+	return (int)c1 - (int)c2;
 }
 
 /*
@@ -295,7 +303,7 @@
 {
 	char *this_char,*value;
 
-	popt->map = 'a';
+	popt->map = 'n';
 	popt->rock = 'y';
 	popt->joliet = 'y';
 	popt->cruft = 'n';
diff -u --recursive --new-file v2.1.119/linux/fs/isofs/namei.c linux/fs/isofs/namei.c
--- v2.1.119/linux/fs/isofs/namei.c	Thu Aug 27 19:56:29 1998
+++ linux/fs/isofs/namei.c	Fri Aug 28 10:49:36 1998
@@ -160,7 +160,8 @@
 		dpnt = de->name;
 
 		if (dir->i_sb->u.isofs_sb.s_rock ||
-		    dir->i_sb->u.isofs_sb.s_joliet_level) {
+		    dir->i_sb->u.isofs_sb.s_joliet_level || 
+		    dir->i_sb->u.isofs_sb.s_mapping == 'a') {
 			if (! page) {
 				page = (unsigned char *)
 					__get_free_page(GFP_KERNEL);
diff -u --recursive --new-file v2.1.119/linux/fs/lockd/svc.c linux/fs/lockd/svc.c
--- v2.1.119/linux/fs/lockd/svc.c	Mon Aug  3 17:48:27 1998
+++ linux/fs/lockd/svc.c	Tue Sep  1 12:17:39 1998
@@ -36,7 +36,7 @@
 
 #define NLMDBG_FACILITY		NLMDBG_SVC
 #define LOCKD_BUFSIZE		(1024 + NLMSSVC_XDRSIZE)
-#define ALLOWED_SIGS		(sigmask(SIGKILL) | sigmask(SIGSTOP))
+#define ALLOWED_SIGS		(sigmask(SIGKILL))
 
 extern struct svc_program	nlmsvc_program;
 struct nlmsvc_binding *		nlmsvc_ops = NULL;
@@ -80,6 +80,12 @@
 	current->pgrp = 1;
 	sprintf(current->comm, "lockd");
 
+	/* Process request with signals blocked.  */
+	spin_lock_irq(&current->sigmask_lock);
+	siginitsetinv(&current->blocked, sigmask(SIGKILL));
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);		
+
 	/* kick rpciod */
 	rpciod_up();
 
@@ -160,18 +166,7 @@
 				nlmsvc_ops->exp_getclient(&rqstp->rq_addr);
 		}
 
-		/* Process request with signals blocked.  */
-		spin_lock_irq(&current->sigmask_lock);
-		siginitsetinv(&current->blocked, ALLOWED_SIGS);
-		recalc_sigpending(current);
-		spin_unlock_irq(&current->sigmask_lock);
-		
 		svc_process(serv, rqstp);
-
-		spin_lock_irq(&current->sigmask_lock);
-		sigemptyset(&current->blocked);
-		recalc_sigpending(current);
-		spin_unlock_irq(&current->sigmask_lock);
 
 		/* Unlock export hash tables */
 		if (nlmsvc_ops)
diff -u --recursive --new-file v2.1.119/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.1.119/linux/fs/nfs/dir.c	Wed Aug 26 11:37:41 1998
+++ linux/fs/nfs/dir.c	Fri Sep  4 09:39:24 1998
@@ -29,11 +29,6 @@
 
 #include <asm/segment.h>	/* for fs functions */
 
-#define NFS_MAX_AGE 10*HZ	/* max age for dentry validation */
-
-/* needed by smbfs as well ... move to dcache? */
-extern void nfs_renew_times(struct dentry *);
-
 #define NFS_PARANOIA 1
 /* #define NFS_DEBUG_VERBOSE 1 */
 
@@ -376,6 +371,7 @@
 	nfs_invalidate_dircache_sb(NULL);
 }
 
+#define NFS_REVALIDATE_INTERVAL (5*HZ)
 /*
  * This is called every time the dcache has a lookup hit,
  * and we should check whether we can really trust that
@@ -384,36 +380,69 @@
  * NOTE! The hit can be a negative hit too, don't assume
  * we have an inode!
  *
- * The decision to drop the dentry should probably be
- * smarter than this. Right now we believe in directories
- * for 10 seconds, and in normal files for five..
+ * If the dentry is older than the revalidation interval, 
+ * we do a new lookup and verify that the dentry is still
+ * correct.
  */
 static int nfs_lookup_revalidate(struct dentry * dentry)
 {
+	struct dentry * parent = dentry->d_parent;
+	struct inode * inode = dentry->d_inode;
 	unsigned long time = jiffies - dentry->d_time;
-	unsigned long max = 5*HZ;
+	int error;
+	struct nfs_fh fhandle;
+	struct nfs_fattr fattr;
 
-	if (dentry->d_inode) {
-		if (is_bad_inode(dentry->d_inode)) {
+	if (inode && is_bad_inode(inode)) {
 #ifdef NFS_PARANOIA
 printk("nfs_lookup_validate: %s/%s has dud inode\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
+parent->d_name.name, dentry->d_name.name);
 #endif
-			goto bad;
-		}
-		if (S_ISDIR(dentry->d_inode->i_mode))
-			max = NFS_MAX_AGE;
+		goto out_bad;
 	}
 
-	return (time < max) || IS_ROOT(dentry);
-bad:
+	if (time < NFS_REVALIDATE_INTERVAL)
+		goto out_valid;
+	/*
+	 * Don't bother looking up a negative dentry ...
+	 */
+	if (!inode)
+		goto out_bad;
+
+	if (IS_ROOT(dentry))
+		goto out_valid;
+	/*
+	 * Do a new lookup and check the dentry attributes.
+	 */
+	error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent), 
+				dentry->d_name.name, &fhandle, &fattr);
+	if (error) {
+printk("nfs_lookup_revalidate: error=%d\n", error);
+		goto out_bad;
+	}
+
+	/* Inode number matches? */
+	if (fattr.fileid != inode->i_ino) {
+printk("nfs_lookup_revalidate: %s/%s inode mismatch, old=%ld, new=%u\n",
+parent->d_name.name, dentry->d_name.name, inode->i_ino, fattr.fileid);
+		goto out_bad;
+	}
+	/* Filehandle matches? */
+	if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) {
+printk("nfs_lookup_revalidate: %s/%s fh changed\n",
+parent->d_name.name, dentry->d_name.name);
+		goto out_bad;
+	}
+
+out_valid:
+	return 1;
+out_bad:
 	return 0;
 }
 
 /*
  * This is called from dput() when d_count is going to 0.
- * We use it to clean up silly-renamed files, and to check
- * for dentries that have already expired.
+ * We use it to clean up silly-renamed files.
  */
 static void nfs_dentry_delete(struct dentry *dentry)
 {
@@ -433,14 +462,6 @@
 				dentry->d_parent->d_name.name,
 				dentry->d_name.name, error);
 	}
-	/*
-	 * Check whether to expire the dentry ...
-	 */
-	else {
-		unsigned long age = jiffies - dentry->d_time;
-		if (age > NFS_MAX_AGE)
-			d_drop(dentry);
-	}
 
 #ifdef NFS_PARANOIA
 	/*
@@ -523,17 +544,12 @@
 #endif
 
 /*
- * Whenever a lookup succeeds, we know the parent directories
- * are all valid, so we want to update the dentry timestamps.
+ * Whenever an NFS operation succeeds, we know that the dentry
+ * is valid, so we update the revalidation timestamp.
  */
-void nfs_renew_times(struct dentry * dentry)
+static inline void nfs_renew_times(struct dentry * dentry)
 {
-	for (;;) {
-		dentry->d_time = jiffies;
-		if (dentry == dentry->d_parent)
-			break;
-		dentry = dentry->d_parent;
-	}
+	dentry->d_time = jiffies;
 }
 
 static int nfs_lookup(struct inode *dir, struct dentry * dentry)
@@ -546,11 +562,6 @@
 	dfprintk(VFS, "NFS: lookup(%s/%s)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
 
-	if (!dir || !S_ISDIR(dir->i_mode)) {
-		printk("nfs_lookup: inode is NULL or not a directory\n");
-		return -ENOENT;
-	}
-
 	error = -ENAMETOOLONG;
 	if (dentry->d_name.len > NFS_MAXNAMLEN)
 		goto out;
@@ -635,12 +646,7 @@
 	struct nfs_fh fhandle;
 
 	dfprintk(VFS, "NFS: create(%x/%ld, %s\n",
-				dir->i_dev, dir->i_ino, dentry->d_name.name);
-
-	if (!dir || !S_ISDIR(dir->i_mode)) {
-		printk("nfs_create: inode is NULL or not a directory\n");
-		return -ENOENT;
-	}
+		dir->i_dev, dir->i_ino, dentry->d_name.name);
 
 	error = -ENAMETOOLONG;
 	if (dentry->d_name.len > NFS_MAXNAMLEN)
@@ -675,12 +681,7 @@
 	struct nfs_fh fhandle;
 
 	dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n",
-				dir->i_dev, dir->i_ino, dentry->d_name.name);
-
-	if (!dir || !S_ISDIR(dir->i_mode)) {
-		printk("nfs_mknod: inode is NULL or not a directory\n");
-		return -ENOENT;
-	}
+		dir->i_dev, dir->i_ino, dentry->d_name.name);
 
 	if (dentry->d_name.len > NFS_MAXNAMLEN)
 		return -ENAMETOOLONG;
@@ -712,12 +713,7 @@
 	struct nfs_fh fhandle;
 
 	dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n",
-				dir->i_dev, dir->i_ino, dentry->d_name.name);
-
-	if (!dir || !S_ISDIR(dir->i_mode)) {
-		printk("nfs_mkdir: inode is NULL or not a directory\n");
-		return -ENOENT;
-	}
+		dir->i_dev, dir->i_ino, dentry->d_name.name);
 
 	error = -ENAMETOOLONG;
 	if (dentry->d_name.len > NFS_MAXNAMLEN)
@@ -754,12 +750,7 @@
 	int error, rehash = 0;
 
 	dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n",
-				dir->i_dev, dir->i_ino, dentry->d_name.name);
-
-	if (!dir || !S_ISDIR(dir->i_mode)) {
-		printk("nfs_rmdir: inode is NULL or not a directory\n");
-		return -ENOENT;
-	}
+		dir->i_dev, dir->i_ino, dentry->d_name.name);
 
 	error = -ENAMETOOLONG;
 	if (dentry->d_name.len > NFS_MAXNAMLEN)
@@ -1010,12 +1001,7 @@
 	int error;
 
 	dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n",
-				dir->i_dev, dir->i_ino, dentry->d_name.name);
-
-	if (!dir || !S_ISDIR(dir->i_mode)) {
-		printk("nfs_unlink: inode is NULL or not a directory\n");
-		return -ENOENT;
-	}
+		dir->i_dev, dir->i_ino, dentry->d_name.name);
 
 	error = -ENAMETOOLONG;
 	if (dentry->d_name.len > NFS_MAXNAMLEN)
@@ -1039,12 +1025,7 @@
 	int error;
 
 	dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n",
-			dir->i_dev, dir->i_ino, dentry->d_name.name, symname);
-
-	if (!dir || !S_ISDIR(dir->i_mode)) {
-		printk("nfs_symlink: inode is NULL or not a directory\n");
-		return -ENOENT;
-	}
+		dir->i_dev, dir->i_ino, dentry->d_name.name, symname);
 
 	error = -ENAMETOOLONG;
 	if (dentry->d_name.len > NFS_MAXNAMLEN)
@@ -1095,11 +1076,6 @@
 	dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n",
 		old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
 		dentry->d_parent->d_name.name, dentry->d_name.name);
-
-	if (!dir || !S_ISDIR(dir->i_mode)) {
-		printk("nfs_link: dir is NULL or not a directory\n");
-		return -ENOENT;
-	}
 
 	error = -ENAMETOOLONG;
 	if (dentry->d_name.len > NFS_MAXNAMLEN)
diff -u --recursive --new-file v2.1.119/linux/fs/nfs/write.c linux/fs/nfs/write.c
--- v2.1.119/linux/fs/nfs/write.c	Thu Aug 27 19:56:29 1998
+++ linux/fs/nfs/write.c	Wed Sep  2 09:29:58 1998
@@ -61,6 +61,7 @@
 
 static void			nfs_wback_lock(struct rpc_task *task);
 static void			nfs_wback_result(struct rpc_task *task);
+static void			nfs_cancel_request(struct nfs_wreq *req);
 
 /*
  * Cache parameters
@@ -248,7 +249,7 @@
 
 	req = head = NFS_WRITEBACK(inode);
 	while (req != NULL) {
-		if (req->wb_dentry == dentry) {
+		if (req->wb_dentry == dentry && !WB_CANCELLED(req)) {
 			found = 1;
 			break;
 		}
@@ -474,6 +475,7 @@
 		if (!PageLocked(page))
 			break;
 		retval = -ERESTARTSYS;
+		checksignals();
 		if (signalled())
 			break;
 		schedule();
@@ -585,8 +587,11 @@
 			transfer_page_lock(req);
 			/* rpc_execute(&req->wb_task); */
 			if (sync) {
-				/* N.B. if signalled, result not ready? */
-				wait_on_write_request(req);
+				/* if signalled, ensure request is cancelled */
+				if ((count = wait_on_write_request(req)) != 0) {
+					nfs_cancel_request(req);
+					status = count;
+				}
 				if ((count = nfs_write_error(inode)) < 0)
 					status = count;
 			}
diff -u --recursive --new-file v2.1.119/linux/fs/nfsd/export.c linux/fs/nfsd/export.c
--- v2.1.119/linux/fs/nfsd/export.c	Sun Jun  7 11:16:36 1998
+++ linux/fs/nfsd/export.c	Mon Aug 31 11:03:38 1998
@@ -392,14 +392,15 @@
 int
 exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, struct knfs_fh *f)
 {
-	struct svc_export	*exp = NULL;
-	struct svc_fh		fh;
+	struct svc_export	*exp;
 	struct dentry		*dentry;
 	struct inode		*inode;
+	struct svc_fh		fh;
 
 	dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino);
 
-	if (!(exp = exp_get(clp, dev, ino)))
+	exp = exp_get(clp, dev, ino);
+	if (!exp)
 		return -EPERM;
 
 	dentry = exp->ex_dentry;
@@ -414,8 +415,11 @@
 		       dev, ino, inode->i_dev, inode->i_ino);
 	}
 
-	dget(dentry);
-	fh_compose(&fh, exp, dentry);
+	/*
+	 * fh must be initialized before calling fh_compose
+	 */
+	fh_init(&fh);
+	fh_compose(&fh, exp, dget(dentry));
 	memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh));
 	fh_put(&fh);
 
diff -u --recursive --new-file v2.1.119/linux/fs/nfsd/lockd.c linux/fs/nfsd/lockd.c
--- v2.1.119/linux/fs/nfsd/lockd.c	Tue Dec  9 12:16:25 1997
+++ linux/fs/nfsd/lockd.c	Mon Aug 31 11:03:38 1998
@@ -22,12 +22,13 @@
 static u32
 nlm_fopen(struct svc_rqst *rqstp, struct knfs_fh *f, struct file *filp)
 {
-	struct svc_fh	fh;
 	u32		nfserr;
+	struct svc_fh	fh;
 
+	/* must initialize before using! */
+	fh_init(&fh);
 	fh.fh_handle = *f;
 	fh.fh_export = NULL;
-	fh.fh_dverified = 0;
 
 	nfserr = nfsd_open(rqstp, &fh, S_IFREG, 0, filp);
 	if (!nfserr)
diff -u --recursive --new-file v2.1.119/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- v2.1.119/linux/fs/nfsd/nfsfh.c	Thu Aug  6 14:06:33 1998
+++ linux/fs/nfsd/nfsfh.c	Mon Aug 31 11:03:38 1998
@@ -1107,7 +1107,7 @@
 
 	dprintk("nfsd: fh_compose(exp %x/%ld %s/%s, ino=%ld)\n",
 		exp->ex_dev, exp->ex_ino,
-		dentry->d_parent->d_name.name, dentry->d_name.name,
+		parent->d_name.name, dentry->d_name.name,
 		(inode ? inode->i_ino : 0));
 
 	/*
@@ -1115,7 +1115,12 @@
 	 * may not be done on error paths, but the cleanup must call fh_put.
 	 * Fix this soon!
 	 */
+	if (fhp->fh_dverified || fhp->fh_locked || fhp->fh_dentry) {
+		printk(KERN_ERR "fh_compose: fh %s/%s not initialized!\n",
+			parent->d_name.name, dentry->d_name.name);
+	}
 	fh_init(fhp);
+
 	fhp->fh_handle.fh_dcookie = dentry;
 	if (inode) {
 		fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
diff -u --recursive --new-file v2.1.119/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.1.119/linux/fs/proc/array.c	Wed Aug 26 11:37:43 1998
+++ linux/fs/proc/array.c	Wed Sep  2 09:39:18 1998
@@ -530,14 +530,12 @@
 	    unsigned long fp, pc;
 	    unsigned long stack_page;
 	    int count = 0;
-	    extern int sys_pause (void);
 
-	    stack_page = p->kernel_stack_page;
-	    if (!stack_page)
-		    return 0;
+	    stack_page = (unsigned long)p;
 	    fp = ((struct switch_stack *)p->tss.ksp)->a6;
 	    do {
-		    if (fp < stack_page || fp >= 4088+stack_page)
+		    if (fp < stack_page+sizeof(struct task_struct) ||
+			fp >= 8184+stack_page)
 			    return 0;
 		    pc = ((unsigned long *)fp)[1];
 		/* FIXME: This depends on the order of these functions. */
diff -u --recursive --new-file v2.1.119/linux/fs/qnx4/BUGS linux/fs/qnx4/BUGS
--- v2.1.119/linux/fs/qnx4/BUGS	Wed Dec 31 16:00:00 1969
+++ linux/fs/qnx4/BUGS	Mon Aug 31 13:01:35 1998
@@ -0,0 +1,21 @@
+Last update: 03-07-1998
+
+- Files in a subdir can't be accessed, I think that the inode information
+  is not correctly copied at some point. Solved 06-06-1998, Richard.
+  
+- At some point the mounted device can't be unmounted. I think that somewhere
+  in the code a buffer is not given free.
+
+- Make the '..' entry work, I give it a great chance that the above bug
+  (not given free) has something to do with this one, after a 'ls -l'
+  the mounted device can't be unmounted and that's where the '..' entry
+  is accessed.
+  Seems to be solved 21-06-1998, Frank.
+
+- File read function not correct, after the first block it goes beserk.
+  Solved 21-06-1998, Frank.
+
+- This fs will not work if not built as a module.
+  Solved 25-06-1998, Frank.
+
+- Write/truncate/delete functions don't update the bitmap.
diff -u --recursive --new-file v2.1.119/linux/fs/qnx4/Makefile linux/fs/qnx4/Makefile
--- v2.1.119/linux/fs/qnx4/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/fs/qnx4/Makefile	Mon Aug 31 13:01:35 1998
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux qnx4-filesystem routines.
+#
+# 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 definitions are now in the main makefile...
+
+O_TARGET := qnx4.o
+O_OBJS   := inode.o dir.o namei.o file.o bitmap.o symlinks.o truncate.o \
+fsync.o
+M_OBJS   := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.119/linux/fs/qnx4/README linux/fs/qnx4/README
--- v2.1.119/linux/fs/qnx4/README	Wed Dec 31 16:00:00 1969
+++ linux/fs/qnx4/README	Mon Aug 31 13:01:35 1998
@@ -0,0 +1,9 @@
+
+  This is a snapshot of the QNX4 filesystem for Linux.
+  Please send diffs and remarks to <qnxfs@rtc-one.net> .
+  
+Credits :
+
+Richard "Scuba" A. Frowijn     <scuba@wxs.nl>
+Frank "Jedi/Sector One" Denis  <j@4u.net>
+
diff -u --recursive --new-file v2.1.119/linux/fs/qnx4/TODO linux/fs/qnx4/TODO
--- v2.1.119/linux/fs/qnx4/TODO	Wed Dec 31 16:00:00 1969
+++ linux/fs/qnx4/TODO	Mon Aug 31 13:01:35 1998
@@ -0,0 +1,31 @@
+Name       : QNX4 TODO list
+Last update: 29-06-1998
+
+    - qnx4_checkroot (inode.c), currently there's a look for the '/' in
+      the root direntry, if so then the current mounted device is a qnx4
+      partition. This has to be rewritten with a look for 'QNX4' in the
+      bootblock, it seems to me the savest way to ensure that the mounted
+      device is in fact a QNX4 partition.
+      Done 20-06-1998, Frank. But some disks (like QNX install floppies)
+      don't have 'QNX4' in their bootblock.
+      
+    - Bitmap functions. To find out the free space, largest free block, etc.
+      Partly done (RO), Richard, 05/06/1998. Optimized 20-06-1998, Frank.
+    
+    - Symbolic links. symlinks.c have to be rewritten.
+    
+    - Extended files.
+    
+    - Complete write, unlink and truncate functions : the bitmap should be
+updated.
+
+    - Porting to linux 2.1.99+ with dcache support. 20-06-1998, Frank.
+    
+    - Don't rewrite the file_read function : use the generic_file_read hook,
+      and write readpage instead. Done on 21-06-1998, Frank.
+
+    - Write dinit and dcheck.
+
+    - Solving the bugs.
+    
+    - Use le32_to_cpu and vice-versa for portability.
diff -u --recursive --new-file v2.1.119/linux/fs/qnx4/bitmap.c linux/fs/qnx4/bitmap.c
--- v2.1.119/linux/fs/qnx4/bitmap.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/qnx4/bitmap.c	Tue Sep  1 10:43:13 1998
@@ -0,0 +1,196 @@
+/* 
+ * QNX4 file system, Linux implementation.
+ * 
+ * Version : 0.1
+ * 
+ * Using parts of the xiafs filesystem.
+ * 
+ * History :
+ * 
+ * 28-05-1998 by Richard Frowijn : first release.
+ * 20-06-1998 by Frank Denis : basic optimisations.
+ * 25-06-1998 by Frank Denis : qnx4_is_free, qnx4_set_bitmap, qnx4_bmap .
+ * 28-06-1998 by Frank Denis : qnx4_free_inode (to be fixed) .
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/qnx4_fs.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include <asm/bitops.h>
+
+int qnx4_new_block(struct super_block *sb)
+{
+	return 0;
+}
+
+void count_bits(const register char *bmPart, register int size,
+		int *const tf)
+{
+	char b;
+	int tot = *tf;
+
+	if (size > QNX4_BLOCK_SIZE) {
+		size = QNX4_BLOCK_SIZE;
+	}
+	do {
+		b = *bmPart++;
+		if ((b & 1) == 0)
+			tot++;
+		if ((b & 2) == 0)
+			tot++;
+		if ((b & 4) == 0)
+			tot++;
+		if ((b & 8) == 0)
+			tot++;
+		if ((b & 16) == 0)
+			tot++;
+		if ((b & 32) == 0)
+			tot++;
+		if ((b & 64) == 0)
+			tot++;
+		if ((b & 128) == 0)
+			tot++;
+		size--;
+	} while (size != 0);
+	*tf = tot;
+}
+
+unsigned long qnx4_count_free_blocks(struct super_block *sb)
+{
+	int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1;
+	int total = 0;
+	int total_free = 0;
+	int offset = 0;
+	int size = sb->u.qnx4_sb.BitMap->di_size;
+	struct buffer_head *bh;
+
+	while (total < size) {
+		if ((bh = bread(sb->s_dev, start + offset, QNX4_BLOCK_SIZE)) == NULL) {
+			printk("qnx4: I/O error in counting free blocks\n");
+			break;
+		}
+		count_bits(bh->b_data, size - total, &total_free);
+		brelse(bh);
+		total += QNX4_BLOCK_SIZE;
+	}
+
+	return total_free;
+}
+
+unsigned long qnx4_count_free_inodes(struct super_block *sb)
+{
+	return qnx4_count_free_blocks(sb) * QNX4_INODES_PER_BLOCK;	/* FIXME */
+}
+
+int qnx4_is_free(struct super_block *sb, int block)
+{
+	int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1;
+	int size = sb->u.qnx4_sb.BitMap->di_size;
+	struct buffer_head *bh;
+	const char *g;
+	int ret = -EIO;
+
+	start += block / (QNX4_BLOCK_SIZE * 8);
+	QNX4DEBUG(("qnx4: is_free requesting block [%lu], bitmap in block [%lu]\n",
+		   (unsigned long) block, (unsigned long) start));
+	(void) size;		/* CHECKME */
+	bh = bread(sb->s_dev, start, QNX4_BLOCK_SIZE);
+	if (bh == NULL) {
+		return -EIO;
+	}
+	g = bh->b_data + (block % QNX4_BLOCK_SIZE);
+	if (((*g) & (1 << (block % 8))) == 0) {
+		QNX4DEBUG(("qnx4: is_free -> block is free\n"));
+		ret = 1;
+	} else {
+		QNX4DEBUG(("qnx4: is_free -> block is busy\n"));
+		ret = 0;
+	}
+	brelse(bh);
+
+	return ret;
+}
+
+int qnx4_bmap(struct inode *inode, int block)
+{
+   QNX4DEBUG(("qnx4: bmap on block [%d]\n", block));
+   if (block < 0) {
+      return 0;
+   }
+   return !qnx4_is_free(inode->i_sb, block);
+}
+
+#ifdef CONFIG_QNX4FS_RW
+
+int qnx4_set_bitmap(struct super_block *sb, int block, int busy)
+{
+	int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1;
+	int size = sb->u.qnx4_sb.BitMap->di_size;
+	struct buffer_head *bh;
+	char *g;
+
+	start += block / (QNX4_BLOCK_SIZE * 8);
+	QNX4DEBUG(("qnx4: set_bitmap requesting block [%lu], bitmap in block [%lu]\n",
+		   (unsigned long) block, (unsigned long) start));
+	(void) size;		/* CHECKME */
+	bh = bread(sb->s_dev, start, QNX4_BLOCK_SIZE);
+	if (bh == NULL) {
+		return -EIO;
+	}
+	g = bh->b_data + (block % QNX4_BLOCK_SIZE);
+	if (busy == 0) {
+		(*g) &= ~(1 << (block % 8));
+	} else {
+		(*g) |= (1 << (block % 8));
+	}
+	mark_buffer_dirty(bh, 1);
+	brelse(bh);
+
+	return 0;
+}
+
+static void qnx4_clear_inode(struct inode *inode)
+{
+	struct qnx4_inode_info *qnx4_ino = &inode->u.qnx4_i;
+
+	memset(qnx4_ino->i_reserved, 0, sizeof qnx4_ino->i_reserved);
+	qnx4_ino->i_size = 0;
+	qnx4_ino->i_num_xtnts = 0;
+	qnx4_ino->i_mode = 0;
+	qnx4_ino->i_status = 0;
+}
+
+void qnx4_free_inode(struct inode *inode)
+{
+	if (!inode) {
+		return;
+	}
+	if (!inode->i_dev) {
+		printk("free_inode: inode has no device\n");
+		return;
+	}
+	if (inode->i_count > 1) {
+		printk("free_inode: inode has count=%d\n", inode->i_count);
+		return;
+	}
+	if (inode->i_nlink) {
+		printk("free_inode: inode has nlink=%d\n", inode->i_nlink);
+		return;
+	}
+	if (!inode->i_sb) {
+		printk("free_inode: inode on nonexistent device\n");
+		return;
+	}
+	if (inode->i_ino < 1) {
+		printk("free_inode: inode 0 or nonexistent inode\n");
+		return;
+	}
+	qnx4_clear_inode(inode);
+	clear_inode(inode);
+}
+
+#endif
diff -u --recursive --new-file v2.1.119/linux/fs/qnx4/dir.c linux/fs/qnx4/dir.c
--- v2.1.119/linux/fs/qnx4/dir.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/qnx4/dir.c	Tue Sep  1 10:43:13 1998
@@ -0,0 +1,126 @@
+/* 
+ * QNX4 file system, Linux implementation.
+ * 
+ * Version : 0.1
+ * 
+ * Using parts of the xiafs filesystem.
+ * 
+ * History :
+ * 
+ * 28-05-1998 by Richard Frowijn : first release.
+ * 20-06-1998 by Frank Denis : Linux 2.1.99+ & dcache support.
+ */
+
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/qnx4_fs.h>
+#include <linux/stat.h>
+
+#include <asm/segment.h>
+
+static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir);
+
+static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	unsigned int offset;
+	struct buffer_head *bh;
+	struct qnx4_inode_entry *de;
+	long blknum;
+	int i;
+	int size;
+
+	blknum = inode->u.qnx4_i.i_first_xtnt.xtnt_blk - 1 +
+	    ((filp->f_pos >> 6) >> 3);
+
+	if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) {
+		return -EBADF;
+	}
+	QNX4DEBUG(("qnx4_readdir:i_size = %ld\n", (long) inode->i_size));
+	QNX4DEBUG(("filp->f_pos         = %ld\n", (long) filp->f_pos));
+	QNX4DEBUG(("BlkNum              = %ld\n", (long) blknum));
+
+	while (filp->f_pos < inode->i_size) {
+		bh = bread(inode->i_dev, blknum, QNX4_BLOCK_SIZE);
+		i = (filp->f_pos - (((filp->f_pos >> 6) >> 3) << 9)) & 0x3f;
+		while (i < QNX4_INODES_PER_BLOCK) {
+			offset = i * QNX4_DIR_ENTRY_SIZE;
+			de = (struct qnx4_inode_entry *) (bh->b_data + offset);
+			size = strlen(de->di_fname);
+			if (size) {
+
+				QNX4DEBUG(("qnx4_readdir:%s\n", de->di_fname));
+
+				if ((de->di_mode) || (de->di_status == QNX4_FILE_LINK)) {
+					if (de->di_status) {
+						if (filldir(dirent, de->di_fname, size, filp->f_pos, de->di_first_xtnt.xtnt_blk) < 0) {
+							brelse(bh);
+							return 0;
+						}
+					}
+				}
+			}
+			i++;
+			filp->f_pos += QNX4_DIR_ENTRY_SIZE;
+		}
+		brelse(bh);
+		blknum++;
+	}
+	UPDATE_ATIME(inode);
+
+	return 0;
+}
+
+static struct file_operations qnx4_dir_operations =
+{
+	NULL,			/* lseek - default */
+	NULL,			/* read */
+	NULL,			/* write - bad */
+	qnx4_readdir,		/* readdir */
+	NULL,			/* poll - default */
+	NULL,			/* ioctl - default */
+	NULL,			/* mmap */
+	NULL,			/* no special open code */
+        NULL,                   /* no special flush code */
+	NULL,			/* no special release code */
+	file_fsync,		/* default fsync */
+	NULL,			/* default fasync */
+	NULL,			/* default check_media_change */
+	NULL,			/* default revalidate */
+};
+
+struct inode_operations qnx4_dir_inode_operations =
+{
+	&qnx4_dir_operations,
+#ifdef CONFIG_QNX4FS_RW
+	qnx4_create,
+#else
+	NULL,			/* create */
+#endif
+	qnx4_lookup,
+	NULL,			/* link */
+#ifdef CONFIG_QNX4FS_RW
+	qnx4_unlink,		/* unlink */
+#else
+	NULL,
+#endif
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+#ifdef CONFIG_QNX4FS_RW
+	qnx4_rmdir,		/* rmdir */
+#else
+	NULL,
+#endif
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL			/* smap */
+};
diff -u --recursive --new-file v2.1.119/linux/fs/qnx4/file.c linux/fs/qnx4/file.c
--- v2.1.119/linux/fs/qnx4/file.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/qnx4/file.c	Tue Sep  1 10:43:13 1998
@@ -0,0 +1,265 @@
+/* 
+ * QNX4 file system, Linux implementation.
+ * 
+ * Version : 0.1
+ * 
+ * Using parts of the xiafs filesystem.
+ * 
+ * History :
+ * 
+ * 25-05-1998 by Richard Frowijn : first release.
+ * 21-06-1998 by Frank Denis : wrote qnx4_readpage to use generic_file_read.
+ * 27-06-1998 by Frank Denis : file overwriting.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/qnx4_fs.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+#include <linux/fs.h>
+#include <linux/qnx4_fs.h>
+
+static int qnx4_readpage(struct file *file, struct page *page);
+
+#ifdef CONFIG_QNX4FS_RW
+static ssize_t qnx4_file_write(struct file *filp, const char *buf,
+			       size_t count, loff_t * ppos)
+{
+	struct dentry *dentry = filp->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	struct qnx4_inode_info *qnx4_ino;
+	struct buffer_head *bh;
+	ssize_t result = -EBUSY, c;
+	off_t pos;
+	unsigned long start, block, extent_end;
+	char *p;
+
+	QNX4DEBUG(("qnx4: file_write(%s/%s (%d), %lu@%lu)\n",
+		   dentry->d_parent->d_name.name, dentry->d_name.name,
+	  inode->i_count, (unsigned long) count, (unsigned long) *ppos));
+	if (inode == NULL) {
+		printk("qnx4: NULL inode for file_write\n");
+		return -EINVAL;
+	}
+	qnx4_ino = &inode->u.qnx4_i;
+	if (S_ISREG(inode->i_mode) == 0) {
+		printk("qnx4: write to non-file, mode %07o\n", inode->i_mode);
+		return -EINVAL;
+	}
+	if (count == 0) {
+		goto out;
+	}
+	if (filp->f_flags & O_APPEND) {
+		pos = inode->i_size;
+	} else {
+		pos = *ppos;
+	}
+	start = qnx4_ino->i_first_xtnt.xtnt_blk + ((pos >> 9) * 0) - 1;
+	result = 0;
+	extent_end = start + qnx4_ino->i_first_xtnt.xtnt_size - 1;
+	QNX4DEBUG(("qnx4: extent length : [%lu] bytes\n",
+		   qnx4_ino->i_first_xtnt.xtnt_size));
+	while (result < count) {
+		block = start + pos / QNX4_BLOCK_SIZE;
+		if (block > extent_end) {
+			if (qnx4_is_free(inode->i_sb, block) <= 0) {
+				printk("qnx4: next inode is busy -> write aborted.\n");
+				result = -ENOSPC;
+				break;
+			}
+		}
+		if ((bh = bread(inode->i_dev, block,
+				QNX4_BLOCK_SIZE)) == NULL) {
+			printk("qnx4: I/O error on write.\n");
+			result = -EIO;
+			goto out;
+		}
+		if (bh == NULL) {
+			if (result != 0) {
+				result = -ENOSPC;
+			}
+			break;
+		}
+		if (block > extent_end) {
+			qnx4_set_bitmap(inode->i_sb, block, 1);
+			extent_end++;
+			qnx4_ino->i_first_xtnt.xtnt_size = extent_end - start + 1;
+		}
+		c = QNX4_BLOCK_SIZE - (pos % QNX4_BLOCK_SIZE);
+		if (c > count - result) {
+			c = count - result;
+		}
+		if (c != QNX4_BLOCK_SIZE && buffer_uptodate(bh) == 0) {
+			ll_rw_block(WRITE, 1, &bh);
+			wait_on_buffer(bh);
+			if (buffer_uptodate(bh) == 0) {
+				brelse(bh);
+				if (result != 0) {
+					result = -EIO;
+				}
+				break;
+			}
+		}
+		p = bh->b_data + (pos % QNX4_BLOCK_SIZE);
+		c -= copy_from_user(p, buf, c);
+		if (c == 0) {
+			brelse(bh);
+			if (result == 0) {
+				result = -EFAULT;
+			}
+			break;
+		}
+		update_vm_cache(inode, pos, p, c);
+		mark_buffer_uptodate(bh, 1);
+		mark_buffer_dirty(bh, 0);
+		brelse(bh);
+		pos += c;
+		buf += c;
+		result += c;
+	}
+	if (pos > inode->i_size) {
+		inode->i_size = pos;
+	}
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	*ppos = pos;
+	mark_inode_dirty(inode);
+
+      out:
+	return result;
+}
+#endif
+
+/*
+ * We have moostly NULL's here: the current defaults are ok for
+ * the qnx4 filesystem.
+ */
+static struct file_operations qnx4_file_operations =
+{
+	NULL,			/* lseek - default */
+	generic_file_read,	/* read */
+#ifdef CONFIG_QNX4FS_RW
+	qnx4_file_write,	/* write */
+#else
+	NULL,
+#endif
+	NULL,			/* readdir - bad */
+	NULL,			/* poll - default */
+	NULL,			/* ioctl - default */
+	generic_file_mmap,	/* mmap */
+	NULL,			/* no special open is needed */
+        NULL,                   /* no special flush code */
+	NULL,			/* release */
+#ifdef CONFIG_QNX4FS_RW   
+	qnx4_sync_file,	        /* fsync */
+#else
+        NULL,
+#endif
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL,			/* revalidate */
+	NULL			/* lock */
+};
+
+struct inode_operations qnx4_file_inode_operations =
+{
+	&qnx4_file_operations,	/* default file operations */
+#ifdef CONFIG_QNX4FS_RW
+	qnx4_create,		/* create */
+#else
+	NULL,
+#endif
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	qnx4_readpage,		/* readpage */
+	NULL,			/* writepage */
+	qnx4_bmap,	        /* bmap */
+#ifdef CONFIG_QNX4FS_RW
+	qnx4_truncate,		/* truncate */
+#else
+	NULL,
+#endif
+	NULL,			/* permission */
+	NULL			/* smap */
+};
+
+static int qnx4_readpage(struct file *file, struct page *page)
+{
+	struct dentry *dentry = file->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	struct qnx4_inode_info *qnx4_ino = &inode->u.qnx4_i;
+	unsigned long buf;
+	unsigned long offset, avail, readlen;
+	unsigned long start;
+	unsigned long count;
+	struct buffer_head *bh;
+	int res = -EIO;
+
+	QNX4DEBUG(("qnx4: readpage offset=[%ld]\n", (long) page->offset));
+
+	if (qnx4_ino->i_xblk != 0) {
+		printk("qnx4: sorry, this file is extended, don't know how to handle it (yet) !\n");
+		return -EIO;
+	}
+	atomic_inc(&page->count);
+	set_bit(PG_locked, &page->flags);
+	buf = page_address(page);
+	clear_bit(PG_uptodate, &page->flags);
+	clear_bit(PG_error, &page->flags);
+	offset = page->offset;
+
+	if (offset < inode->i_size) {
+		res = 0;
+		avail = inode->i_size - offset;
+		readlen = MIN(avail, PAGE_SIZE);
+		start = qnx4_ino->i_first_xtnt.xtnt_blk + (offset >> 9) - 1;
+		count = PAGE_SIZE / QNX4_BLOCK_SIZE;
+		do {
+			QNX4DEBUG(("qnx4: reading page starting at [%ld]\n", (long) start));
+			if ((bh = bread(inode->i_dev, start, QNX4_BLOCK_SIZE)) == NULL) {
+				printk("qnx4: data corrupted or I/O error.\n");
+				res = -EIO;
+			} else {
+				memcpy((void *) buf, bh->b_data, QNX4_BLOCK_SIZE);
+			}
+			buf += QNX4_BLOCK_SIZE;
+			start++;
+			count--;
+		} while (count != 0);
+	}
+	if (res != 0) {
+		set_bit(PG_error, &page->flags);
+		memset((void *) buf, 0, PAGE_SIZE);
+	} else {
+		set_bit(PG_uptodate, &page->flags);
+	}
+	clear_bit(PG_locked, &page->flags);
+	wake_up(&page->wait);
+/*  free_page(buf); */
+
+	return res;
+}
diff -u --recursive --new-file v2.1.119/linux/fs/qnx4/fsync.c linux/fs/qnx4/fsync.c
--- v2.1.119/linux/fs/qnx4/fsync.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/qnx4/fsync.c	Tue Sep  1 10:43:13 1998
@@ -0,0 +1,166 @@
+/* 
+ * QNX4 file system, Linux implementation.
+ * 
+ * Version : 0.1
+ * 
+ * Using parts of the xiafs filesystem.
+ * 
+ * History :
+ * 
+ * 24-03-1998 by Richard Frowijn : first release.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/locks.h>
+
+#include <linux/fs.h>
+#include <linux/qnx4_fs.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#define blocksize QNX4_BLOCK_SIZE
+
+/*
+ * The functions for qnx4 fs file synchronization.
+ */
+
+#ifdef CONFIG_QNX4FS_RW
+
+static int sync_block(struct inode *inode, unsigned short *block, int wait)
+{
+	struct buffer_head *bh;
+	unsigned short tmp;
+
+	if (!*block)
+		return 0;
+	tmp = *block;
+	bh = get_hash_table(inode->i_dev, *block, blocksize);
+	if (!bh)
+		return 0;
+	if (*block != tmp) {
+		brelse(bh);
+		return 1;
+	}
+	if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
+		brelse(bh);
+		return -1;
+	}
+	if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
+		brelse(bh);
+		return 0;
+	}
+	ll_rw_block(WRITE, 1, &bh);
+	bh->b_count--;
+	return 0;
+}
+
+static int sync_iblock(struct inode *inode, unsigned short *iblock,
+		       struct buffer_head **bh, int wait)
+{
+	int rc;
+	unsigned short tmp;
+
+	*bh = NULL;
+	tmp = *iblock;
+	if (!tmp)
+		return 0;
+	rc = sync_block(inode, iblock, wait);
+	if (rc)
+		return rc;
+	*bh = bread(inode->i_dev, tmp, blocksize);
+	if (tmp != *iblock) {
+		brelse(*bh);
+		*bh = NULL;
+		return 1;
+	}
+	if (!*bh)
+		return -1;
+	return 0;
+}
+
+static int sync_direct(struct inode *inode, int wait)
+{
+	int i;
+	int rc, err = 0;
+
+	for (i = 0; i < 7; i++) {
+		rc = sync_block(inode,
+				(unsigned short *) inode->u.qnx4_i.i_first_xtnt.xtnt_blk + i, wait);
+		if (rc > 0)
+			break;
+		if (rc)
+			err = rc;
+	}
+	return err;
+}
+
+static int sync_indirect(struct inode *inode, unsigned short *iblock, int wait)
+{
+	int i;
+	struct buffer_head *ind_bh;
+	int rc, err = 0;
+
+	rc = sync_iblock(inode, iblock, &ind_bh, wait);
+	if (rc || !ind_bh)
+		return rc;
+
+	for (i = 0; i < 512; i++) {
+		rc = sync_block(inode,
+				((unsigned short *) ind_bh->b_data) + i,
+				wait);
+		if (rc > 0)
+			break;
+		if (rc)
+			err = rc;
+	}
+	brelse(ind_bh);
+	return err;
+}
+
+static int sync_dindirect(struct inode *inode, unsigned short *diblock,
+			  int wait)
+{
+	int i;
+	struct buffer_head *dind_bh;
+	int rc, err = 0;
+
+	rc = sync_iblock(inode, diblock, &dind_bh, wait);
+	if (rc || !dind_bh)
+		return rc;
+
+	for (i = 0; i < 512; i++) {
+		rc = sync_indirect(inode,
+				((unsigned short *) dind_bh->b_data) + i,
+				   wait);
+		if (rc > 0)
+			break;
+		if (rc)
+			err = rc;
+	}
+	brelse(dind_bh);
+	return err;
+}
+
+int qnx4_sync_file(struct file *file, struct dentry *dentry)
+{
+        struct inode *inode = dentry->d_inode;
+	int wait, err = 0;
+        
+        (void) file;
+	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+	      S_ISLNK(inode->i_mode)))
+		return -EINVAL;
+
+	for (wait = 0; wait <= 1; wait++) {
+		err |= sync_direct(inode, wait);
+	}
+	err |= qnx4_sync_inode(inode);
+	return (err < 0) ? -EIO : 0;
+}
+
+#endif
diff -u --recursive --new-file v2.1.119/linux/fs/qnx4/inode.c linux/fs/qnx4/inode.c
--- v2.1.119/linux/fs/qnx4/inode.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/qnx4/inode.c	Tue Sep  1 10:43:13 1998
@@ -0,0 +1,463 @@
+/* 
+ * QNX4 file system, Linux implementation.
+ * 
+ * Version : 0.1
+ * 
+ * Using parts of the xiafs filesystem.
+ * 
+ * History :
+ * 
+ * 01-06-1998 by Richard Frowijn : first release.
+ * 20-06-1998 by Frank Denis : Linux 2.1.99+ support, boot signature, misc.
+ * 30-06-1998 by Frank Denis : first step to write inodes.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/qnx4_fs.h>
+#include <linux/fs.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+
+#define QNX4_VERSION  4
+#define QNX4_BMNAME   ".bitmap"
+#define CHECK_BOOT_SIGNATURE 0
+
+static struct super_operations qnx4_sops;
+
+#ifdef CONFIG_QNX4FS_RW
+
+int qnx4_sync_inode(struct inode *inode)
+{
+	int err = 0;
+# if 0
+	struct buffer_head *bh;
+
+   	bh = qnx4_update_inode(inode);
+	if (bh && buffer_dirty(bh))
+	{
+		ll_rw_block(WRITE, 1, &bh);
+		wait_on_buffer(bh);
+		if (buffer_req(bh) && !buffer_uptodate(bh))
+		{
+			printk ("IO error syncing qnx4 inode [%s:%08lx]\n",
+				kdevname(inode->i_dev), inode->i_ino);
+			err = -1;
+		}
+	        brelse (bh);	   
+	} else if (!bh) {
+		err = -1;
+	}
+# endif
+
+	return err;
+}
+
+static void qnx4_delete_inode(struct inode *inode)
+{
+	QNX4DEBUG(("qnx4: deleting inode [%lu]\n", (unsigned long) inode->i_ino));
+	inode->i_size = 0;
+	qnx4_truncate(inode);
+	qnx4_free_inode(inode);
+}
+
+static void qnx4_write_super(struct super_block *sb)
+{
+	QNX4DEBUG(("qnx4: write_super\n"));
+	sb->s_dirt = 0;
+}
+
+static void qnx4_put_inode(struct inode *inode)
+{
+	if (inode->i_nlink != 0) {
+		return;
+	}
+	inode->i_size = 0;
+}
+
+static void qnx4_write_inode(struct inode *inode)
+{
+	struct qnx4_inode_entry *raw_inode;
+	int block, ino;
+	struct buffer_head *bh;
+	ino = inode->i_ino;
+
+	QNX4DEBUG(("qnx4: write inode 1.\n"));
+	if (inode->i_nlink == 0) {
+		return;
+	}
+	if (!ino) {
+		printk("qnx4: bad inode number on dev %s: %d is out of range\n",
+		       kdevname(inode->i_dev), ino);
+		return;
+	}
+	QNX4DEBUG(("qnx4: write inode 2.\n"));
+	block = ino / QNX4_INODES_PER_BLOCK;
+	if (!(bh = bread(inode->i_dev, block, QNX4_BLOCK_SIZE))) {
+		printk("qnx4: major problem: unable to read inode from dev "
+		       "%s\n", kdevname(inode->i_dev));
+		return;
+	}
+	raw_inode = ((struct qnx4_inode_entry *) bh->b_data) +
+	    (ino % QNX4_INODES_PER_BLOCK);
+	raw_inode->di_mode = inode->i_mode;
+	raw_inode->di_uid = inode->i_uid;
+	raw_inode->di_gid = inode->i_gid;
+	raw_inode->di_nlink = inode->i_nlink;
+	raw_inode->di_size = inode->i_size;
+	raw_inode->di_mtime = inode->i_mtime;
+	raw_inode->di_atime = inode->i_atime;
+	raw_inode->di_ctime = inode->i_ctime;
+	raw_inode->di_first_xtnt.xtnt_size = inode->i_blocks;
+	mark_buffer_dirty(bh, 1);
+	brelse(bh);
+}
+
+#endif
+
+static struct super_block *qnx4_read_super(struct super_block *, void *, int);
+static void qnx4_put_super(struct super_block *sb);
+static void qnx4_read_inode(struct inode *);
+static int qnx4_remount(struct super_block *sb, int *flags, char *data);
+static int qnx4_statfs(struct super_block *, struct statfs *, int);
+
+static struct super_operations qnx4_sops =
+{
+	qnx4_read_inode,
+#ifdef CONFIG_QNX4FS_RW
+	qnx4_write_inode,
+#else
+	NULL,
+#endif
+	qnx4_put_inode,
+#ifdef CONFIG_QNX4FS_RW
+	qnx4_delete_inode,
+	NULL,			/* notify_change */
+#else
+	NULL,			/* delete_inode */
+	NULL,			/* notify_change */
+#endif
+	qnx4_put_super,
+#ifdef CONFIG_QNX4FS_RW
+	qnx4_write_super,
+#else
+	NULL,
+#endif
+	qnx4_statfs,
+	qnx4_remount,
+	NULL			/* clear_inode */
+};
+
+static int qnx4_remount(struct super_block *sb, int *flags, char *data)
+{
+	struct qnx4_sb_info *qs;
+
+	qs = &sb->u.qnx4_sb;
+	qs->Version = QNX4_VERSION;
+	if (*flags & MS_RDONLY) {
+		return 0;
+	}
+	mark_buffer_dirty(qs->sb_buf, 1);
+
+	return 0;
+}
+
+struct buffer_head *inode_getblk(struct inode *inode, int nr,
+				 int create)
+{
+	int tmp;
+	int tst;
+	struct buffer_head *result = NULL;
+
+	tst = nr;
+      repeat:
+	tmp = tst;
+	if (tmp) {
+		result = getblk(inode->i_dev, tmp, QNX4_BLOCK_SIZE);
+		if (tmp == tst) {
+			return result;
+		}
+		brelse(result);
+		goto repeat;
+	}
+	if (!create) {
+		return NULL;
+	}
+#if 0
+	tmp = qnx4_new_block(inode->i_sb);
+	if (!tmp) {
+		return NULL;
+	}
+	result = getblk(inode->i_dev, tmp, QNX4_BLOCK_SIZE);
+	if (tst) {
+		qnx4_free_block(inode->i_sb, tmp);
+		brelse(result);
+		goto repeat;
+	}
+	tst = tmp;
+#endif
+	inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty(inode);
+	return result;
+}
+
+struct buffer_head *qnx4_bread(struct inode *inode, int block, int create)
+{
+	struct buffer_head *bh;
+
+	bh = inode_getblk(inode, block, create);
+	if (!bh || buffer_uptodate(bh)) {
+		return bh;
+	}
+	ll_rw_block(READ, 1, &bh);
+	wait_on_buffer(bh);
+	if (buffer_uptodate(bh)) {
+		return bh;
+	}
+	brelse(bh);
+
+	return NULL;
+}
+
+static int qnx4_statfs(struct super_block *sb,
+		       struct statfs *buf, int bufsize)
+{
+	struct statfs tmp;
+
+	memset(&tmp, 0, sizeof tmp);
+	tmp.f_type = sb->s_magic;
+	tmp.f_bsize = sb->s_blocksize;
+	tmp.f_blocks = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_size) * 8;
+	tmp.f_bfree = qnx4_count_free_blocks(sb);
+	tmp.f_bavail = tmp.f_bfree;
+	tmp.f_files = 0x00;	/* change this !!! */
+	tmp.f_ffree = qnx4_count_free_inodes(sb);
+	tmp.f_namelen = QNX4_NAME_MAX;
+
+	return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0;
+}
+
+/*
+ * Check the root directory of the filesystem to make sure
+ * it really _is_ a qnx4 filesystem, and to check the size
+ * of the directory entry.
+ */
+static const char *qnx4_checkroot(struct super_block *s)
+{
+	struct buffer_head *bh;
+	struct qnx4_inode_entry *rootdir;
+	int rd, rl;
+	int i, j;
+	int found = 0;
+
+	if (s == NULL) {
+		return "no qnx4 filesystem (null superblock).";
+	}
+	if (*(s->u.qnx4_sb.sb->RootDir.di_fname) != '/') {
+		return "no qnx4 filesystem (no root dir).";
+	} else {
+		QNX4DEBUG(("QNX4 filesystem found on dev %s.\n", kdevname(s->s_dev)));
+		rd = s->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_blk - 1;
+		rl = s->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_size;
+		for (j = 0; j < rl; j++) {
+			bh = bread(s->s_dev, rd + j, QNX4_BLOCK_SIZE);	/* root dir, first block */
+			if (bh == NULL) {
+				return "unable to read root entry.";
+			}
+			for (i = 0; i < QNX4_INODES_PER_BLOCK; i++) {
+				rootdir = (struct qnx4_inode_entry *) (bh->b_data + i * QNX4_DIR_ENTRY_SIZE);
+				if (rootdir->di_fname != NULL) {
+					QNX4DEBUG(("Rootdir entry found : [%s]\n", rootdir->di_fname));
+					if (!strncmp(rootdir->di_fname, QNX4_BMNAME, sizeof QNX4_BMNAME)) {
+						found = 1;
+						s->u.qnx4_sb.BitMap = rootdir;	/* keep bitmap inode known */
+						break;
+					}
+				}
+			}
+			brelse(bh);
+			if (found != 0) {
+				break;
+			}
+		}
+		if (found == 0) {
+			return "bitmap file not found.";
+		}
+	}
+	return NULL;
+}
+
+static struct super_block *qnx4_read_super(struct super_block *s, 
+					   void *data, int silent)
+{
+	struct buffer_head *bh;
+	kdev_t dev = s->s_dev;
+#if CHECK_BOOT_SIGNATURE
+	char *tmpc;
+#endif
+	const char *errmsg;
+
+	MOD_INC_USE_COUNT;
+	lock_super(s);
+	set_blocksize(dev, QNX4_BLOCK_SIZE);
+	s->s_blocksize = QNX4_BLOCK_SIZE;
+	s->s_blocksize_bits = 9;
+	s->s_dev = dev;
+
+#if CHECK_BOOT_SIGNATURE
+	bh = bread(dev, 0, QNX4_BLOCK_SIZE);
+	if (!bh) {
+		printk("qnx4: unable to read the boot sector\n");
+		goto outnobh;
+	}
+	tmpc = (char *) bh->b_data;
+	if (tmpc[4] != 'Q' || tmpc[5] != 'N' || tmpc[6] != 'X' ||
+	    tmpc[7] != '4' || tmpc[8] != 'F' || tmpc[9] != 'S') {
+		printk("qnx4: wrong fsid in boot sector.\n");
+		goto out;
+	}
+	brelse(bh);
+#endif
+	bh = bread(dev, 1, QNX4_BLOCK_SIZE);
+	if (!bh) {
+		printk("qnx4: unable to read the superblock\n");
+		goto outnobh;
+	}
+	s->s_op = &qnx4_sops;
+	s->s_magic = QNX4_SUPER_MAGIC;
+#ifndef CONFIG_QNX4FS_RW
+	s->s_flags |= MS_RDONLY;	/* Yup, read-only yet */
+#endif
+	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);
+	if (s->s_root == NULL) {
+		printk("qnx4: get inode failed\n");
+		goto out;
+	}
+	errmsg = qnx4_checkroot(s);
+	if (errmsg != NULL) {
+		printk("qnx4: %s\n", errmsg);
+		goto out;
+	}
+	brelse(bh);
+	unlock_super(s);
+	s->s_dirt = 1;
+
+	return s;
+
+      out:
+	brelse(bh);
+      outnobh:
+	s->s_dev = 0;
+	unlock_super(s);
+	MOD_DEC_USE_COUNT;
+
+	return NULL;
+}
+
+static void qnx4_put_super(struct super_block *sb)
+{
+	MOD_DEC_USE_COUNT;
+	return;
+}
+
+static void qnx4_read_inode(struct inode *inode)
+{
+	struct buffer_head *bh;
+	struct qnx4_inode_entry *raw_inode;
+	int block, ino;
+
+	ino = inode->i_ino;
+	inode->i_op = NULL;
+	inode->i_mode = 0;
+
+	QNX4DEBUG(("Reading inode : [%d]\n", ino));
+	if (!ino) {
+		printk("qnx4: bad inode number on dev %s: %d is out of range\n",
+		       kdevname(inode->i_dev), ino);
+		return;
+	}
+	block = ino / QNX4_INODES_PER_BLOCK;
+
+	if (!(bh = bread(inode->i_dev, block, QNX4_BLOCK_SIZE))) {
+		printk("qnx4: major problem: unable to read inode from dev "
+		       "%s\n", kdevname(inode->i_dev));
+		return;
+	}
+	raw_inode = ((struct qnx4_inode_entry *) bh->b_data) +
+	    (ino % QNX4_INODES_PER_BLOCK);
+
+	inode->i_mode = raw_inode->di_mode;
+	inode->i_uid = raw_inode->di_uid;
+	inode->i_gid = raw_inode->di_gid;
+	inode->i_nlink = raw_inode->di_nlink;
+	inode->i_size = raw_inode->di_size;
+	inode->i_mtime = raw_inode->di_mtime;
+	inode->i_atime = raw_inode->di_atime;
+	inode->i_ctime = raw_inode->di_ctime;
+	inode->i_blocks = raw_inode->di_first_xtnt.xtnt_size;
+	inode->i_blksize = QNX4_DIR_ENTRY_SIZE;
+
+	memcpy(&inode->u.qnx4_i, (struct qnx4_inode_info *) raw_inode, QNX4_DIR_ENTRY_SIZE);
+	inode->i_op = &qnx4_file_inode_operations;
+	if (S_ISREG(inode->i_mode)) {
+		inode->i_op = &qnx4_file_inode_operations;
+	} else {
+		if (S_ISDIR(inode->i_mode)) {
+			inode->i_op = &qnx4_dir_inode_operations;
+		} else {
+			if (S_ISLNK(inode->i_mode)) {
+				inode->i_op = &qnx4_symlink_inode_operations;
+			} else {
+				if (S_ISCHR(inode->i_mode)) {
+					inode->i_op = &chrdev_inode_operations;
+				} else {
+					if (S_ISBLK(inode->i_mode)) {
+						inode->i_op = &blkdev_inode_operations;
+					} else {
+						if (S_ISFIFO(inode->i_mode)) {
+							init_fifo(inode);
+						}
+					}
+				}
+			}
+		}
+	}
+	brelse(bh);
+}
+
+static struct file_system_type qnx4_fs_type =
+{
+	"qnx4",
+	FS_REQUIRES_DEV,
+	qnx4_read_super,
+	NULL
+};
+
+__initfunc(int init_qnx4_fs(void))
+{
+	printk("QNX4 filesystem v0.2 registered.\n");
+	return register_filesystem(&qnx4_fs_type);
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+	return init_qnx4_fs();
+}
+
+void cleanup_module(void)
+{
+	unregister_filesystem(&qnx4_fs_type);
+}
+
+#endif
diff -u --recursive --new-file v2.1.119/linux/fs/qnx4/namei.c linux/fs/qnx4/namei.c
--- v2.1.119/linux/fs/qnx4/namei.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/qnx4/namei.c	Tue Sep  1 10:43:13 1998
@@ -0,0 +1,279 @@
+/* 
+ * QNX4 file system, Linux implementation.
+ * 
+ * Version : 0.1
+ * 
+ * Using parts of the xiafs filesystem.
+ * 
+ * History :
+ * 
+ * 01-06-1998 by Richard Frowijn : first release.
+ * 21-06-1998 by Frank Denis : dcache support, fixed error codes.
+ * 04-07-1998 by Frank Denis : first step for rmdir/unlink.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/qnx4_fs.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/errno.h>
+
+#include <asm/segment.h>
+
+/*
+ * check if the filename is correct. For some obscure reason, qnx writes a
+ * new file twice in the directory entry, first with all possible options at 0
+ * and for a second time the way it is, they want us not to access the qnx
+ * filesystem when whe are using linux.
+ */
+static int qnx4_match(int len, const char *name,
+		      struct buffer_head *bh, unsigned long *offset)
+{
+	struct qnx4_inode_entry *de;
+	int namelen;
+
+	if (bh == NULL) {
+		printk("qnx4: matching unassigned buffer !\n");
+		return 0;
+	}
+	de = (struct qnx4_inode_entry *) (bh->b_data + *offset);
+	*offset += QNX4_DIR_ENTRY_SIZE;
+	if ((de->di_status & 0x08) == 0x08) {
+		namelen = QNX4_NAME_MAX;
+	} else {
+		namelen = _SHORT_NAME_MAX;
+	}
+	/* "" means "." ---> so paths like "/usr/lib//libc.a" work */
+	if (!len && (de->di_fname[0] == '.') && (de->di_fname[1] == '\0')) {
+		return 1;
+	}
+	if (len != strlen(de->di_fname)) {
+		return 0;
+	}
+	if (strncmp(name, de->di_fname, len) == 0) {
+		if ((de->di_mode) || (de->di_status == QNX4_FILE_LINK)) {
+			if (de->di_status) {
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+static struct buffer_head *qnx4_find_entry(int len, struct inode *dir,
+	   const char *name, struct qnx4_inode_entry **res_dir, int *ino)
+{
+	unsigned long block, offset, blkofs;
+	struct buffer_head *bh;
+
+	*res_dir = NULL;
+	if (!dir || !dir->i_sb) {
+		if (!dir) {
+			printk("qnx4: NULL dir.\n");
+		} else {
+			printk("qnx4: no superblock on dir.\n");
+		}
+		return NULL;
+	}
+	bh = NULL;
+	blkofs = dir->u.qnx4_i.i_first_xtnt.xtnt_blk - 1;
+	offset = block = 0;
+	while (block * QNX4_BLOCK_SIZE + offset < dir->i_size) {
+		if (!bh) {
+			bh = qnx4_bread(dir, block + blkofs, 0);
+			if (!bh) {
+				block++;
+				continue;
+			}
+		}
+		*res_dir = (struct qnx4_inode_entry *) (bh->b_data + offset);
+		if (qnx4_match(len, name, bh, &offset)) {
+			*ino = (block + blkofs) * QNX4_INODES_PER_BLOCK +
+			    (offset / QNX4_DIR_ENTRY_SIZE) - 1;
+			return bh;
+		}
+		if (offset < bh->b_size) {
+			continue;
+		}
+		brelse(bh);
+		bh = NULL;
+		offset = 0;
+		block++;
+	}
+	brelse(bh);
+	*res_dir = NULL;
+	return NULL;
+}
+
+int qnx4_lookup(struct inode *dir, struct dentry *dentry)
+{
+	int ino;
+	struct qnx4_inode_entry *de;
+	struct qnx4_link_info *lnk;
+	struct buffer_head *bh;
+	const char *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
+	struct inode *foundinode;
+
+	if (!dir) {
+		return -EBADF;
+	}
+	if (!S_ISDIR(dir->i_mode)) {
+		return -EBADF;
+	}
+	if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino))) {
+		return -ENOENT;
+	}
+	/* The entry is linked, let's get the real info */
+	if ((de->di_status & QNX4_FILE_LINK) == QNX4_FILE_LINK) {
+		lnk = (struct qnx4_link_info *) de;
+		ino = (lnk->dl_inode_blk - 1) * QNX4_INODES_PER_BLOCK +
+		    lnk->dl_inode_ndx;
+	}
+	brelse(bh);
+
+	if ((foundinode = iget(dir->i_sb, ino)) == NULL) {
+		QNX4DEBUG(("qnx4: lookup->iget -> NULL\n"));
+		return -EACCES;
+	}
+	d_add(dentry, foundinode);
+
+	return 0;
+}
+
+#ifdef CONFIG_QNX4FS_RW
+int qnx4_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+	QNX4DEBUG(("qnx4: qnx4_create\n"));
+	if (dir == NULL) {
+		return -ENOENT;
+	}
+	return -ENOSPC;
+}
+
+int qnx4_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	struct buffer_head *bh;
+	struct qnx4_inode_entry *de;
+	struct inode *inode;
+	int retval;
+	int ino;
+
+	QNX4DEBUG(("qnx4: qnx4_rmdir [%s]\n", dentry->d_name.name));
+	bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name,
+			     &de, &ino);
+	if (bh == NULL) {
+		return -ENOENT;
+	}
+	if ((inode = iget(dir->i_sb, ino)) == NULL) {
+		QNX4DEBUG(("qnx4: lookup->iget -> NULL\n"));
+		retval = -EACCES;
+		goto end_rmdir;
+	}
+	retval = -EPERM;
+	if ((dir->i_mode & S_ISVTX) &&
+	    current->fsuid != inode->i_uid &&
+	    current->fsuid != dir->i_uid && !capable(CAP_FOWNER)) {
+		QNX4DEBUG(("qnx4: rmdir->capabilities\n"));
+		goto end_rmdir;
+	}
+	if (inode->i_dev != dir->i_dev) {
+		QNX4DEBUG(("qnx4: rmdir->different devices\n"));
+		goto end_rmdir;
+	}
+	if (inode == dir) {	/* we may not delete ".", but "../dir" is ok */
+		QNX4DEBUG(("qnx4: inode==dir\n"));
+		goto end_rmdir;
+	}
+	if (!S_ISDIR(inode->i_mode)) {
+		QNX4DEBUG(("qnx4: rmdir->not a directory\n"));
+		retval = -ENOTDIR;
+		goto end_rmdir;
+	}
+#if 0
+	if (!empty_dir(inode)) {
+		retval = -ENOTEMPTY;
+		goto end_rmdir;
+	}
+#endif
+	if (dentry->d_count > 1) {
+		retval = -EBUSY;
+		goto end_rmdir;
+	}
+	if (inode->i_nlink != 2) {
+		QNX4DEBUG(("empty directory has nlink!=2 (%d)\n", inode->i_nlink));
+	}
+	QNX4DEBUG(("qnx4: deleting directory\n"));
+	de->di_status = 0;
+	memset(de->di_fname, 0, sizeof de->di_fname);
+	de->di_mode = 0;
+	mark_buffer_dirty(bh, 1);
+	inode->i_nlink = 0;
+	mark_inode_dirty(inode);
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	dir->i_nlink--;
+	mark_inode_dirty(dir);
+	d_delete(dentry);
+	retval = 0;
+
+      end_rmdir:
+	brelse(bh);
+
+	return retval;
+}
+
+int qnx4_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct buffer_head *bh;
+	struct qnx4_inode_entry *de;
+	struct inode *inode;
+	int retval;
+	int ino;
+
+	QNX4DEBUG(("qnx4: qnx4_unlink [%s]\n", dentry->d_name.name));
+	bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name,
+			     &de, &ino);
+	if (bh == NULL) {
+		return -ENOENT;
+	}
+	if ((inode = iget(dir->i_sb, ino)) == NULL) {
+		QNX4DEBUG(("qnx4: lookup->iget -> NULL\n"));
+		retval = -EACCES;
+		goto end_unlink;
+	}
+	retval = -EPERM;
+	if (S_ISDIR(inode->i_mode)) {
+		goto end_unlink;
+	}
+	if ((dir->i_mode & S_ISVTX) &&
+	    current->fsuid != inode->i_uid &&
+	    current->fsuid != dir->i_uid && !capable(CAP_FOWNER)) {
+		goto end_unlink;
+	}
+	if (!inode->i_nlink) {
+		QNX4DEBUG(("Deleting nonexistent file (%s:%lu), %d\n",
+			   kdevname(inode->i_dev),
+			   inode->i_ino, inode->i_nlink));
+		inode->i_nlink = 1;
+	}
+	de->di_status = 0;
+	memset(de->di_fname, 0, sizeof de->di_fname);
+	de->di_mode = 0;
+	mark_buffer_dirty(bh, 1);
+	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	mark_inode_dirty(dir);
+	inode->i_nlink--;
+	inode->i_ctime = dir->i_ctime;
+	mark_inode_dirty(inode);
+	d_delete(dentry);
+	retval = 0;
+
+      end_unlink:
+	brelse(bh);
+
+	return retval;
+}
+#endif
diff -u --recursive --new-file v2.1.119/linux/fs/qnx4/symlinks.c linux/fs/qnx4/symlinks.c
--- v2.1.119/linux/fs/qnx4/symlinks.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/qnx4/symlinks.c	Tue Sep  1 10:43:13 1998
@@ -0,0 +1,121 @@
+/* 
+ * QNX4 file system, Linux implementation.
+ * 
+ * Version : 0.1
+ * 
+ * Using parts of the xiafs filesystem.
+ * 
+ * History :
+ * 
+ * 28-05-1998 by Richard Frowijn : first release.
+ * 21-06-1998 by Frank Denis : ugly changes to make it compile on Linux 2.1.99+
+ */
+
+/* THIS FILE HAS TO BE REWRITTEN */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/qnx4_fs.h>
+#include <linux/stat.h>
+
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+static int qnx4_readlink(struct dentry *, char *, int);
+static struct dentry *qnx4_follow_link(struct dentry *, struct dentry *);
+
+/*
+ * symlinks can't do much...
+ */
+struct inode_operations qnx4_symlink_inode_operations =
+{
+	NULL,			/* no file-operations */
+#ifdef CONFIG_QNX4FS_RW
+	qnx4_create,		/* create */
+#else
+	NULL,
+#endif
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	qnx4_readlink,		/* readlink */
+	qnx4_follow_link,	/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
+
+static struct dentry *qnx4_follow_link(struct dentry *dentry,
+				       struct dentry *base)
+{
+#if 0
+	struct inode *inode = dentry->d_inode;
+	struct buffer_head *bh;
+
+	if (!inode) {
+		return ERR_PTR(-ENOENT);
+	}
+	if (current->link_count > 5) {
+		return ERR_PTR(-ELOOP);
+	}
+	if (!(bh = qnx4_bread(inode, 0, 0))) {
+		return ERR_PTR(-EIO);
+	}
+	current->link_count++;
+	current->link_count--;
+	brelse(bh);
+	return 0;
+#else
+	printk("qnx4: qnx4_follow_link needs to be fixed.\n");
+	return ERR_PTR(-EIO);
+#endif
+}
+
+static int qnx4_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+	struct inode *inode = dentry->d_inode;
+	struct buffer_head *bh;
+	int i;
+	char c;
+	struct qnx4_inode_info *qnx4_ino;
+
+	QNX4DEBUG(("qnx4: qnx4_readlink() called\n"));
+
+	if (buffer == NULL || inode == NULL || !S_ISLNK(inode->i_mode)) {
+		return -EINVAL;
+	}
+	qnx4_ino = &inode->u.qnx4_i;
+	if (buflen > 1023) {
+		buflen = 1023;
+	}
+	bh = bread(inode->i_dev, qnx4_ino->i_first_xtnt.xtnt_blk,
+		   QNX4_BLOCK_SIZE);
+	QNX4DEBUG(("qnx4: qnx4_bread sym called -> [%s]\n",
+		   bh->b_data));
+	if (bh == NULL) {
+		QNX4DEBUG(("qnx4: NULL symlink bh\n"));
+		return 0;
+	}
+	if (bh->b_data[0] != 0) {
+		i = 0;
+		while (i < buflen && (c = bh->b_data[i])) {
+			i++;
+			put_user(c, buffer++);
+		}
+		brelse(bh);
+		return i;
+	} else {
+		brelse(bh);
+		memcpy(buffer, "fixme", 5);
+		return 5;
+	}
+}
diff -u --recursive --new-file v2.1.119/linux/fs/qnx4/truncate.c linux/fs/qnx4/truncate.c
--- v2.1.119/linux/fs/qnx4/truncate.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/qnx4/truncate.c	Tue Sep  1 10:43:13 1998
@@ -0,0 +1,38 @@
+/* 
+ * QNX4 file system, Linux implementation.
+ * 
+ * Version : 0.1
+ * 
+ * Using parts of the xiafs filesystem.
+ * 
+ * History :
+ * 
+ * 30-06-1998 by Frank DENIS : ugly filler.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/qnx4_fs.h>
+#include <linux/fs.h>
+#include <linux/locks.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_QNX4FS_RW
+
+void qnx4_truncate(struct inode *inode)
+{
+	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+	      S_ISLNK(inode->i_mode))) {
+		return;
+	}
+	if (!(S_ISDIR(inode->i_mode))) {
+		/* TODO */
+	}
+	QNX4DEBUG(("qnx4: qnx4_truncate called\n"));
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty(inode);
+}
+
+#endif
diff -u --recursive --new-file v2.1.119/linux/fs/read_write.c linux/fs/read_write.c
--- v2.1.119/linux/fs/read_write.c	Tue Aug 18 22:02:06 1998
+++ linux/fs/read_write.c	Mon Aug 31 12:11:26 1998
@@ -288,7 +288,7 @@
 	file = fget(fd);
 	if (!file)
 		goto bad_file;
-	if (file->f_mode & FMODE_READ)
+	if (file->f_op && file->f_op->read && (file->f_mode & FMODE_READ))
 		ret = do_readv_writev(VERIFY_WRITE, file, vector, count);
 	fput(file);
 
@@ -309,7 +309,7 @@
 	file = fget(fd);
 	if (!file)
 		goto bad_file;
-	if (file->f_mode & FMODE_WRITE) {
+	if (file->f_op && file->f_op->write && (file->f_mode & FMODE_WRITE)) {
 		down(&file->f_dentry->d_inode->i_sem);
 		ret = do_readv_writev(VERIFY_READ, file, vector, count);
 		up(&file->f_dentry->d_inode->i_sem);
diff -u --recursive --new-file v2.1.119/linux/fs/ufs/acl.c linux/fs/ufs/acl.c
--- v2.1.119/linux/fs/ufs/acl.c	Tue Jul 28 14:21:09 1998
+++ linux/fs/ufs/acl.c	Tue Sep  1 10:50:11 1998
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 1998
  * Daniel Pirkl <daniel.pirkl@email.cz>
- * Charles Uiversity, Faculty of Mathematics and Physics
+ * Charles University, Faculty of Mathematics and Physics
  *
  *  from
  *
@@ -16,8 +16,7 @@
  */
 
 /*
- * This file will contain the Access Control Lists management for the
- * second extended file system.
+ * This file will contain the Access Control Lists management for UFS
  */
 
 #include <linux/errno.h>
@@ -39,8 +38,8 @@
 	 * Nobody gets write access to a file on a readonly-fs
 	 */
 	if ((mask & S_IWOTH) && 
-            (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) &&
-            IS_RDONLY(inode))
+	    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) &&
+	    IS_RDONLY(inode))
 		return -EROFS;
 	/*
 	 * Nobody gets write access to an immutable file
@@ -57,7 +56,7 @@
 		mode >>= 3;
 	/*
 	 * Access is always granted for root. We now check last,
-         * though, for BSD process accounting correctness
+	 * though, for BSD process accounting correctness
 	 */
 	if (((mode & mask & S_IRWXO) == mask) || fsuser())
 		return 0;
diff -u --recursive --new-file v2.1.119/linux/fs/ufs/balloc.c linux/fs/ufs/balloc.c
--- v2.1.119/linux/fs/ufs/balloc.c	Tue Jul 28 14:21:09 1998
+++ linux/fs/ufs/balloc.c	Tue Sep  1 10:50:11 1998
@@ -32,7 +32,7 @@
 #define UFSDM \
 ufs_print_cylinder_stuff (ucg, swab); \
 printk("inode: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nifree), \
-swab32(sb->fs_cs(ucpi->c_cgx).cs_nifree), SWAB32(ucg->cg_cs.cs_nifree)); \
+SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nifree), SWAB32(ucg->cg_cs.cs_nifree)); \
 printk("block: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nbfree), \
 SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nbfree), SWAB32(ucg->cg_cs.cs_nbfree)); \
 printk("fragment: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nffree), \
@@ -133,7 +133,7 @@
 	if (sb->s_flags & MS_SYNCHRONOUS) {
 		ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi);
 		ubh_wait_on_buffer (UCPI_UBH);
-        }
+	}
 	sb->s_dirt = 1;
 	
 	unlock_super (sb);
@@ -211,7 +211,7 @@
 		cylno = ufs_cbtocylno(i);
 		INC_SWAB16(ubh_cg_blks(ucpi, cylno, ufs_cbtorpos(i)));
 		INC_SWAB32(ubh_cg_blktot(ucpi, cylno));
-  	}
+	}
 
 	UFSDM
 	
@@ -220,7 +220,7 @@
 	if (sb->s_flags & MS_SYNCHRONOUS) {
 		ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi);
 		ubh_wait_on_buffer (UCPI_UBH);
-        }
+	}
 
 	if (overflow) {
 		fragment += count;
@@ -249,7 +249,7 @@
 		mark_buffer_dirty (bh, 1); \
 		if (IS_SYNC(inode)) { \
 			ll_rw_block (WRITE, 1, &bh); \
-  			wait_on_buffer (bh); \
+			wait_on_buffer (bh); \
 		} \
 		brelse (bh); \
 	}
@@ -328,7 +328,7 @@
 	 * allocate new fragment
 	 */
 	if (oldcount == 0) {
-	        result = ufs_alloc_fragments (inode, cgno, goal, count, err);
+		result = ufs_alloc_fragments (inode, cgno, goal, count, err);
 		if (result) {
 			*p = SWAB32(result);
 			*err = 0;
@@ -373,7 +373,7 @@
 		request = uspi->s_fpb;
 		if (SWAB32(usb1->fs_cstotal.cs_nffree) < uspi->s_dsize *
 		    (uspi->s_minfree - 2) / 100)
-		    	break;
+			break;
 		usb1->fs_optim = SWAB32(UFS_OPTSPACE);
 		break;
 	}
@@ -532,7 +532,7 @@
 	}
 
 	/*
- 	 * 3. brute force search
+	 * 3. brute force search
 	 * We start at i = 2 ( 0 is checked at 1.step, 1 at 2.step )
 	 */
 	cgno = (oldcg + 1) % uspi->s_ncg;
@@ -642,7 +642,7 @@
 		goto norot;
 	}
 	goal = ufs_blknum (goal);
- 	goal = ufs_dtogd (goal);
+	goal = ufs_dtogd (goal);
 	
 	/*
 	 * If the requested block is available, use it.
@@ -652,7 +652,7 @@
 		goto gotit;
 	}
 	
-	/*** This function should be optimalized later ***/
+	/*** This function should be optimized later ***/
 
 norot:	
 	result = ufs_bitmap_search (sb, ucpi, goal, uspi->s_fpb);
diff -u --recursive --new-file v2.1.119/linux/fs/ufs/dir.c linux/fs/ufs/dir.c
--- v2.1.119/linux/fs/ufs/dir.c	Wed Aug 26 11:37:43 1998
+++ linux/fs/ufs/dir.c	Tue Sep  1 10:50:11 1998
@@ -6,7 +6,7 @@
  * Laboratory for Computer Science Research Computing Facility
  * Rutgers, The State University of New Jersey
  *
- * swab support by Francois-Rene Rideau <rideau@ens.fr> 19970406
+ * swab support by Francois-Rene Rideau <fare@tunes.org> 19970406
  *
  * 4.4BSD (FreeBSD) support added on February 1st 1998 by
  * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
@@ -46,14 +46,14 @@
 
 
 	/* Isn't that already done in the upper layer???
-         * the VFS layer really needs some explicit documentation!
-         */
+	 * the VFS layer really needs some explicit documentation!
+	 */
 	if (!inode || !S_ISDIR(inode->i_mode))
 		return -EBADF;
 
 	sb = inode->i_sb;
 	swab = sb->u.ufs_sb.s_swab;
-        flags = sb->u.ufs_sb.s_flags;
+	flags = sb->u.ufs_sb.s_flags;
 
 	UFSD(("ENTER, ino %lu  f_pos %lu\n", inode->i_ino, (unsigned long) filp->f_pos))
 
@@ -63,14 +63,14 @@
 
 	while (!error && !stored && filp->f_pos < inode->i_size) {
 		lblk = (filp->f_pos) >> sb->s_blocksize_bits;
-	        /* XXX - ufs_bmap() call needs error checking */
-	        blk = ufs_bmap(inode, lblk);
+		/* XXX - ufs_bmap() call needs error checking */
+		blk = ufs_bmap(inode, lblk);
 		bh = bread (sb->s_dev, blk, sb->s_blocksize);
 		if (!bh) {
-	                /* XXX - error - skip to the next block */
-	                printk("ufs_readdir: "
+			/* XXX - error - skip to the next block */
+			printk("ufs_readdir: "
 			       "dir inode %lu has a hole at offset %lu\n",
-	                       inode->i_ino, (unsigned long int)filp->f_pos);
+			       inode->i_ino, (unsigned long int)filp->f_pos);
 			filp->f_pos += sb->s_blocksize - offset;
 			continue;
 		}
@@ -103,23 +103,23 @@
 		while (!error && filp->f_pos < inode->i_size
 		       && offset < sb->s_blocksize) {
 			de = (struct ufs_dir_entry *) (bh->b_data + offset);
-	                /* XXX - put in a real ufs_check_dir_entry() */
-	                if ((de->d_reclen == 0) || (ufs_namlen(de) == 0)) {
+			/* XXX - put in a real ufs_check_dir_entry() */
+			if ((de->d_reclen == 0) || (ufs_namlen(de) == 0)) {
 			/* SWAB16() was unneeded -- compare to 0 */
-	                        filp->f_pos = (filp->f_pos &
-				              (sb->s_blocksize - 1)) +
-				               sb->s_blocksize;
-	                        brelse(bh);
-	                        return stored;
-	                }
+				filp->f_pos = (filp->f_pos &
+					      (sb->s_blocksize - 1)) +
+					       sb->s_blocksize;
+				brelse(bh);
+				return stored;
+			}
 #if 0 /* XXX */
 			if (!ext2_check_dir_entry ("ext2_readdir", inode, de,
 			/* XXX - beware about de having to be swabped somehow */
 						   bh, offset)) {
 				/* On error, skip the f_pos to the
-	                           next block. */
+				   next block. */
 				filp->f_pos = (filp->f_pos &
-				              (sb->s_blocksize - 1)) +
+					      (sb->s_blocksize - 1)) +
 					       sb->s_blocksize;
 				brelse (bh);
 				return stored;
@@ -139,7 +139,7 @@
 				UFSD(("filldir(%s,%u)\n", de->d_name, SWAB32(de->d_ino)))
 				UFSD(("namlen %u\n", ufs_namlen(de)))
 				error = filldir(dirent, de->d_name, ufs_namlen(de),
-				                filp->f_pos, SWAB32(de->d_ino));
+						filp->f_pos, SWAB32(de->d_ino));
 				if (error)
 					break;
 				if (version != inode->i_version)
diff -u --recursive --new-file v2.1.119/linux/fs/ufs/inode.c linux/fs/ufs/inode.c
--- v2.1.119/linux/fs/ufs/inode.c	Tue Jul 28 14:21:09 1998
+++ linux/fs/ufs/inode.c	Tue Sep  1 10:50:11 1998
@@ -91,7 +91,7 @@
 		return 0;
 	tmp = SWAB32(((u32 *) bh->b_data)[nr >> uspi->s_fpbshift]) + (nr & uspi->s_fpbmask);
 	brelse (bh);
-	UFSD(("EXIT, resutl %u\n", tmp))
+	UFSD(("EXIT, result %u\n", tmp))
 	return tmp;
 }
 
@@ -254,7 +254,7 @@
 	 */
 	else /* (lastblock > block) */ {
 		if (lastblock && (tmp = SWAB32(inode->u.ufs_i.i_u1.i_data[lastblock-1])))
- 			goal = tmp + uspi->s_fpb;
+			goal = tmp + uspi->s_fpb;
 		tmp = ufs_new_fragments (inode, p, fragment - blockoff, 
 			goal, uspi->s_fpb, err);
 	}
@@ -322,10 +322,10 @@
 		*err = -EFBIG;
 		return NULL;
 	}
- 	if (block && (tmp = SWAB32(((u32*)bh->b_data)[block-1]) + uspi->s_fpb))
- 		goal = tmp + uspi->s_fpb;
- 	else
- 		goal = bh->b_blocknr + uspi->s_fpb;
+	if (block && (tmp = SWAB32(((u32*)bh->b_data)[block-1]) + uspi->s_fpb))
+		goal = tmp + uspi->s_fpb;
+	else
+		goal = bh->b_blocknr + uspi->s_fpb;
 	tmp = ufs_new_fragments (inode, p, ufs_blknum(new_fragment), goal, uspi->s_fpb, err);
 	if (!tmp) {
 		if (SWAB32(*p)) {
@@ -345,7 +345,7 @@
 	inode->i_ctime = CURRENT_TIME;
 	mark_inode_dirty(inode);
 	brelse (bh);
-	UFSD(("EXIT, resutl %u\n", tmp + blockoff))
+	UFSD(("EXIT, result %u\n", tmp + blockoff))
 	return result;
 }
 
@@ -492,7 +492,7 @@
 	}
 	
 	/*
-	 * Linux i_size can be 32 on some architektures. We will mark 
+	 * Linux i_size can be 32 on some architectures. We will mark 
 	 * big files as read only and let user access first 32 bits.
 	 */
 	inode->u.ufs_i.i_size = SWAB64(ufs_inode->ui_size);
diff -u --recursive --new-file v2.1.119/linux/fs/ufs/namei.c linux/fs/ufs/namei.c
--- v2.1.119/linux/fs/ufs/namei.c	Tue Jul 28 14:21:09 1998
+++ linux/fs/ufs/namei.c	Tue Sep  1 10:50:11 1998
@@ -266,9 +266,9 @@
 	de = (struct ufs_dir_entry *) bh->b_data;
 	*err = -ENOSPC;
 	while (1) {
-		if ((char *)de >= SECTOR_SIZE + bh->b_data) {
+		if ((char *)de >= UFS_SECTOR_SIZE + bh->b_data) {
 			fragoff = offset & ~uspi->s_fmask;
-			if (fragoff != 0 && fragoff != SECTOR_SIZE)
+			if (fragoff != 0 && fragoff != UFS_SECTOR_SIZE)
 				ufs_error (sb, "ufs_add_entry", "internal error"
 					" fragoff %u", fragoff);
 			if (!fragoff) {
@@ -285,9 +285,9 @@
 				}
 				de = (struct ufs_dir_entry *) (bh->b_data + fragoff);
 				de->d_ino = SWAB32(0);
-				de->d_reclen = SWAB16(SECTOR_SIZE);
+				de->d_reclen = SWAB16(UFS_SECTOR_SIZE);
 				de->d_u.d_namlen = SWAB16(0);
-				dir->i_size = offset + SECTOR_SIZE;
+				dir->i_size = offset + UFS_SECTOR_SIZE;
 				mark_inode_dirty(dir);
 			} else {
 				de = (struct ufs_dir_entry *) bh->b_data;
@@ -384,11 +384,11 @@
 			return 0;
 		}
 		i += SWAB16(de->d_reclen);
-		if (i == SECTOR_SIZE) pde = NULL;
+		if (i == UFS_SECTOR_SIZE) pde = NULL;
 		else pde = de;
 		de = (struct ufs_dir_entry *)
 		    ((char *) de + SWAB16(de->d_reclen));
-		if (i == SECTOR_SIZE && SWAB16(de->d_reclen) == 0)
+		if (i == UFS_SECTOR_SIZE && SWAB16(de->d_reclen) == 0)
 			break;
 	}
 	UFSD(("EXIT\n"))
@@ -537,7 +537,7 @@
 		goto out;
 
 	inode->i_op = &ufs_dir_inode_operations;
-	inode->i_size = SECTOR_SIZE;
+	inode->i_size = UFS_SECTOR_SIZE;
 	dir_block = ufs_bread (inode, 0, 1, &err);
 	if (!dir_block) {
 		inode->i_nlink--; /* is this nlink == 0? */
@@ -545,7 +545,7 @@
 		iput (inode);
 		return err;
 	}
-	inode->i_blocks = sb->s_blocksize / SECTOR_SIZE;
+	inode->i_blocks = sb->s_blocksize / UFS_SECTOR_SIZE;
 	de = (struct ufs_dir_entry *) dir_block->b_data;
 	de->d_ino = SWAB32(inode->i_ino);
 	de->d_u.d_namlen = SWAB16(1);
@@ -553,7 +553,7 @@
 	strcpy (de->d_name, ".");
 	de = (struct ufs_dir_entry *) ((char *) de + SWAB16(de->d_reclen));
 	de->d_ino = SWAB32(dir->i_ino);
-	de->d_reclen = SWAB16(SECTOR_SIZE - UFS_DIR_REC_LEN(1));
+	de->d_reclen = SWAB16(UFS_SECTOR_SIZE - UFS_DIR_REC_LEN(1));
 	de->d_u.d_namlen = SWAB16(2);
 	strcpy (de->d_name, "..");
 	inode->i_nlink = 2;
@@ -605,7 +605,7 @@
 
 	if (inode->i_size < UFS_DIR_REC_LEN(1) + UFS_DIR_REC_LEN(2) ||
 	    !(bh = ufs_bread (inode, 0, 0, &err))) {
-	    	ufs_warning (inode->i_sb, "empty_dir",
+		ufs_warning (inode->i_sb, "empty_dir",
 			      "bad directory (dir #%lu) - no data block",
 			      inode->i_ino);
 		return 1;
@@ -614,7 +614,7 @@
 	de1 = (struct ufs_dir_entry *) ((char *) de + SWAB16(de->d_reclen));
 	if (SWAB32(de->d_ino) != inode->i_ino || !SWAB32(de1->d_ino) || 
 	    strcmp (".", de->d_name) || strcmp ("..", de1->d_name)) {
-	    	ufs_warning (inode->i_sb, "empty_dir",
+		ufs_warning (inode->i_sb, "empty_dir",
 			      "bad directory (dir #%lu) - no `.' or `..'",
 			      inode->i_ino);
 		return 1;
@@ -625,7 +625,7 @@
 		if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) {
 			brelse (bh);
 			bh = ufs_bread (inode, offset >> sb->s_blocksize_bits, 1, &err);
-	 		if (!bh) {
+			if (!bh) {
 				ufs_error (sb, "empty_dir",
 					    "directory #%lu contains a hole at offset %lu",
 					    inode->i_ino, offset);
diff -u --recursive --new-file v2.1.119/linux/fs/ufs/super.c linux/fs/ufs/super.c
--- v2.1.119/linux/fs/ufs/super.c	Tue Jul 28 14:21:09 1998
+++ linux/fs/ufs/super.c	Tue Sep  1 10:50:11 1998
@@ -15,7 +15,7 @@
  * Gertjan van Wingerde <gertjan@cs.vu.nl>
  *
  * Clean swab support on 19970406 by
- * Francois-Rene Rideau <rideau@ens.fr>
+ * Francois-Rene Rideau <fare@tunes.org>
  *
  * 4.4BSD (FreeBSD) support added on February 1st 1998 by
  * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
@@ -26,10 +26,10 @@
  *
  * write support Daniel Pirkl <daniel.pirkl@email.cz> 1998
  * 
- 
  */
 
 #include <linux/module.h>
+#include <linux/init.h>
 
 #include <linux/kernel.h>
 #include <linux/fs.h>
@@ -130,7 +130,7 @@
 
 /*
  * Called while file system is mounted, read super block
- * and create important imtermal structures.
+ * and create important internal structures.
  */
 struct super_block * ufs_read_super (
 	struct super_block * sb,
@@ -148,7 +148,7 @@
 	unsigned flags, swab;
 	s64 tmp;
 	static unsigned offsets[] = {0, 96, 160};  /* different superblock locations */
-	            
+		    
 	UFSD(("ENTER\n"))
 	
 	uspi = NULL;
@@ -156,13 +156,12 @@
 	base = space = NULL;
 	sb->u.ufs_sb.s_ucg = NULL;
 	flags = 0;
-	swab = 0;
 	
 	/* sb->s_dev and sb->s_flags are set by our caller
 	 * data is the mystery argument to sys_mount()
 	 *
 	 * Our caller also sets s_dev, s_covered, s_rd_only, s_dirt,
- 	 *   and s_type when we return.
+	 *   and s_type when we return.
 	 */
 
 	MOD_INC_USE_COUNT;
@@ -183,7 +182,7 @@
 	i = 0;
 	uspi->s_sbbase = offsets[i];
 	
-again:	
+again:
 	set_blocksize (sb->s_dev, block_size);
 
 	/*
@@ -199,30 +198,47 @@
 	
 	/*
 	 * Check ufs magic number
+	 * This code uses goto, because it's a lesser evil than unbalanced
+	 * structure in conditional code. Brought to you by Fare' as a minimal
+	 * hack to live with Daniel's (unnecessary, IMNSHO) manual swab
+	 * optimization -- see swab.h.
 	 */
-	if (usb3->fs_magic != UFS_MAGIC) {
-		switch (le32_to_cpup(&usb3->fs_magic)) {
+#if defined(__LITTLE_ENDIAN) || defined(__BIG_ENDIAN) /* sane bytesex */
+	switch (usb3->fs_magic) {
 		case UFS_MAGIC:
-			swab = UFS_LITTLE_ENDIAN; break;
+			swab = UFS_NATIVE_ENDIAN;
+			goto magic_found;
 		case UFS_CIGAM:
-			swab = UFS_BIG_ENDIAN; break;
-		default:
-			/*
-			 * Try another super block location
-			 */
-			if (++i < sizeof(offsets)/sizeof(unsigned)) {
-				ubh_brelse2(ubh);
-				ubh = NULL;
-				uspi->s_sbbase = offsets[i];
-				goto again;
-			}
-			else {
-				printk("ufs_read_super: super block loacation not in { 0, 96, 160} or bad magic number\n");
-				goto failed;
-			}
-		}
+			swab = UFS_SWABBED_ENDIAN;
+			goto magic_found;
 	}
-	
+#else /* bytesex perversion */
+	switch (le32_to_cpup(&usb3->fs_magic)) {
+		case UFS_MAGIC:
+			swab = UFS_LITTLE_ENDIAN;
+			goto magic_found;
+		case UFS_CIGAM:
+			swab = UFS_BIG_ENDIAN;
+			goto magic_found;
+	}
+#endif
+	/*
+	 * Magic number not found -- try another super block location
+	 */
+	if (++i < sizeof(offsets)/sizeof(unsigned)) {
+		ubh_brelse2(ubh);
+		ubh = NULL;
+		uspi->s_sbbase = offsets[i];
+		goto again;
+	} else {
+		printk("ufs_read_super: "
+		       "super block location not in "
+		       "{ 0, 96, 160} "
+		       "or bad magic number\n");
+		goto failed;
+	}
+	magic_found:
+
 	/*
 	 * Check block and fragment sizes
 	 */
@@ -231,23 +247,27 @@
 	uspi->s_sbsize = SWAB32(usb1->fs_sbsize);
 
 	if (uspi->s_bsize != 4096 && uspi->s_bsize != 8192) {
-		printk("ufs_read_super: fs_bsize %u != {4096, 8192}\n", uspi->s_bsize);
+		printk("ufs_read_super: fs_bsize %u != {4096, 8192}\n",
+		       uspi->s_bsize);
 		goto failed;
 	}
 	if (uspi->s_fsize != 512 && uspi->s_fsize != 1024) {
-		printk("ufs_read_super: fs_fsize %u != {512, 1024}\n", uspi->s_fsize);
-	        goto failed;
+		printk("ufs_read_super: fs_fsize %u != {512, 1024}\n",
+		       uspi->s_fsize);
+		goto failed;
 	}
 	
 	/*
-	 * Block size is not 1024, set block_size to 512, 
-	 * free buffers and read it again
+	 * Block size is not 1024. Free buffers, set block_size and
+	 * super_block_size to superblock-declared values, and try again.
 	 */
 	if (uspi->s_fsize != block_size || uspi->s_sbsize != super_block_size) {
 		ubh_brelse2(ubh);
 		ubh = NULL;
 		uspi->s_fmask = SWAB32(usb1->fs_fmask);
 		uspi->s_fshift = SWAB32(usb1->fs_fshift);
+		block_size = uspi->s_fsize;
+		super_block_size = uspi->s_sbsize;
 		goto again;
 	}
 
@@ -255,54 +275,59 @@
 	ufs_print_super_stuff (usb1, usb2, usb3, swab);
 #endif
 	/*
-	 * Check file system type
+	 * Check file system flavor
 	 */
 	flags |= UFS_VANILLA;
 	/* XXX more consistency check */
 	UFSD(("ufs_read_super: maxsymlinklen 0x%8.8x\n", usb3->fs_u.fs_44.fs_maxsymlinklen))
 	if (usb3->fs_u.fs_44.fs_maxsymlinklen >= 0) {
 		if (usb3->fs_u.fs_44.fs_inodefmt >= UFS_44INODEFMT) {
-			UFSD(("44BSD\n"))
+			UFSD(("Flavor: 44BSD\n"))
 			flags |= UFS_44BSD;
 			sb->s_flags |= MS_RDONLY;
 		} else {
-			UFSD(("OLD\n"))
+			UFSD(("Flavor: OLD\n"))
 			sb->s_flags |= UFS_OLD;       /* 4.2BSD */
 		}
 	} else if (uspi->s_sbbase > 0) {
-		UFSD(("NEXT\n"))
+		UFSD(("Flavor: NEXT\n"))
 		flags |= UFS_NEXT;
 		sb->s_flags |= MS_RDONLY;
 	} else {
-		UFSD(("SUN\n"))
+		UFSD(("Flavor: SUN\n"))
 		flags |= UFS_SUN;
-        }
+	}
 
 	/*
-	 * Check, if file system was correctly unmounted.
+	 * Check whether file system was correctly unmounted.
 	 * If not, make it read only.
 	 */
-        if (((flags & UFS_ST_MASK) == UFS_ST_44BSD) ||
-            ((flags & UFS_ST_MASK) == UFS_ST_OLD) ||
-            ((flags & UFS_ST_MASK) == UFS_ST_NEXT) ||
-            (((flags & UFS_ST_MASK) == UFS_ST_SUN) &&
-            ufs_state(usb3) == UFS_FSOK - usb1->fs_time)) {
+	if (((flags & UFS_ST_MASK) == UFS_ST_44BSD) ||
+	    ((flags & UFS_ST_MASK) == UFS_ST_OLD) ||
+	    ((flags & UFS_ST_MASK) == UFS_ST_NEXT) ||
+	    (((flags & UFS_ST_MASK) == UFS_ST_SUN) &&
+	    ufs_state(usb3) == UFS_FSOK - usb1->fs_time)) {
 		switch(usb1->fs_clean) {
-		    case UFS_FSCLEAN:
-			UFSD(("fs is clean\n"))
-			break;
-		    case UFS_FSSTABLE:
-			UFSD(("fs is stable\n"))
-			break;
-		    case UFS_FSACTIVE:
+		case UFS_FSACTIVE:	/* 0x00 */
 			printk("ufs_read_super: fs is active\n");
 			sb->s_flags |= MS_RDONLY;
 			break;
-		    case UFS_FSBAD:
+		case UFS_FSCLEAN:	/* 0x01 */
+			UFSD(("ufs_read_super: fs is clean\n"))
+			break;
+		case UFS_FSSTABLE:
+			UFSD(("ufs_read_super: fs is stable\n"))
+			break;
+		case UFS_FSOSF1:	/* 0x03 */
+		/* XXX - is this the correct interpretation under DEC OSF/1? */
+			printk("ufs_read_super: "
+			       "fs is clean and stable (OSF/1)\n");
+			break;
+		case UFS_FSBAD:		/* 0xFF */
 			printk("ufs_read_super: fs is bad\n");
 			sb->s_flags |= MS_RDONLY;
 			break;
-		    default:
+		default:
 			printk("ufs_read_super: can't grok fs_clean 0x%x\n",
 				usb1->fs_clean);
 			sb->s_flags |= MS_RDONLY;
@@ -335,7 +360,7 @@
 	/* s_bsize already set */
 	/* s_fsize already set */
 	uspi->s_fpb = SWAB32(usb1->fs_frag);
-        uspi->s_minfree = SWAB32(usb1->fs_minfree);
+	uspi->s_minfree = SWAB32(usb1->fs_minfree);
 	uspi->s_bmask = SWAB32(usb1->fs_bmask);
 	uspi->s_fmask = SWAB32(usb1->fs_fmask);
 	uspi->s_bshift = SWAB32(usb1->fs_bshift);
@@ -345,7 +370,7 @@
 	/* s_sbsize already set */
 	uspi->s_csmask = SWAB32(usb1->fs_csmask);
 	uspi->s_csshift = SWAB32(usb1->fs_csshift);
-        uspi->s_nindir = SWAB32(usb1->fs_nindir);
+	uspi->s_nindir = SWAB32(usb1->fs_nindir);
 	uspi->s_inopb = SWAB32(usb1->fs_inopb);
 	uspi->s_nspf = SWAB32(usb1->fs_nspf);
 	uspi->s_npsect = SWAB32(usb1->fs_npsect);
@@ -360,11 +385,11 @@
 	uspi->s_ipg = SWAB32(usb1->fs_ipg);
 	uspi->s_fpg = SWAB32(usb1->fs_fpg);
 	uspi->s_cpc = SWAB32(usb2->fs_cpc);
-        ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qbmask[0];
-        ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qbmask[1];
+	((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qbmask[0];
+	((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qbmask[1];
 	uspi->s_qbmask = SWAB64(tmp);
-        ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qfmask[0];
-        ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qfmask[1];
+	((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qfmask[0];
+	((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qfmask[1];
 	uspi->s_qfmask = SWAB64(tmp);
 	uspi->s_postblformat = SWAB32(usb3->fs_postblformat);
 	uspi->s_nrpos = SWAB32(usb3->fs_nrpos);
@@ -372,7 +397,7 @@
 	uspi->s_rotbloff = SWAB32(usb3->fs_rotbloff);
 
 	/*
-	 * Compute another fraquently used values
+	 * Compute another frequently used values
 	 */
 	uspi->s_fpbmask = uspi->s_fpb - 1;
 	uspi->s_apbshift = uspi->s_bshift - 2;
@@ -382,15 +407,18 @@
 	uspi->s_2apb = 1 << uspi->s_2apbshift;
 	uspi->s_3apb = 1 << uspi->s_3apbshift;
 	uspi->s_apbmask = uspi->s_apb - 1;
- 	uspi->s_nspfshift = uspi->s_fshift - SECTOR_BITS;
+	uspi->s_nspfshift = uspi->s_fshift - UFS_SECTOR_BITS;
 	uspi->s_nspb = uspi->s_nspf << uspi->s_fpbshift;
 	uspi->s_inopf = uspi->s_inopb >> uspi->s_fpbshift;
-		
+
+	/* we could merge back s_swab and s_flags by having
+	   foo.s_flags = flags | swab; here, and #defining
+	   s_swab to s_flags & UFS_BYTESEX in swab.h */
 	sb->u.ufs_sb.s_flags = flags;
 	sb->u.ufs_sb.s_swab = swab;
 	sb->u.ufs_sb.s_rename_lock = 0;
 	sb->u.ufs_sb.s_rename_wait = NULL;
-         	                                                          
+									  
 	sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO), NULL);
 
 	/*
@@ -419,6 +447,7 @@
 	/*
 	 * Read cylinder group (we read only first fragment from block
 	 * at this time) and prepare internal data structures for cg caching.
+	 * XXX - something here fails on CDROMs from DEC!
 	 */
 	if (!(sb->u.ufs_sb.s_ucg = kmalloc (sizeof(struct buffer_head *) * uspi->s_ncg, GFP_KERNEL)))
 		goto failed;
@@ -669,7 +698,7 @@
 };
 
 
-int init_ufs_fs(void)
+__initfunc(int init_ufs_fs(void))
 {
 	return(register_filesystem(&ufs_fs_type));
 }
diff -u --recursive --new-file v2.1.119/linux/fs/ufs/swab.h linux/fs/ufs/swab.h
--- v2.1.119/linux/fs/ufs/swab.h	Tue Jul 28 14:21:09 1998
+++ linux/fs/ufs/swab.h	Tue Sep  1 10:50:11 1998
@@ -1,7 +1,7 @@
 /*
- *  linux/fs/ufs/ufs_swab.h
+ *  linux/fs/ufs/swab.h
  *
- * Copyright (C) 1997 Francois-Rene Rideau <rideau@ens.fr>
+ * Copyright (C) 1997, 1998 Francois-Rene Rideau <fare@tunes.org>
  * Copyright (C) 1998 Jakub Jelinek <jj@ultra.linux.cz>
  */
 
@@ -14,6 +14,29 @@
  *    in case there are ufs implementations that have strange bytesexes,
  *    you'll need to modify code here as well as in ufs_super.c and ufs_fs.h
  *    to support them.
+ *
+ *    WE ALSO ASSUME A REMOTELY SANE ARCHITECTURE BYTESEX.
+ *    We are not ready to confront insane bytesexual perversions where
+ *    conversion to/from little/big-endian is not an involution.
+ *    That is, we require that XeYZ_to_cpu(x) == cpu_to_XeYZ(x)
+ *
+ *    NOTE that swab macros depend on a variable (or macro) swab being in
+ *    scope and properly initialized (usually from sb->u.ufs_sb.s_swab).
+ *    Its meaning depends on whether the architecture is sane-endian or not.
+ *    For sane architectures, it's a flag taking values UFS_NATIVE_ENDIAN (0)
+ *    or UFS_SWABBED_ENDIAN (1), indicating whether to swab or not.
+ *    For pervert architectures, it's either UFS_LITTLE_ENDIAN or
+ *    UFS_BIG_ENDIAN whose meaning you'll have to guess.
+ *
+ *    It is important to keep these conventions in synch with ufs_fs.h
+ *    and super.c. Failure to do so (initializing swab to 0 both for
+ *    NATIVE_ENDIAN and LITTLE_ENDIAN) led to nasty crashes on big endian
+ *    machines reading little endian UFSes. Search for "swab =" in super.c.
+ *
+ *    I also suspect the whole UFS code to trust the on-disk structures
+ *    much too much, which might lead to losing badly when mounting
+ *    inconsistent partitions as UFS filesystems. fsck required (but of
+ *    course, no fsck.ufs has yet to be ported from BSD to Linux as of 199808).
  */
 
 #include <linux/ufs_fs.h>
@@ -28,23 +51,23 @@
 #define SWAB64(x) ufs_swab64(swab,x)
 
 /*
- * We often use swabing, when we want to increment/decrement some value, so these
- * macros might become handy and increase readability. (Daniel)
+ * We often use swabing, when we want to increment/decrement some value,
+ * so these macros might become handy and increase readability. (Daniel)
  */
-#define INC_SWAB16(x)	x=ufs_swab16_add(swab,x,1)
-#define INC_SWAB32(x)	x=ufs_swab32_add(swab,x,1)
-#define INC_SWAB64(x)	x=ufs_swab64_add(swab,x,1)
-#define DEC_SWAB16(x)	x=ufs_swab16_add(swab,x,-1)
-#define DEC_SWAB32(x)	x=ufs_swab32_add(swab,x,-1)
-#define DEC_SWAB64(x)	x=ufs_swab64_add(swab,x,-1)
-#define ADD_SWAB16(x,y)	x=ufs_swab16_add(swab,x,y)
-#define ADD_SWAB32(x,y)	x=ufs_swab32_add(swab,x,y)
-#define ADD_SWAB64(x,y)	x=ufs_swab64_add(swab,x,y)
-#define SUB_SWAB16(x,y)	x=ufs_swab16_add(swab,x,-(y))
-#define SUB_SWAB32(x,y)	x=ufs_swab32_add(swab,x,-(y))
-#define SUB_SWAB64(x,y)	x=ufs_swab64_add(swab,x,-(y))
+#define INC_SWAB16(x)	((x)=ufs_swab16_add(swab,x,1))
+#define INC_SWAB32(x)	((x)=ufs_swab32_add(swab,x,1))
+#define INC_SWAB64(x)	((x)=ufs_swab64_add(swab,x,1))
+#define DEC_SWAB16(x)	((x)=ufs_swab16_add(swab,x,-1))
+#define DEC_SWAB32(x)	((x)=ufs_swab32_add(swab,x,-1))
+#define DEC_SWAB64(x)	((x)=ufs_swab64_add(swab,x,-1))
+#define ADD_SWAB16(x,y)	((x)=ufs_swab16_add(swab,x,y))
+#define ADD_SWAB32(x,y)	((x)=ufs_swab32_add(swab,x,y))
+#define ADD_SWAB64(x,y)	((x)=ufs_swab64_add(swab,x,y))
+#define SUB_SWAB16(x,y)	((x)=ufs_swab16_add(swab,x,-(y)))
+#define SUB_SWAB32(x,y)	((x)=ufs_swab32_add(swab,x,-(y)))
+#define SUB_SWAB64(x,y)	((x)=ufs_swab64_add(swab,x,-(y)))
 
-#ifndef __PDP_ENDIAN
+#if defined(__LITTLE_ENDIAN) || defined(__BIG_ENDIAN) /* sane bytesex */
 extern __inline__ __const__ __u16 ufs_swab16(unsigned swab, __u16 x) {
 	if (swab)
 		return swab16(x);
@@ -81,21 +104,21 @@
 	else
 		return x + y;
 }
-#else /* __PDP_ENDIAN */
+#else /* bytesexual perversion -- BEWARE! Read note at top of file! */
 extern __inline__ __const__ __u16 ufs_swab16(unsigned swab, __u16 x) {
-	if (swab & UFS_LITTLE_ENDIAN)
+	if (swab == UFS_LITTLE_ENDIAN)
 		return le16_to_cpu(x);
 	else
 		return be16_to_cpu(x);
 }
 extern __inline__ __const__ __u32 ufs_swab32(unsigned swab, __u32 x) {
-	if (swab & UFS_LITTLE_ENDIAN)
+	if (swab == UFS_LITTLE_ENDIAN)
 		return le32_to_cpu(x);
 	else
 		return be32_to_cpu(x);
 }
 extern __inline__ __const__ __u64 ufs_swab64(unsigned swab, __u64 x) {
-	if (swab & UFS_LITTLE_ENDIAN)
+	if (swab == UFS_LITTLE_ENDIAN)
 		return le64_to_cpu(x);
 	else
 		return be64_to_cpu(x);
@@ -109,6 +132,6 @@
 extern __inline__ __const__ __u64 ufs_swab64_add(unsigned swab, __u64 x, __u64 y) {
 	return ufs_swab64(swab, ufs_swab64(swab, x) + y);
 }
-#endif /* __PDP_ENDIAN */
+#endif /* byte sexuality */
 
 #endif /* _UFS_SWAB_H */
diff -u --recursive --new-file v2.1.119/linux/fs/ufs/symlink.c linux/fs/ufs/symlink.c
--- v2.1.119/linux/fs/ufs/symlink.c	Tue Jul 28 14:21:09 1998
+++ linux/fs/ufs/symlink.c	Tue Sep  1 10:50:11 1998
@@ -109,7 +109,7 @@
 		i++;
 	if (copy_to_user(buffer, link, i))
 		i = -EFAULT;
- 	UPDATE_ATIME(inode);
+	UPDATE_ATIME(inode);
 	if (bh)
 		brelse (bh);
 	UFSD(("ENTER\n"))
diff -u --recursive --new-file v2.1.119/linux/fs/ufs/truncate.c linux/fs/ufs/truncate.c
--- v2.1.119/linux/fs/ufs/truncate.c	Tue Jul 28 14:21:09 1998
+++ linux/fs/ufs/truncate.c	Tue Sep  1 10:50:11 1998
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 1998
  * Daniel Pirkl <daniel.pirkl@email.cz>
- * Charles Uiversity, Faculty of Mathematics and Physics
+ * Charles University, Faculty of Mathematics and Physics
  *
  *  from
  *
@@ -68,7 +68,7 @@
 	struct ufs_sb_private_info * uspi;
 	struct buffer_head * bh;
 	u32 * p;
-   	unsigned frag1, frag2, frag3, frag4, block1, block2;
+	unsigned frag1, frag2, frag3, frag4, block1, block2;
 	unsigned frag_to_free, free_count;
 	unsigned i, j, tmp;
 	int retry;
diff -u --recursive --new-file v2.1.119/linux/fs/ufs/util.c linux/fs/ufs/util.c
--- v2.1.119/linux/fs/ufs/util.c	Tue Jul 28 14:21:09 1998
+++ linux/fs/ufs/util.c	Tue Sep  1 10:50:11 1998
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 1998
  * Daniel Pirkl <daniel.pirkl@email.cz>
- * Charles Uiversity, Faculty of Mathematics and Physics
+ * Charles University, Faculty of Mathematics and Physics
  */
  
 #include <linux/malloc.h>
@@ -194,4 +194,3 @@
 		bhno++;
 	}
 }
-		
\ No newline at end of file
diff -u --recursive --new-file v2.1.119/linux/fs/ufs/util.h linux/fs/ufs/util.h
--- v2.1.119/linux/fs/ufs/util.h	Tue Jul 28 14:21:09 1998
+++ linux/fs/ufs/util.h	Tue Sep  1 10:50:11 1998
@@ -11,7 +11,7 @@
 
 
 /*
- * some usefull marcos
+ * some useful macros
  */
 #define in_range(b,first,len)	((b)>=(first)&&(b)<(first)+(len))
 #define howmany(x,y)		(((x)+(y)-1)/(y))
@@ -28,7 +28,7 @@
 	: (usb3)->fs_u.fs_44.fs_state /* 4.4BSD way */)
 
 /*
- * namlen, it's format depends of flags
+ * namlen, its format depends of flags
  */
 #define ufs_namlen(de) _ufs_namlen_(de,flags,swab)
 static inline __u16 _ufs_namlen_(struct ufs_dir_entry * de, unsigned flags, unsigned swab) {
@@ -43,7 +43,7 @@
  * Here is how the uid is computed:
  * if the file system is 4.2BSD, get it from oldids.
  * if it has sun extension and oldids is USEEFT, get it from ui_sun.
- * if it is 4.4 or Hurd, get it from ui_44 (which is the same as ui_hurd).
+ * if it is 4.4 or Hurd, get it from ui_44 (which is the same as from ui_hurd).
  */
 #define ufs_uid(inode) _ufs_uid_(inode,flags,swab)
 static inline __u32 _ufs_uid_(struct ufs_inode * inode, unsigned flags, unsigned swab) {
@@ -72,13 +72,13 @@
 }
 
 /*
- * marcros used for retyping
+ * macros used to avoid needless retyping
  */
 #define UCPI_UBH ((struct ufs_buffer_head *)ucpi)
 #define USPI_UBH ((struct ufs_buffer_head *)uspi)
 
 /*
- * This functions manipulate with ufs_buffers
+ * These functions manipulate ufs buffers
  */
 #define ubh_bread(dev,fragment,size) _ubh_bread_(uspi,dev,fragment,size)  
 extern struct ufs_buffer_head * _ubh_bread_(struct ufs_sb_private_info *, kdev_t, unsigned, unsigned);
@@ -106,11 +106,11 @@
 
 #define ubh_get_usb_second(ubh) \
 	((struct ufs_super_block_second *)(ubh)-> \
-	bh[SECTOR_SIZE >> uspi->s_fshift]->b_data + (SECTOR_SIZE & ~uspi->s_fmask))
+	bh[UFS_SECTOR_SIZE >> uspi->s_fshift]->b_data + (UFS_SECTOR_SIZE & ~uspi->s_fmask))
 
 #define ubh_get_usb_third(ubh) \
 	((struct ufs_super_block_third *)((ubh)-> \
-	bh[SECTOR_SIZE*2 >> uspi->s_fshift]->b_data + (SECTOR_SIZE*2 & ~uspi->s_fmask)))
+	bh[UFS_SECTOR_SIZE*2 >> uspi->s_fshift]->b_data + (UFS_SECTOR_SIZE*2 & ~uspi->s_fmask)))
 
 #define ubh_get_ucg(ubh) \
 	((struct ufs_cylinder_group *)((ubh)->bh[0]->b_data))
@@ -160,7 +160,7 @@
 	SWAB32((usb)->fs_cstotal.cs_nffree) - (uspi->s_dsize * (percentreserved) / 100))
 
 /*
- * Macros for access to cylinder group array structures
+ * Macros to access cylinder group array structures
  */
 #define ubh_cg_blktot(ucpi,cylno) \
 	(*((__u32*)ubh_get_addr(UCPI_UBH, (ucpi)->c_btotoff + ((cylno) << 2))))
@@ -170,12 +170,12 @@
 	(ucpi)->c_boff + (((cylno) * uspi->s_nrpos + (rpos)) << 1 ))))
 
 /*
- * Bitmap operation
- * This functions work like classical bitmap operations. The diference 
- * is that we havn't the whole bitmap in one continuous part of memory,
- * but in a few buffers.
- * The parameter of each function is super_block, ufs_buffer_head and
- * position of the begining of the bitmap.
+ * Bitmap operations
+ * These functions work like classical bitmap operations.
+ * The difference is that we don't have the whole bitmap
+ * in one contiguous chunk of memory, but in several buffers.
+ * The parameters of each function are super_block, ufs_buffer_head and
+ * position of the beginning of the bitmap.
  */
 #define ubh_setbit(ubh,begin,bit) \
 	(*ubh_get_addr(ubh, (begin) + ((bit) >> 3)) |= (1 << ((bit) & 7)))
@@ -188,8 +188,11 @@
 
 #define ubh_isclr(ubh,begin,bit) (!ubh_isset(ubh,begin,bit))
 
-#define ubh_find_first_zero_bit(ubh,begin,size) _ubh_find_next_zero_bit_(uspi,ubh,begin,size,0)
-#define ubh_find_next_zero_bit(ubh,begin,size,offset) _ubh_find_next_zero_bit_(uspi,ubh,begin,size,offset)
+#define ubh_find_first_zero_bit(ubh,begin,size) \
+	_ubh_find_next_zero_bit_(uspi,ubh,begin,size,0)
+#define ubh_find_next_zero_bit(ubh,begin,size,offset) \
+	_ubh_find_next_zero_bit_(uspi,ubh,begin,size,offset)
+
 static inline unsigned _ubh_find_next_zero_bit_(
 	struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh,
 	unsigned begin, unsigned size, unsigned offset)
@@ -213,14 +216,17 @@
 	return (base << (uspi->s_fshift + 3)) + offset - begin;
 } 	
 
-#define ubh_isblockclear(ubh,begin,block) (!_ubh_isblockset_(uspi,ubh,begin,block))
-#define ubh_isblockset(ubh,begin,block) _ubh_isblockset_(uspi,ubh,begin,block)
+#define ubh_isblockclear(ubh,begin,block) \
+	(!_ubh_isblockset_(uspi,ubh,begin,block))
+#define ubh_isblockset(ubh,begin,block) \
+	_ubh_isblockset_(uspi,ubh,begin,block)
+
 static inline int _ubh_isblockset_(struct ufs_sb_private_info * uspi,
 	struct ufs_buffer_head * ubh, unsigned begin, unsigned block)
 {
 	switch (uspi->s_fpb) {
 	case 8:
-	    	return (*ubh_get_addr (ubh, begin + block) == 0xff);
+		return (*ubh_get_addr (ubh, begin + block) == 0xff);
 	case 4:
 		return (*ubh_get_addr (ubh, begin + (block >> 1)) == (0x0f << ((block & 0x01) << 2)));
 	case 2:
@@ -237,8 +243,8 @@
 {
 	switch (uspi->s_fpb) {
 	case 8:
-	    	*ubh_get_addr (ubh, begin + block) = 0x00;
-	    	return; 
+		*ubh_get_addr (ubh, begin + block) = 0x00;
+		return; 
 	case 4:
 		*ubh_get_addr (ubh, begin + (block >> 1)) &= ~(0x0f << ((block & 0x01) << 2));
 		return;
@@ -257,8 +263,8 @@
 {
 	switch (uspi->s_fpb) {
 	case 8:
-	    	*ubh_get_addr(ubh, begin + block) = 0xff;
-	    	return;
+		*ubh_get_addr(ubh, begin + block) = 0xff;
+		return;
 	case 4:
 		*ubh_get_addr(ubh, begin + (block >> 1)) |= (0x0f << ((block & 0x01) << 2));
 		return;
@@ -295,7 +301,8 @@
 		ADD_SWAB32(fraglist[fragsize], cnt);
 }
 
-#define ubh_scanc(ubh,begin,size,table,mask) _ubh_scanc_(uspi,ubh,begin,size,table,mask)
+#define ubh_scanc(ubh,begin,size,table,mask) \
+	_ubh_scanc_(uspi,ubh,begin,size,table,mask)
 static inline unsigned _ubh_scanc_(struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh, 
 	unsigned begin, unsigned size, unsigned char * table, unsigned char mask)
 {
diff -u --recursive --new-file v2.1.119/linux/fs/umsdos/Makefile linux/fs/umsdos/Makefile
--- v2.1.119/linux/fs/umsdos/Makefile	Tue Feb 17 13:12:48 1998
+++ linux/fs/umsdos/Makefile	Wed Sep  2 16:12:37 1998
@@ -1,11 +1,11 @@
 #
-# Makefile for the umsdos unix-like filesystem routines.
+# Makefile for the umsdos Unix-like filesystem routines.
 #
 # 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).
+# unless it's something special (not a .c file).
 #
-# Note 2! The CFLAGS definitions are now in the main makefile...
+# Note 2:  the CFLAGS definitions are now in the main makefile.
 O_TARGET := umsdos.o
 
 O_OBJS   := dir.o  file.o inode.o ioctl.o mangle.o namei.o \
diff -u --recursive --new-file v2.1.119/linux/fs/umsdos/README-WIP.txt linux/fs/umsdos/README-WIP.txt
--- v2.1.119/linux/fs/umsdos/README-WIP.txt	Wed May 20 19:10:41 1998
+++ linux/fs/umsdos/README-WIP.txt	Wed Sep  2 16:12:37 1998
@@ -1,45 +1,50 @@
 Changes by Matija Nalis (mnalis@jagor.srce.hr) on umsdos dentry fixing
 (started by Peter T. Waltenberg <peterw@karaka.chch.cri.nz>)
 
---------- WARNING --------- WARNING --------- WARNING -----------
-THIS IS TRULY EXPERIMENTAL. IT IS NOT BETA YET. PLEASE EXCUSE MY
-YELLING, BUT ANY USE OF THIS MODULES MAY VERY WELL DESTROY YOUR
+---------- WARNING --------- WARNING --------- WARNING -----------
+THIS IS TRULY EXPERIMENTAL.  IT IS NOT BETA YET.  PLEASE EXCUSE MY
+YELLING, BUT ANY USE OF THIS MODULE MAY VERY WELL DESTROY YOUR
 UMSDOS FILESYSTEM, AND MAYBE EVEN OTHER FILESYSTEMS IN USE.
 YOU'VE BEEN WARNED.
---------- WARNING --------- WARNING --------- WARNING -----------
+---------- WARNING --------- WARNING --------- WARNING -----------
 
-Current status (980428) - UMSDOS dentry-WIP-Beta 0.82-4:
+Current status (980901) - UMSDOS dentry-WIP-Beta 0.82-7:
 
 (1) pure MSDOS (no --linux-.--- EMD file):
 
-- readdir - works
-- lookup - works
-- read file - works
-
-- creat file - works
-- write file - works
-- mkdir - works
-- rmdir - QUESTIONABLE. probable problem on non-empty dirs.
++ readdir			- works
++ lookup			- works
++ read file			- works
+
++ creat file			- works
++ delete file			- works
++ write file			- works
++ rename file (same dir)	- works
++ rename file (dif. dir)	- works
++ rename dir (same dir)		- works
++ rename dir (dif. dir)		- works
++ mkdir				- works
+- rmdir 			- QUESTIONABLE. probable problem on non-empty dirs.
 
 Notes: possible very minor problems with dentry/inode/... kernel structures (very rare)
 
 
 (2) umsdos (with --linux-.--- EMD file):
 
-- readdir - works.
-- lookup -  works.
-- permissions/owners stuff - works
-- long file names - works
-- read file - works
-- switching MSDOS/UMSDOS - works?
-- switching UMSDOS/MSDOS - works?
-- pseudo root things - COMPLETELY UNTESTED
-- resolve symlink - seems to work fully now!
-- dereference symlink - seems to work fully now!
-- hard links - seems to work now
-- special files (block/char device, fifos, sockets...) - seems to work ok.
-- other ioctls - some UNTESTED
-- dangling symlink - UNTESTED !
++ readdir			- works
++ lookup 			- works
++ permissions/owners stuff	- works
++ long file names		- works
++ read file			- works
+- switching MSDOS/UMSDOS	- works?
+- switching UMSDOS/MSDOS	- works?
+- pseudo root things		- COMPLETELY UNTESTED (commented out currently!)
++ resolve symlink		- works
++ dereference symlink		- works
+- hard links			- broken again...
++ special files (block/char devices, FIFOs, sockets...)	- seems to work.
+- other ioctls			- some UNTESTED
++ dangling symlink		- works
 
 - create symlink		- seems to work both on short & long names now !
 - create hardlink		- WARNING: NOT FIXED YET!
@@ -57,65 +62,62 @@
 - rmdir 			- may work, but readdir blocks linux afterwards. to be FIXED!
 - umssyncing			- seems to work, but NEEDS MORE TESTING
 
-- CVF-FAT stuff (compressed DOS filesystem) - there is some support from
-  Frank Gockel <gockel@sent13.uni-duisburg.de> to use it even under
-  umsdosfs. But I have no way of testing it -- please let me know if there
-  are problems that are specific to umsdos (eg. it works under msdosfs, but
-  not under umsdosfs)
++ CVF-FAT stuff (compressed DOS filesystem) - there is some support from Frank
+  Gockel <gockel@sent13.uni-duisburg.de> to use it even under umsdosfs, but I
+  have no way of testing it -- please let me know if there are problems specific
+  to umsdos (for instance, it works under msdosfs, but not under umsdosfs).
 
 
 
-Notes: moderate dentry/inode kernel structures trashing. Probably some other
-kernel structures compromised. Have SysRq support compiled in, and use
-Sync/Emergency-remount-RO. And if you don't try mounting read/write -
-you should have no big problems...
-
-Notes2: kernel structures trashing seems to be _MUCH_ lower if no
-symlinks/hardlink present. (hardlinks seem to be biggest problem)
-
-Notes3: Notes2 is probably somewhat outdated now that hardlink/symlink stuff
-is supposed to be fixed enough to work, but I haven't got the time to test
-it.
-
-Note5: rmdir(2) fails with EBUSY - sdir->i_count > 1 (like 7 ??). It must be
-some error with dir->i_count++, or something related to iput() ? See if
-number changes if we access the dir in different ways..
-
-Note6: there is problem with unmounting umsdosfs, it seems to stay
-registered or something. Remounting same device on any mount point with
-different fstype (like msdos or vfat) ignores fstype and umsdosfs kicks back
-in. 
-
-Note7: also we screwed umount(2)-ing the fs at times (EBUSY), by removing
-all those iput/dput's. When rest of code is fixed, we'll put them back at
-(hopefully) correct places.
+Notes:  there is moderate trashing of dentry/inode kernel structures.  Probably
+some other kernel structures are compromised.  You should have SysRq support
+compiled in, and use Sync/Emergency-remount-RO.  If you don't try mounting
+read/write, you should have no big problems. When most things begin to work,
+I'll get to finding/fixing those inode/dentry ?_count leakages.
+
+Note 4:  rmdir(2) fails with EBUSY - sdir->i_count > 1 (like 7 ??).  It may be
+some error with dir->i_count++, or something related to iput().  See if
+number changes if we access the directory in different ways.
+
+Note 5:  there is a problem with unmounting umsdosfs.  It seems to stay
+registered or something.  Remounting the same device on any mount point with a
+different fstype (such as msdos or vfat) ignores the new fstype and umsdosfs
+kicks back in. Should be fixed in 0.82-6 (at least, with nothing in between)!
+Much of inode/dentry corruption is fixed in 0.82-7, especially when mounting
+read-only.
+
+Note 6:  also we screwed umount(2)-ing the fs at times (EBUSY), by missing
+some of those iput/dput's. When most of the rest of the code is fixed, we'll
+put them back at the correct places (hopefully). Also much better in 0.82-7.
 
 ------------------------------------------------------------------------------
 
 Some general notes:
 
-There is great amount of kernel log messages. Use SysRq log-level 5 to turn
-most of them off, or 4 to turn all but really fatal ones off. Probably good
-idea to kill klogd/syslogd so it will only go to console.
+There is a great amount of kernel log messages.  Use SysRq log-level 5 to turn
+most of them off, or 4 to turn all but really fatal ones off.  Probably good
+idea to kill klogd/syslogd so it will only go to console. You can also
+comment out include/linux/umsdos_fs.h definition of UMS_DEBUG to get rid of
+most debugging messages. Please don't turn it off without good reason.
 
-It should work enough to test it, even enough to give you few chances to
+It should work enough to test it, even enough to give you a few chances to
 umount/rmmod module, recompile it, and reinsert it again (but probably
-screaming about still used inodes on device and stuff). This is first on
+screaming about still used inodes on device and stuff).  This is first on
 my list of things to get fixed, as it would greatly improve speed of
 compile/test/reboot/set_environment/recompile cycle by removing
-'reboot/set_environment' component that now occures every few cycles.
+'reboot/set_environment' component that now occurs every few cycles.
 
-But I need some help from someone knowing about dentries/inodes use more
-than I. If you can help, please contact me... I'm mostly worried about
+I need some help from someone who knows more than I about the use of dentries
+and inodes.  If you can help, please contact me.  I'm mostly worried about
 iget/iput and dget/dput, and deallocating temporary dentries we create.
-should we destroy temp dentries ? using d_invalidate ? using d_drop ? just
-dput them ?
+Should we destroy temp dentries? using d_invalidate? using d_drop? just
+dput them?
 
-I'm unfortunatelly somewhat out of time to read linux-kernel, but I do check
-for any messages having UMSDOS in subject, and read them. I might miss it in
-all that volume, though. I should reply to any direct Email in few days. If
-I don't - probably I never got your message. You can try mnalis@open.hr or
-mnalis@voyager.hr; however mnalis@jagor.srce.hr is preferable one.
+I'm unfortunately somewhat out of time to read linux-kernel, but I do check
+for messages having "UMSDOS" in the subject, and read them.  I might miss
+some in all that volume, though.  I should reply to any direct e-mail in few
+days.  If I don't, probably I never got your message.  You can try
+mnalis@voyager.hr; however mnalis@jagor.srce.hr is preferable.
 
 
 ------------------------------------------------------------------------------
@@ -126,34 +128,33 @@
 - iput: device 00:00 inode 318 still has aliases! problem. Check in iput()
   for device 0,0. Probably null pointer passed around when it shouldn't be ?
 - dput/iput problem...
-- what about .dotfiles ? working ? multiple dots ? etc....
-- fix stuff like dir->i_count++ to atomic_inc(&dir->i_count) and simular?
+- What about .dotfiles? Are they working? How about multiple dots?
+- fix stuff like dir->i_count++ to atomic_inc(&dir->i_count) and similar?
+
+- should check what happen when multiple UMSDOSFS are mounted
 
 - chase down all "FIXME", "DELME", "CNT", check_dentry, check_inode, kill_dentry
   and fix it properly.
 
-- umsdos_create_any - calling msdos_create will create dentry for shor name. Hmmmm..?
-- kill_dentry - put it where is needed. Also dput() at needed places.
-
-- when should dput()/iput() be used ?!!
+- umsdos_create_any - calling msdos_create will create dentry for short name. Hmmmm..?
 
 - what is dir->i_count++ ? locking directory ? should this be lock_parent or
 something ?
 
-- i_binary=2 is for CVF (compressed filesystem).
++ i_binary=2 is for CVF (compressed filesystem).
 
 - SECURITY WARNING: short dentries should be invalidated, or they could be
   accessed instead of proper long names.
 
-- I've put many check_dentry() calls to trace down problems. those should be
-  removed in final version.
+- I've put many check_dentry_path(), check_inode() calls to trace down
+  problems. those should be removed in final version.
 
-- iput()s with "FIXME?" comment are uncomented and probably ok. Those with
-  "FIXME??" should be tested but prolly work. Commented iput()s with
+- iput()s with a "FIXME?" comment are uncommented and probably OK. Those with
+  "FIXME??" should be tested but probably work. Commented iput()s with
   any "FIXME" comments should probably be uncommented and tested. At some
   places we may need dput() instead of iput(), but that should be checked.
 
-- as for iput() : (my only pointer so far. anyone else ?)
++ as for iput(): (my only pointer so far. anyone else?)
 
 >development I only know about iput. All functions that get an inode as
 >argument and don't return it have to call iput on it before exit, i.e. when
diff -u --recursive --new-file v2.1.119/linux/fs/umsdos/check.c linux/fs/umsdos/check.c
--- v2.1.119/linux/fs/umsdos/check.c	Wed May 20 19:10:41 1998
+++ linux/fs/umsdos/check.c	Wed Sep  2 16:12:37 1998
@@ -1,7 +1,5 @@
 /*
  * linux/fs/umsdos/check.c
- *
- *
  */
 
 #include <linux/signal.h>
@@ -51,6 +49,6 @@
 			}
 		}
 		if (err)
-			printk ("\nErreur MM %d\n", err);
+			printk ("\nError MM %d\n", err);
 	}
 }
diff -u --recursive --new-file v2.1.119/linux/fs/umsdos/dir.c linux/fs/umsdos/dir.c
--- v2.1.119/linux/fs/umsdos/dir.c	Wed Aug 26 11:37:43 1998
+++ linux/fs/umsdos/dir.c	Wed Sep  2 16:12:37 1998
@@ -19,9 +19,6 @@
 
 #include <asm/uaccess.h>
 
-#define PRINTK(x)
-#define Printk(x) printk x
-
 #define UMSDOS_SPECIAL_DIRFPOS	3
 extern struct inode *pseudo_root;
 
@@ -30,55 +27,56 @@
  * I've retained this to facilitate the lookup of some of the hard-wired files/directories UMSDOS
  * uses. It's easier to do once than hack all the other instances. Probably safer as well
  */
+   
+/* 
+ * d_dir is directory to search for file, name&len define the file.
+ * compat_umsdos_real_lookup returns dentry pointing to wanted file,
+ * or NULL if not found. Calling code is respondible to call fin_dentry (result) 
+ */
 
-/* FIXME: it returns inode with i_count of 0. this should be redesigned to return dentry instead,
-   and correct dentry (with correct d_parent) */
-
-int compat_umsdos_real_lookup (struct inode *dir, const char *name, int len, struct inode **inode)
+struct dentry *compat_umsdos_real_lookup (struct dentry *d_dir, const char *name, int len)
 {
 	int rv;
 	struct dentry *dentry;
-	unsigned long ino;
 
-	Printk ((KERN_DEBUG "compat_umsdos_real_lookup !!CNTx!!: start\n"));
-	check_inode (dir);
-	dentry = creat_dentry (name, len, NULL, NULL);
-	rv = umsdos_real_lookup (dir, dentry);
-	iput (dir);	/* should be here, because umsdos_real_lookup does inc_count(dir) */
+	PRINTK ((KERN_DEBUG "compat_umsdos_real_lookup !!CNTx!!: start\n"));
+	
+	check_dentry_path (d_dir, "compat_umsdos_real_lookup B4 dir");
+	dentry = creat_dentry (name, len, NULL, d_dir);
+
+/*	check_dentry_path (dentry, "compat_umsdos_real_lookup B4");*/
+
+	rv = umsdos_real_lookup (d_dir->d_inode, dentry);
+	check_dentry_path (dentry, "compat_umsdos_real_lookup END");
 
 	if (rv) {
-		Printk ((KERN_WARNING "compat_umsdos_real_lookup failed with %d\n", rv));
-		return rv;
+		printk (KERN_WARNING "compat_umsdos_real_lookup failed with %d\n", rv);
+		return NULL;
 	}
 
-	if (!inode) {
-		Printk ((KERN_ERR "inode should be set here. Arrgh! segfaulting...\n"));
-	}
-		
-	ino = dentry->d_inode->i_ino;
-	*inode = dentry->d_inode;
 
-	dput (dentry);	/* we are done with it: FIXME: does this work /mn/ ? */
-	
-	check_dentry (dentry);
-	check_inode (dir);
+	PRINTK ((KERN_DEBUG "compat_umsdos_real_lookup !!CNTx!!: end\n"));
 
-	Printk ((KERN_DEBUG "compat_umsdos_real_lookup !!CNTx!!: end\n"));
+	if (dentry->d_inode) return dentry;	/* all OK */
 	
-	return rv;
+	/* otherwise, we have a negative dentry. return NULL */
+	Printk ((KERN_DEBUG "compat_umsdos_real_lookup: negative dentry - file not found\n"));
+	fin_dentry (dentry);
+	return NULL;
 }
 
 
 int compat_msdos_create (struct inode *dir, const char *name, int len, int mode, struct inode **inode)
 {
 	int rv;
-	struct dentry *dentry;
+	struct dentry *dentry, *d_dir;
 
 	check_inode (dir);
-	dentry = creat_dentry (name, len, NULL, NULL);
-	check_dentry (dentry);
+	d_dir = geti_dentry (dir);
+	dentry = creat_dentry (name, len, NULL, d_dir);
+	check_dentry_path (dentry, "compat_msdos_create START");
 	rv = msdos_create (dir, dentry, mode);
-	check_dentry (dentry);
+	check_dentry_path (dentry, "compat_msdos_create END");
 	if (inode != NULL)
 		*inode = dentry->d_inode;
 
@@ -91,7 +89,7 @@
  * So  grep *  doesn't complain in the presence of directories.
  */
  
-int UMSDOS_dir_read (struct file *filp, char *buff, size_t size, loff_t *count)
+int dummy_dir_read (struct file *filp, char *buff, size_t size, loff_t *count)
 {
 	return -EISDIR;
 }
@@ -120,7 +118,7 @@
 	struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf;
 
 	if (d->count == 0) {
-		PRINTK ((KERN_DEBUG "dir_once :%.*s: offset %Ld\n", dentry->d_len, dentry->d_name, offset));
+		PRINTK ((KERN_DEBUG "dir_once :%.*s: offset %Ld\n", len, name, offset));
 		ret = d->filldir (d->dirbuf, name, len, offset, ino);
 		d->stop = ret < 0;
 		d->count = 1;
@@ -139,8 +137,7 @@
  * inode. See umsdos_locate_ancestor() below.
  */
  
-static int umsdos_readdir_x (
-				    struct inode *dir,	/* Point to a description of the super block */
+static int umsdos_readdir_x (	    struct inode *dir,	/* Point to a description of the super block */
 				    struct file *filp,	/* Point to a directory which is read */
 				    void *dirbuf,	/* Will hold count directory entry */
 							/* but filled by the filldir function */
@@ -150,6 +147,7 @@
 				    filldir_t filldir)
 {
 	int ret = 0;
+	struct dentry *old_dent;
 
 	umsdos_startlookup (dir);
 	if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS
@@ -179,7 +177,7 @@
 	                
 	
 		/* #Specification: readdir / . and ..
-		 * The msdos filesystem manage the . and .. entry properly
+		 * The msdos filesystem manages the . and .. entry properly
 		 * so the EMD file won't hold any info about it.
 		 * 
 		 * In readdir, we assume that for the root directory
@@ -232,23 +230,22 @@
 		if (u_entry != NULL)
 			u_entry->flags = 0;
 	} else {
-		struct inode *emd_dir;
-
 		Printk (("umsdos_readdir_x: normal file /mn/?\n"));
-		emd_dir = umsdos_emd_dir_lookup (dir, 0);
-		if (emd_dir != NULL) {
+		old_dent = filp->f_dentry;	/* save dentry of directory */
+
+		if (fix_emd_filp (filp) == 0) {
 			off_t start_fpos = filp->f_pos;
 
-			Printk (("umsdos_readdir_x: emd_dir->i_ino=%ld\n", emd_dir->i_ino));
+			Printk (("umsdos_readdir_x: emd_dir->i_ino=%ld\n", filp->f_dentry->d_inode->i_ino));
 			if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS + 1)
 				filp->f_pos = 0;
-			Printk (("f_pos %Ld i_size %ld\n", filp->f_pos, emd_dir->i_size));
+			Printk (("f_pos %Ld i_size %ld\n", filp->f_pos, filp->f_dentry->d_inode->i_size));
 			ret = 0;
-			while (filp->f_pos < emd_dir->i_size) {
+			while (filp->f_pos < filp->f_dentry->d_inode->i_size) {
 				struct umsdos_dirent entry;
 				off_t cur_f_pos = filp->f_pos;
 
-				if (umsdos_emd_dir_readentry (emd_dir, filp, &entry) != 0) {
+				if (umsdos_emd_dir_readentry (filp, &entry) != 0) {
 					ret = -EIO;
 					break;
 				} else if (entry.name_len != 0) {
@@ -271,55 +268,50 @@
 					 * So the easy way is used!
 					 */
 					struct umsdos_info info;
-					struct inode *inode;
-
-					int lret;
+					struct dentry *d_dir, *dret;
 
 					umsdos_parse (entry.name, entry.name_len, &info);
 					info.f_pos = cur_f_pos;
 					umsdos_manglename (&info);
-					lret = compat_umsdos_real_lookup (dir, info.fake.fname, info.fake.len, &inode);
-					Printk (("Cherche inode de %s lret %d flags %d\n", info.fake.fname, lret, entry.flags));
-					if (lret == 0
-					  && (entry.flags & UMSDOS_HLINK)
-					    && follow_hlink) {
-						struct inode *rinode;
-
-						Printk ((KERN_DEBUG "umsdos_hlink2inode now\n"));
-						lret = umsdos_hlink2inode (inode, &rinode);
-						inode = rinode;
+					d_dir = geti_dentry (dir);
+					dret = compat_umsdos_real_lookup (d_dir, info.fake.fname, info.fake.len);
+
+					Printk (("Looking for inode of %s dret %p flags %d\n", info.fake.fname, dret, entry.flags));
+					if (dret && !IS_ERR(dret)
+					   && (entry.flags & UMSDOS_HLINK)
+					   && follow_hlink) {
+						dret = umsdos_solve_hlink (dret);
 					}
-					if (lret == 0) {
-						/* #Specification: pseudo root / reading real root
+					
+					if (dret && !IS_ERR(dret)) {
+						/* #Specification:  pseudo root / reading real root
 						 * The pseudo root (/linux) is logically
-						 * erased from the real root. This mean that
+						 * erased from the real root.  This means that
 						 * ls /DOS, won't show "linux". This avoids
-						 * infinite recursion /DOS/linux/DOS/linux while
+						 * infinite recursion (/DOS/linux/DOS/linux/...) while
 						 * walking the file system.
 						 */
-						if (inode != pseudo_root
-						    && (internal_read
-							|| !(entry.flags & UMSDOS_HIDDEN))) {
+						if (dret->d_inode != pseudo_root
+						    && (internal_read || !(entry.flags & UMSDOS_HIDDEN))) {
 							Printk ((KERN_DEBUG "filldir now\n"));
-							if (filldir (dirbuf, entry.name, entry.name_len, cur_f_pos, inode->i_ino) < 0) {
+							if (filldir (dirbuf, entry.name, entry.name_len, cur_f_pos, dret->d_inode->i_ino) < 0) {
 								filp->f_pos = cur_f_pos;
 							}
-							Printk (("Trouve ino %ld ", inode->i_ino));
+							Printk (("Found ino %ld ", dret->d_inode->i_ino));
 							if (u_entry != NULL)
 								*u_entry = entry;
-							iput (inode); /* FIXME? */
+							fin_dentry (dret);
 							break;
 						}
-						Printk ((KERN_DEBUG " dir.c:Putting inode %lu with i_count=%d\n", inode->i_ino, inode->i_count));
-						iput (inode); /* FIXME? */
+						fin_dentry (dret);
 					} else {
-						/* #Specification: umsdos / readdir / not in MSDOS
+						/* #Specification:  umsdos / readdir / not in MSDOS
 						 * During a readdir operation, if the file is not
-						 * in the MSDOS directory anymore, the entry is
+						 * in the MS-DOS directory any more, the entry is
 						 * removed from the EMD file silently.
 						 */
 						Printk (("'Silently' removing EMD for file\n"));
-						ret = umsdos_writeentry (dir, emd_dir, &info, 1);
+						ret = umsdos_writeentry (dir, filp->f_dentry->d_inode, &info, 1);
 						if (ret != 0) {
 							break;
 						}
@@ -334,25 +326,26 @@
 			 */
 			if (filp->f_pos == 0)
 				filp->f_pos = start_fpos;
-			Printk ((KERN_DEBUG " dir.c:Putting emd_dir %lu with i_count=%d\n", emd_dir->i_ino, emd_dir->i_count));
-			iput (emd_dir); /* FIXME? */
+			Printk ((KERN_DEBUG "dir.c:  putting emd_dir %lu with i_count=%d\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_count));
+
+			fin_dentry (filp->f_dentry);
+			filp->f_dentry = old_dent;	/* restore dentry of directory */
 		}
 	}
 	umsdos_endlookup (dir);
 	
-	Printk (("read dir %p pos %Ld ret %d\n", dir, filp->f_pos, ret));
+	Printk ((KERN_DEBUG "read dir %p pos %Ld ret %d\n", dir, filp->f_pos, ret));
 	return ret;
 }
 
 
 /*
- * Read count directory entries from directory filp
+ * Read count directory entries from directory filp.
  * Return a negative value from linux/errno.h.
- * Return 0 or positive if successful
+ * Return 0 or positive if successful.
  */
  
-static int UMSDOS_readdir (
-				  struct file *filp,	/* Point to a directory which is read */
+static int UMSDOS_readdir (	  struct file *filp,	/* Point to a directory which is read. */
 				  void *dirbuf,		/* Will hold directory entries  */
 				  filldir_t filldir)
 {
@@ -370,46 +363,46 @@
 		struct umsdos_dirent entry;
 
 		bufk.count = 0;
-		Printk (("UMSDOS_readdir: calling _x (%p,%p,%p,%d,%p,%d,%p)\n", dir, filp, &bufk, 0, &entry, 1, umsdos_dir_once));
+		PRINTK (("UMSDOS_readdir: calling _x (%p,%p,%p,%d,%p,%d,%p)\n", dir, filp, &bufk, 0, &entry, 1, umsdos_dir_once));
 		ret = umsdos_readdir_x (dir, filp, &bufk, 0, &entry, 1, umsdos_dir_once);
 		if (bufk.count == 0)
 			break;
 		count += bufk.count;
 	}
+	/* FIXME: do we first need to deallocate old dentry ? look/check. see also all other instances of fix_emd_filp */
 	Printk (("UMSDOS_readdir out %d count %d pos %Ld\n", ret, count, filp->f_pos));
 	return count ? : ret;
 }
 
 
 /*
- * Complete the inode content with info from the EMD file
+ * Complete the inode content with info from the EMD file.
  */
 
-void umsdos_lookup_patch (
-				 struct inode *dir,
+void umsdos_lookup_patch (	 struct inode *dir,
 				 struct inode *inode,
 				 struct umsdos_dirent *entry,
 				 off_t emd_pos)
 {
 	/*
-	 * This function modify the state of a dir inode. It decides
-	 * if the dir is a umsdos dir or a dos dir. This is done
+	 * This function modifies the state of a dir inode.  It decides
+	 * whether the dir is a UMSDOS or DOS directory.  This is done
 	 * deeper in umsdos_patch_inode() called at the end of this function.
 	 * 
-	 * umsdos_patch_inode() may block because it is doing disk access.
+	 * Because it is does disk access, umsdos_patch_inode() may block.
 	 * At the same time, another process may get here to initialise
-	 * the same dir inode. There is 3 cases.
+	 * the same directory inode. There are three cases.
 	 * 
-	 * 1-The inode is already initialised. We do nothing.
-	 * 2-The inode is not initialised. We lock access and do it.
-	 * 3-Like 2 but another process has lock the inode, so we try
-	 * to lock it and right after check if initialisation is still
-	 * needed.
+	 * 1) The inode is already initialised.  We do nothing.
+	 * 2) The inode is not initialised.  We lock access and do it.
+	 * 3) Like 2 but another process has locked the inode, so we try
+	 * to lock it and check right afterward check whether
+	 * initialisation is still needed.
 	 * 
 	 * 
-	 * Thanks to the mem option of the kernel command line, it was
+	 * Thanks to the "mem" option of the kernel command line, it was
 	 * possible to consistently reproduce this problem by limiting
-	 * my mem to 4 meg and running X.
+	 * my memory to 4 MB and running X.
 	 */
 	/*
 	 * Do this only if the inode is freshly read, because we will lose
@@ -437,10 +430,10 @@
 			 * mtime will be meaningful. We do this only for regular
 			 * file.
 			 * 
-			 * We don't rely on MSDOS for mtime for directory because
-			 * the MSDOS directory date is creation time (strange
-			 * MSDOS behavior) which fit nowhere in the three UNIX
-			 * time stamp.
+			 * We don't rely on MS-DOS for mtime for directories
+			 * because the MS-DOS date on a directory is its
+			 * creation time (strange MSDOS behavior) which
+			 * corresponds to none of the three Unix time stamps.
 			 */
 			if (S_ISREG (entry->mode))
 				entry->mtime = inode->i_mtime;
@@ -452,35 +445,35 @@
 			inode->i_uid = entry->uid;
 			inode->i_gid = entry->gid;
 			/* #Specification: umsdos / conversion mode
-			 * The msdos fs can do some inline conversion
-			 * of the data of a file. It can translate
-			 * silently from MsDOS text file format to Unix
-			 * one (crlf -> lf) while reading, and the reverse
+			 * The msdos filesystem can do some inline conversion
+			 * of the data of a file.  It can translate silently
+			 * from the MS-DOS text file format to the Unix one
+			 * (CRLF -> LF) while reading, and the reverse
 			 * while writing. This is activated using the mount
 			 * option conv=....
 			 * 
-			 * This is not useful for Linux file in promoted
-			 * directory. It can even be harmful. For this
+			 * This is not useful for Linux files in a promoted
+			 * directory.  It can even be harmful.  For this
 			 * reason, the binary (no conversion) mode is
 			 * always activated.
 			 */
 			/* #Specification: umsdos / conversion mode / todo
 			 * A flag could be added to file and directories
 			 * forcing an automatic conversion mode (as
-			 * done with the msdos fs).
+			 * done with the msdos filesystem).
 			 * 
 			 * This flag could be setup on a directory basis
-			 * (instead of file) and all file in it would
-			 * logically inherited. If the conversion mode
+			 * (instead of file) and all files in it would
+			 * logically inherit it.  If the conversion mode
 			 * is active (conv=) then the i_binary flag would
 			 * be left untouched in those directories.
 			 * 
-			 * It was proposed that the sticky bit was used
-			 * to set this. The problem is that new file would
-			 * be written incorrectly. The other problem is that
+			 * It was proposed that the sticky bit be used to set
+			 * this.  A problem with that is that new files would
+			 * be written incorrectly.  The other problem is that
 			 * the sticky bit has a meaning for directories. So
 			 * another bit should be used (there is some space
-			 * in the EMD file for it) and a special utilities
+			 * in the EMD file for it) and a special utility
 			 * would be used to assign the flag to a directory).
 			 * I don't think it is useful to assign this flag
 			 * on a single file.
@@ -488,18 +481,18 @@
 
 			MSDOS_I (inode)->i_binary = 1;
 			/* #Specification: umsdos / i_nlink
-			 * The nlink field of an inode is maintain by the MSDOS file system
-			 * for directory and by UMSDOS for other file. The logic is that
+			 * The nlink field of an inode is maintained by the MSDOS file system
+			 * for directory and by UMSDOS for other files.  The logic is that
 			 * MSDOS is already figuring out what to do for directories and
-			 * does nothing for other files. For MSDOS, there are no hard link
-			 * so all file carry nlink==1. UMSDOS use some info in the
+			 * does nothing for other files.  For MSDOS, there are no hard links
+			 * so all file carry nlink==1.  UMSDOS use some info in the
 			 * EMD file to plug the correct value.
 			 */
 			if (!S_ISDIR (entry->mode)) {
 				if (entry->nlink > 0) {
 					inode->i_nlink = entry->nlink;
 				} else {
-					printk (KERN_ERR "UMSDOS: lookup_patch entry->nlink < 1 ???\n");
+					printk (KERN_ERR "UMSDOS:  lookup_patch entry->nlink < 1 ???\n");
 				}
 			}
 			umsdos_patch_inode (inode, dir, emd_pos);
@@ -507,7 +500,7 @@
 		if (S_ISDIR (inode->i_mode))
 			umsdos_unlockcreate (inode);
 		if (inode->u.umsdos_i.i_emd_owner == 0)
-			printk (KERN_WARNING "emd_owner still 0 ???\n");
+			printk (KERN_WARNING "UMSDOS:  emd_owner still 0?\n");
 	}
 }
 
@@ -523,8 +516,7 @@
  * Just to record the offset of one entry.
  */
 
-static int umsdos_filldir_k (
-				    void *buf,
+static int umsdos_filldir_k (	    void *buf,
 				    const char *name,
 				    int len,
 				    off_t offset,
@@ -543,8 +535,7 @@
 	ino_t search_ino;
 };
 
-static int umsdos_dir_search (
-				     void *buf,
+static int umsdos_dir_search (	     void *buf,
 				     const char *name,
 				     int len,
 				     off_t offset,
@@ -569,15 +560,14 @@
  * Locate entry of an inode in a directory.
  * Return 0 or a negative error code.
  * 
- * Normally, this function must succeed. It means a strange corruption
+ * Normally, this function must succeed.  It means a strange corruption
  * in the file system if not.
  */
 
-int umsdos_inode2entry (
-			       struct inode *dir,
+int umsdos_inode2entry (       struct inode *dir,
 			       struct inode *inode,
-			       struct umsdos_dirent *entry)
-{				/* Will hold the entry */
+			       struct umsdos_dirent *entry)	/* Will hold the entry */
+{
 	int ret = -ENOENT;
 
 	if (pseudo_root && inode == pseudo_root) {
@@ -591,14 +581,14 @@
 	} else {
 		struct inode *emddir = umsdos_emd_dir_lookup (dir, 0);
 
-		iput (emddir); /* FIXME? */
+		/* iput (emddir); / * FIXME? */
 		if (emddir == NULL) {
-			/* This is a DOS directory */
+			/* This is a DOS directory. */
 			struct UMSDOS_DIR_SEARCH bufk;
 			struct file filp;
 			struct dentry *i2e;
 			
-			i2e = creat_dentry ("i2e.nul", 7, dir, NULL);
+			i2e = creat_dentry ("@i2e.nul@", 9, dir, NULL);
 
 			fill_new_filp (&filp, i2e);
 
@@ -619,7 +609,7 @@
 			struct file filp;
 			struct dentry *i2e;
 
-			i2e = creat_dentry ("i2e.nn", 6, dir, NULL);
+			i2e = creat_dentry ("@i2e.nn@", 8, dir, NULL);
 			fill_new_filp (&filp, i2e);
 
 			filp.f_reada = 1;
@@ -627,14 +617,15 @@
 			Printk ((KERN_ERR "umsdos_inode2entry skip...: WARNING: Known filp problem. segfaulting :) fixed ?/mn/\n"));
 			while (1) {
 				struct UMSDOS_DIRENT_K bufk;
+				struct dentry *old_dent;
 
-				if (umsdos_readdir_x (dir, &filp, &bufk
-				   ,1, entry, 0, umsdos_filldir_k) < 0) {
-					printk ("UMSDOS: can't locate inode %ld in EMD file???\n"
-						,inode->i_ino);
+				old_dent = filp.f_dentry;
+				if (umsdos_readdir_x (dir, &filp, &bufk, 1, entry, 0, umsdos_filldir_k) < 0) {
+					printk ("UMSDOS: can't locate inode %ld in EMD file???\n", inode->i_ino);
 					break;
 				} else if (bufk.ino == inode->i_ino) {
 					ret = 0;
+					filp.f_dentry = old_dent;
 					umsdos_lookup_patch (dir, inode, entry, bufk.f_pos);
 					break;
 				}
@@ -650,21 +641,24 @@
  * Return 0 or a negative error code.
  */
 
-static int umsdos_locate_ancestor (
-					  struct inode *dir,
+static int umsdos_locate_ancestor (	  struct inode *dir,
 					  struct inode **result,
 					  struct umsdos_dirent *entry)
 {
-	int ret;
+	int ret=-99;
+	struct dentry *dret, *d_dir = creat_dentry ("@d_dir2@", 7, dir, NULL);
 
 	umsdos_patch_inode (dir, NULL, 0);
 	/* FIXME */
-	ret = compat_umsdos_real_lookup (dir, "..", 2, result);
-	Printk (("result %d %p ", ret, *result));
-	if (ret == 0) {
-		struct inode *adir = *result;
+	dret = compat_umsdos_real_lookup (d_dir, "..", 2);
+	Printk (("result %p %p ", dret, *result));
+	if (dret) {
+		struct inode *adir;
+		*result = dret->d_inode;
+		adir = *result;
 
 		ret = umsdos_inode2entry (adir, dir, entry);
+		fin_dentry (dret);
 	}
 	Printk (("\n"));
 	return ret;
@@ -678,8 +672,7 @@
  * It uses the same strategy as the standard getcwd().
  */
 
-int umsdos_locate_path (
-			       struct inode *inode,
+int umsdos_locate_path (       struct inode *inode,
 			       char *path)
 {
 	int ret = 0;
@@ -733,7 +726,7 @@
 		kfree (bpath);
 	}
 	Printk (("\n"));
-	iput (dir); /* FIXME?? */
+	/* iput (dir); / * FIXME?? */
 	return ret;
 }
 
@@ -742,8 +735,7 @@
  * Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
  */
 
-int umsdos_is_pseudodos (
-				struct inode *dir,
+int umsdos_is_pseudodos (	struct inode *dir,
 				struct dentry *dentry)
 {
 	/* #Specification: pseudo root / DOS hard coded
@@ -763,28 +755,33 @@
 
 
 /*
- * Check if a file exist in the current directory.
- * Return 0 if ok, negative error code if not (ex: -ENOENT).
+ * Check if a file exists in the current directory.
+ * Return 0 if OK, negative error code if not (ex: -ENOENT).
+ *
+ * fills dentry->d_inode with found inode, and increments its count.
+ * if not found, return -ENOENT.
  */
 
-int umsdos_lookup_x (
-			    struct inode *dir,
+int umsdos_lookup_x (	    struct inode *dir,
 			    struct dentry *dentry,
-			    int nopseudo)
-{				/* Don't care about pseudo root mode */
+			    int nopseudo)	/* Don't care about pseudo root mode */
+{				
 	int ret = -ENOENT;
 	struct inode *root_inode;
 	int len = dentry->d_name.len;
 	const char *name = dentry->d_name.name;
+	
 
-	PRINTK ((KERN_DEBUG "umsdos_lookup_x: /mn/ name=%.*s, dir=%lu (i_count=%d), d_parent=%p\n", (int) dentry->d_name.len, dentry->d_name.name, dir->i_ino, dir->i_count, dentry->d_parent));		/* FIXME /mn/ debug only */
+#if UMS_DEBUG
+	Printk ((KERN_DEBUG "umsdos_lookup_x: /mn/ name=%.*s, dir=%lu (i_count=%d), d_parent=%p\n", (int) dentry->d_name.len, dentry->d_name.name, dir->i_ino, dir->i_count, dentry->d_parent));		/* FIXME /mn/ debug only */
 	if (dentry->d_parent)
-		PRINTK ((KERN_DEBUG "   d_parent is %.*s\n", (int) dentry->d_parent->d_name.len, dentry->d_parent->d_name.name));	/* FIXME : delme /mn/ */
+		Printk ((KERN_DEBUG "   d_parent is %.*s\n", (int) dentry->d_parent->d_name.len, dentry->d_parent->d_name.name));	/* FIXME : delme /mn/ */
+#endif
 
 	root_inode = iget (dir->i_sb, UMSDOS_ROOT_INO);
-	Printk ((KERN_ERR "umsdos_lookup_x (CNT!): entering root_count=%d, dir %lu _count=%d\n", root_inode->i_count, dir->i_ino, dir->i_count));	/* FIXME: DEBUG, DELME */
+	Printk ((KERN_ERR "umsdos_lookup_x (CNT!): entering root_count+1=%d, dir %lu _count=%d\n", root_inode->i_count, dir->i_ino, dir->i_count));	/* FIXME: DEBUG, DELME */
 
-	d_instantiate (dentry, NULL);
+	dentry->d_inode = NULL;
 	umsdos_startlookup (dir);
 	if (len == 1 && name[0] == '.') {
 		d_add (dentry, dir);
@@ -802,26 +799,32 @@
 			inc_count (pseudo_root);
 		} else {
 			/* #Specification: locating .. / strategy
-			 * We use the msdos filesystem to locate the parent directory.
-			 * But it is more complicated than that.
+			 * We use the msdos filesystem to locate the parent directory,
+			 * but it is more complicated than that:
 			 * 
-			 * We have to step back even further to
+			 * we have to step back even further to
 			 * get the parent of the parent, so we can get the EMD
-			 * of the parent of the parent. Using the EMD file, we can
-			 * locate all the info on the parent, such a permissions
-			 * and owner.
+			 * of the parent of the parent.  Using the EMD file, we
+			 * can locate all the info on the parent, such as
+			 * permissions and ownership.
 			 */
+			struct dentry *dret, *d_dir = creat_dentry ("@d_dir3@", 5, dir, NULL);
+
 
-			ret = compat_umsdos_real_lookup (dir, "..", 2, &dentry->d_inode);
-			Printk (("ancestor ret %d dir %p *result %p ", ret, dir, dentry->d_inode));
-			if (ret == 0
+			dret = compat_umsdos_real_lookup (d_dir, "..", 2);
+			Printk (("ancestor ret %p dir %p *result %p ", dret, dir, dentry->d_inode));
+			if (dret
 			    && dentry->d_inode != root_inode
 			    && dentry->d_inode != pseudo_root) {
 				struct inode *aadir;
 				struct umsdos_dirent entry;
+				
+				Printk ((KERN_ERR "WARNING: umsdos_lookup_x: this won't work!\n"));
+				
+				dentry->d_inode = dret->d_inode;	/* FIXME! this should be rewritten ! it won't work this way! */
 
 				ret = umsdos_locate_ancestor (dentry->d_inode, &aadir, &entry);
-				iput (aadir);	/* FIXME */
+				fin_dentry (dret);
 			}
 		}
 	} else if (umsdos_is_pseudodos (dir, dentry)) {
@@ -830,7 +833,7 @@
 		 * and return the inode of the real root.
 		 */
 		d_add (dentry, root_inode);
-		inc_count (dentry->d_inode);
+		inc_count (dentry->d_inode); /* FIXME?! */
 		ret = 0;
 	} else {
 		struct umsdos_info info;
@@ -838,59 +841,61 @@
 		ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
 		if (ret == 0)
 			ret = umsdos_findentry (dir, &info, 0);
-		Printk (("lookup %.*s pos %lu ret %d len %d ", info.fake.len, info.fake.fname, info.f_pos, ret
-			 ,info.fake.len));
+		Printk (("lookup %.*s pos %lu ret %d len %d ", info.fake.len, info.fake.fname, info.f_pos, ret, info.fake.len));
 		if (ret == 0) {
 			/* #Specification: umsdos / lookup
-			 * A lookup for a file is done in two step. First, we locate
-			 * the file in the EMD file. If not present, we return
-			 * an error code (-ENOENT). If it is there, we repeat the
-			 * operation on the msdos file system. If this fails, it means
-			 * that the file system is not in sync with the emd file.
-			 * We silently remove this entry from the emd file,
-			 * and return ENOENT.
+			 * A lookup for a file is done in two steps.  First, we
+			 * locate the file in the EMD file.  If not present, we
+			 * return an error code (-ENOENT).  If it is there, we
+			 * repeat the operation on the msdos file system. If
+			 * this fails, it means that the file system is not in
+			 * sync with the EMD file.   We silently remove this
+			 * entry from the EMD file, and return ENOENT.
 			 */
-			struct inode *inode;
-
-			ret = compat_umsdos_real_lookup (dir, info.fake.fname, info.fake.len, &inode);
+			struct dentry *dret, *dir_dentry;
+		
+			dir_dentry = geti_dentry (dir);
+			dret = compat_umsdos_real_lookup (dir_dentry, info.fake.fname, info.fake.len);
+			
 
-			Printk ((KERN_DEBUG "umsdos_lookup_x: compat_umsdos_real_lookup for %.*s returned %d with inode=%p\n", info.fake.len, info.fake.fname, ret, inode));
+			PRINTK ((KERN_DEBUG "umsdos_lookup_x: compat_umsdos_real_lookup for %.*s returned %p\n", info.fake.len, info.fake.fname, dret));
 
-			if (inode == NULL) {
-				printk (KERN_WARNING "UMSDOS: Erase entry %.*s, out of sync with MsDOS\n"
-					,info.fake.len, info.fake.fname);
+			if (dret == NULL) {
+				printk (KERN_WARNING "UMSDOS:  Erase entry %.*s, out of sync with MS-DOS\n", info.fake.len, info.fake.fname);
 				umsdos_delentry (dir, &info, S_ISDIR (info.entry.mode));
 			} else {
-				Printk ((KERN_DEBUG "umsdos_lookup_x /mn/ debug: ino=%li\n", inode->i_ino));
+				Printk ((KERN_DEBUG "umsdos_lookup_x /mn/ debug: ino=%li\n", dret->d_inode->i_ino));
 
-				/* we've found it. now put inode in dentry */
-				d_add (dentry, inode);
-
-				umsdos_lookup_patch (dir, inode, &info.entry, info.f_pos);
-				Printk (("lookup ino %ld flags %d\n", inode->i_ino, info.entry.flags));
+				umsdos_lookup_patch (dir, dret->d_inode, &info.entry, info.f_pos);
+				Printk (("lookup ino %ld flags %d\n", dret->d_inode->i_ino, info.entry.flags));
 				if (info.entry.flags & UMSDOS_HLINK) {
-					Printk ((KERN_DEBUG "umsdos_lookup_x: here goes HLINK\n"));
-					ret = umsdos_hlink2inode (inode, &dentry->d_inode);
+					dret = umsdos_solve_hlink (dret);
 				}
-				if (pseudo_root && dentry->d_inode == pseudo_root && !nopseudo) {
+				if (pseudo_root && dret->d_inode == pseudo_root && !nopseudo) {
 					/* #Specification: pseudo root / dir lookup
 					 * For the same reason as readdir, a lookup in /DOS for
 					 * the pseudo root directory (linux) will fail.
 					 */
 					/*
-					 * This has to be allowed for resolving hard link
+					 * This has to be allowed for resolving hard links
 					 * which are recorded independently of the pseudo-root
 					 * mode.
 					 */
 					Printk ((KERN_ERR "umsdos_lookup_x: warning: untested /mn/ Pseudo_root thingy\n"));
-					iput (pseudo_root); /* FIXME?? */
-					d_instantiate (dentry, NULL);		/* FIXME: should be dput(dentry) ? */
+					/* iput (pseudo_root); / * FIXME?? */
+					d_instantiate (dentry, NULL);		/* negative lookup */
 					ret = -ENOENT;
+				} else {
+					/* We've found it OK.  Now put inode in dentry. */
+					inc_count (dret->d_inode);	/* lookup should return incremented i_count */
+					d_add (dentry, dret->d_inode);
 				}
+				fin_dentry (dret);
 			}
 		}
 	}
 	umsdos_endlookup (dir);
+	iput (root_inode);	/* pair to iget() above.WHY is it not needed ?! */
 	PRINTK ((KERN_DEBUG "umsdos_lookup_x: returning %d : name=%.*s (i_count=%d), dir=%lu (i_count=%d)\n", ret, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_count, dir->i_ino, dir->i_count));
 	Printk ((KERN_ERR "umsdos_lookup_x (CNT!): exiting root_count=%d, dir %lu _count=%d\n", root_inode->i_count, dir->i_ino, dir->i_count));	/* FIXME: DEBUG, DELME */
 	return ret;
@@ -898,90 +903,82 @@
 
 
 /*
- * Check if a file exist in the current directory.
- * Return 0 if ok, negative error code if not (ex: -ENOENT).
- * 
+ * Check whether a file exists in the current directory.
+ * Return 0 if OK, negative error code if not (ex: -ENOENT).
  * 
+ * called by VFS. should fill dentry->d_inode (via d_add), and 
+ * set (increment) dentry->d_inode->i_count.
+ *
  */
 
-int UMSDOS_lookup (
-			  struct inode *dir,
+int UMSDOS_lookup (	  struct inode *dir,
 			  struct dentry *dentry)
 {
 	int ret;
 
-	check_dentry (dentry);
 	ret = umsdos_lookup_x (dir, dentry, 0);
-	check_dentry (dentry);
 
-#if 1
 	if (ret == -ENOENT) {
 		Printk ((KERN_DEBUG "UMSDOS_lookup: converting -ENOENT to negative dentry !\n"));
-		d_add (dentry, NULL);	/* create negative dentry if not found */
+		d_add (dentry, NULL);	/* Create negative dentry if not found. */
 		ret = 0;
 	}
-#endif
 
 	return ret;
 }
 
 
-
 /*
- * Locate the inode pointed by a (pseudo) hard link
- * Return 0 if ok, a negative error code if not.
+ * gets dentry which points to pseudo-hardlink
+ *
+ * it should try to find file it points to
+ * if file is found, it should dput() original dentry and return new one (with d_count = i_count = 1)
+ * Otherwise, it should return with error, with dput()ed original dentry.
+ *
  */
 
-int umsdos_hlink2inode (struct inode *hlink, struct inode **result)
-{
-	struct inode *root_inode;
+struct dentry *umsdos_solve_hlink (struct dentry *hlink)
+{
+	struct dentry *base = hlink->d_sb->s_root;	/* root is our root for resolving pseudo-hardlink */
+	struct dentry *final = NULL;
+	struct inode *result;
 	int ret = -EIO;
-	struct dentry *dentry_src, *dentry_dst;
+	struct dentry *dentry_dst, *d_dir;
 	char *path;
 
-#if 0				/* FIXME: DELME */
-	Printk (("FIXME: just test. hlink2inode returning -ENOENT\n /mn/\n"));
-	return -ENOENT;		/* /mn/ FIXME just for test */
-#endif
+  	check_dentry_path (hlink, "HLINK BEGIN hlink");
 
 	path = (char *) kmalloc (PATH_MAX, GFP_KERNEL);
 
-	root_inode = iget (hlink->i_sb, UMSDOS_ROOT_INO);
-	*result = NULL;
+	result = NULL;
+
 	if (path == NULL) {
-		ret = -ENOMEM;
-		iput (hlink); /* FIXME? */
+		final = ERR_PTR (-ENOMEM);
 	} else {
 		struct file filp;
-		loff_t offs = 0;
-
-		dentry_src = creat_dentry ("hlink-mn", 8, hlink, NULL);
-
-		fill_new_filp (&filp, dentry_src);
+		fill_new_filp (&filp, hlink);
 		filp.f_flags = O_RDONLY;
+		filp.f_pos = 0;
 
 		Printk (("hlink2inode "));
-		if (umsdos_file_read_kmem (hlink, &filp, path, hlink->i_size, &offs) == hlink->i_size) {
+		if (umsdos_file_read_kmem (&filp, path, hlink->d_inode->i_size) == hlink->d_inode->i_size) {
 			struct inode *dir;
 			char *pt = path;
 
-			dir = root_inode;
-			path[hlink->i_size] = '\0';
-			iput (hlink); /* FIXME? */
-			inc_count (dir);
+			dir = base->d_inode;	/* start at root inode */
+			path[hlink->d_inode->i_size] = '\0';
+			inc_count (dir);	/* since we're going to iput() it in the loop below... */
+
 			while (1) {
 				char *start = pt;
 				int len;
 
-				while (*pt != '\0' && *pt != '/')
-					pt++;
+				while (*pt != '\0' && *pt != '/') pt++;
 				len = (int) (pt - start);
-				if (*pt == '/')
-					*pt++ = '\0';
-				/* FIXME. /mn/ fixed ? */
-
-				dentry_dst = creat_dentry (start, len, NULL, NULL);
+				if (*pt == '/') *pt++ = '\0';
 
+				d_dir = geti_dentry (dir);
+				dentry_dst = creat_dentry (start, len, NULL, d_dir);
 				if (dir->u.umsdos_i.i_emd_dir == 0) {
 					/* This is a DOS directory */
 
@@ -991,31 +988,48 @@
 					Printk (("hlink2inode /mn/: doing umsdos_lookup_x on %.*s\n", (int) dentry_dst->d_name.len, dentry_dst->d_name.name));
 					ret = umsdos_lookup_x (dir, dentry_dst, 1);
 				}
+				
+				iput (dir);	/* dir no longer needed */
 				Printk (("  returned %d\n", ret));
-				*result = dentry_dst->d_inode;	/* /mn/ ok ? */
+				result = dentry_dst->d_inode;
+				inc_count (result);	/* we need inode to survive. We'll iput it in next loop */
+
+				fin_dentry (dentry_dst);	/* no longer needed - this is pair to creat_dentry */
 
 				Printk (("h2n lookup :%s: -> %d ", start, ret));
-				if (ret == 0 && *pt != '\0') {
-					dir = *result;
-				} else {
+				
+				if (ret != 0) {
+					iput (result);			/* we have no longer any use for it... */
+					final = ERR_PTR (ret);		/* path componenet not found ! */
 					break;
+				} else {
+					if (*pt != '\0') {
+						dir = result;
+					} else {			/* we're finished! */
+						final = creat_dentry (hlink->d_name.name, hlink->d_name.len, result, hlink->d_parent);
+						break;
+					}
 				}
-			}
+			} /* end while */
 		} else {
-			Printk (("umsdos_hlink2inode: all those iput's() frighten me /mn/. Whatabout dput() ? FIXME!\n"));
-			iput (hlink); /* FIXME? */
+			Printk (("umsdos_solve_hlink: failed reading pseudolink!\n"));
 		}
-		Printk (("hlink2inode ret = %d %p -> %p\n", ret, hlink, *result));
+
+		Printk (("hlink2inode ret = %d %p -> %p\n", ret, hlink, result));
 		kfree (path);
 	}
-	return ret;
-}
+
+	fin_dentry (hlink);	/* original hlink no longer needed */
+  	check_dentry_path (hlink, "HLINK FIN hlink");
+  	check_dentry_path (final, "HLINK RET final");
+	return final;
+}	
 
 
 static struct file_operations umsdos_dir_operations =
 {
 	NULL,			/* lseek - default */
-	UMSDOS_dir_read,	/* read */
+	dummy_dir_read,		/* read */
 	NULL,			/* write - bad */
 	UMSDOS_readdir,		/* readdir */
 	NULL,			/* poll - default */
diff -u --recursive --new-file v2.1.119/linux/fs/umsdos/emd.c linux/fs/umsdos/emd.c
--- v2.1.119/linux/fs/umsdos/emd.c	Wed May 20 19:10:41 1998
+++ linux/fs/umsdos/emd.c	Wed Sep  2 16:12:37 1998
@@ -20,35 +20,24 @@
 
 #include <asm/delay.h>
 
-#define PRINTK(x)
-#define Printk(x) printk x
 
 /*
  *    Read a file into kernel space memory
  *      returns how many bytes read (from fat_file_read)
  */
 
-ssize_t umsdos_file_read_kmem (	struct inode *emd_dir,
-				struct file *filp,
+ssize_t umsdos_file_read_kmem (	struct file *filp,
 				char *buf,
-				size_t count,
-				loff_t *offs)
+				size_t count)
 {
 	int ret;
 
-	struct dentry *old_dentry;
 	mm_segment_t old_fs = get_fs ();
 
 	set_fs (KERNEL_DS);
 
-	old_dentry = filp->f_dentry;	/* save it */
-	filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir, NULL);
-	*offs = filp->f_pos;
-
-	PRINTK ((KERN_DEBUG "umsdos_file_read_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d, offs=%p\n", filp, buf, count, offs));
-	PRINTK ((KERN_DEBUG "  using emd=%ld\n", emd_dir->i_ino));
+	PRINTK ((KERN_DEBUG "umsdos_file_read_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d\n", filp, buf, count));
 	PRINTK ((KERN_DEBUG "  inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
-	PRINTK ((KERN_DEBUG "  ofs=%ld\n", (unsigned long) *offs));
 	PRINTK ((KERN_DEBUG "  f_pos=%Lu\n", filp->f_pos));
 	PRINTK ((KERN_DEBUG "  name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
 	PRINTK ((KERN_DEBUG "  i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary));
@@ -58,25 +47,11 @@
 	PRINTK ((KERN_DEBUG "  f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
 
 	MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2;
-	ret = fat_file_read (filp, buf, count, offs);
-	PRINTK ((KERN_DEBUG "fat_file_read returned with %d!\n", ret));
-
-	filp->f_pos = *offs;	/* we needed *filp only for this? grrrr... /mn/ */
-	/* FIXME: I have no idea what f_pos is used for. It seems to be used this way before offs was introduced.
-	 * this probably needs fixing /mn/ */
-
-	d_drop (filp->f_dentry);	/* FIXME: hmmmm... we should not dispose of it in this way ? */
-	filp->f_dentry = old_dentry;	/* restore orig. dentry (it is dentry of file we need info about. Dunno why it gets passed to us
-					 * since we have no use for it, expect to store totally unrelated data of offset of EMD_FILE
-					 * end not directory in it. But what the hell now... fat_file_read requires it also, but prolly expects
-					 * it to be file* of EMD not file we want to read EMD entry about... ugh. complicated to explain :) /mn/ */
 
-	/* FIXME: we probably need to destroy original filp->f_dentry first ? Do we ? And how ? this way we leave all sorts of dentries, inodes etc. lying around */
-	/* Also FIXME: all the same problems in umsdos_file_write_kmem */
+	ret = fat_file_read (filp, buf, count, &filp->f_pos);
+	PRINTK ((KERN_DEBUG "fat_file_read returned with %d!\n", ret));
 
-	PRINTK ((KERN_DEBUG "  (ret) using emd=%lu\n", emd_dir->i_ino));
 	PRINTK ((KERN_DEBUG "  (ret) inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
-	PRINTK ((KERN_DEBUG "  (ret) ofs=%Lu\n", *offs));
 	PRINTK ((KERN_DEBUG "  (ret) f_pos=%Lu\n", filp->f_pos));
 	PRINTK ((KERN_DEBUG "  (ret) name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
 	PRINTK ((KERN_DEBUG "  (ret) i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary));
@@ -89,9 +64,9 @@
 	{
 		struct umsdos_dirent *mydirent = buf;
 
-		PRINTK ((KERN_DEBUG "  (DDD) uid=%d\n", mydirent->uid));
-		PRINTK ((KERN_DEBUG "  (DDD) gid=%d\n", mydirent->gid));
-		PRINTK ((KERN_DEBUG "  (DDD) name=>%.20s<\n", mydirent->name));
+		Printk ((KERN_DEBUG "  (DDD) uid=%d\n", mydirent->uid));
+		Printk ((KERN_DEBUG "  (DDD) gid=%d\n", mydirent->gid));
+		Printk ((KERN_DEBUG "  (DDD) name=>%.20s<\n", mydirent->name));
 	}
 #endif
 
@@ -102,25 +77,23 @@
 
 /*
  *    Write to file from kernel space. 
- *      Does the real job, assumes all structures are initialized !
+ *      Does the real job, assumes all structures are initialized!
  */
 
 
 ssize_t umsdos_file_write_kmem_real (struct file * filp,
 				     const char *buf,
-				     size_t count,
-				     loff_t * offs)
+				     size_t count)
 {
 	ssize_t ret;
 	mm_segment_t old_fs = get_fs ();
 
 	set_fs (KERNEL_DS);
 
-	PRINTK ((KERN_DEBUG "umsdos_file_write_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d, offs=%p\n", filp, buf, count, offs));
+	PRINTK ((KERN_DEBUG "umsdos_file_write_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d\n", filp, buf, count));
 	PRINTK ((KERN_DEBUG "  struct dentry=%p\n", filp->f_dentry));
 	PRINTK ((KERN_DEBUG "  struct inode=%p\n", filp->f_dentry->d_inode));
 	PRINTK ((KERN_DEBUG "  inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
-	PRINTK ((KERN_DEBUG "  ofs=%ld\n", (unsigned long) *offs));
 	PRINTK ((KERN_DEBUG "  f_pos=%Lu\n", filp->f_pos));
 	PRINTK ((KERN_DEBUG "  name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
 	PRINTK ((KERN_DEBUG "  i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary));
@@ -131,13 +104,13 @@
 
 	/* note: i_binary=2 is for CVF-FAT. We put it here, instead of
 	 * umsdos_file_write_kmem, since it is also wise not to compress symlinks
-	 * (in unlikely event that they are > 512 bytes and can be compressed 
-	 * FIXME: should we set it when reading symlink too ? */
+	 * (in the unlikely event that they are > 512 bytes and can be compressed 
+	 * FIXME: should we set it when reading symlinks too? */
 
 	MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2;
 
-	ret = fat_file_write (filp, buf, count, offs);
-	PRINTK ((KERN_DEBUG "fat_file_write returned with %ld!\n", ret));
+	ret = fat_file_write (filp, buf, count, &filp->f_pos);
+	Printk ((KERN_DEBUG "fat_file_write returned with %ld!\n", (long int) ret));
 
 	set_fs (old_fs);
 	return ret;
@@ -145,32 +118,22 @@
 
 
 /*
- *    Write to a file from kernel space
+ *    Write to a file from kernel space.
  */
 
-ssize_t umsdos_file_write_kmem (struct inode * emd_dir,
-				struct file * filp,
+ssize_t umsdos_file_write_kmem (struct file *filp,
 				const char *buf,
-				size_t count,
-				loff_t * offs)
+				size_t count)
 {
 	int ret;
-	struct dentry *old_dentry;
-
 
 	Printk ((KERN_DEBUG " STARTED WRITE_KMEM /mn/\n"));
-	Printk ((KERN_DEBUG "  using emd=%ld\n", emd_dir->i_ino));
-
-	old_dentry = filp->f_dentry;	/* save it */
-	filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir, NULL);
-
-	*offs = filp->f_pos;	/* FIXME, in read_kmem also: offs is not used so why pass it ?!!! /mn/ */
-
-	ret = umsdos_file_write_kmem_real (filp, buf, count, offs);
+	ret = umsdos_file_write_kmem_real (filp, buf, count);
 
+#warning Should d_drop be here ?
+#if 0
 	d_drop (filp->f_dentry);
-	filp->f_pos = *offs;
-	filp->f_dentry = old_dentry;
+#endif
 
 	return ret;
 }
@@ -182,24 +145,18 @@
  * Write a block of bytes into one EMD file.
  * The block of data is NOT in user space.
  * 
- * Return 0 if ok, a negative error code if not.
+ * Return 0 if OK, a negative error code if not.
  */
 
-ssize_t umsdos_emd_dir_write (	struct inode * emd_dir,
-				struct file * filp,
+ssize_t umsdos_emd_dir_write (	struct file *filp,
 				char *buf,	/* buffer in kernel memory, not in user space */
-				size_t count,
-				loff_t * offs)
+				size_t count)
 {
 	int written;
-	loff_t myofs = 0;
 
 #ifdef __BIG_ENDIAN
 	struct umsdos_dirent *d = (struct umsdos_dirent *) buf;
 
-#endif
-	filp->f_flags = 0;
-#ifdef __BIG_ENDIAN
 	d->nlink = cpu_to_le16 (d->nlink);
 	d->uid = cpu_to_le16 (d->uid);
 	d->gid = cpu_to_le16 (d->gid);
@@ -210,13 +167,10 @@
 	d->mode = cpu_to_le16 (d->mode);
 #endif
 
-	if (offs)
-		myofs = *offs;	/* if offs is not NULL, read it */
-	Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %p, %d, %Ld\n", emd_dir, filp, buf, count, myofs));
-	written = umsdos_file_write_kmem (emd_dir, filp, buf, count, &myofs);
+	filp->f_flags = 0;
+	Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %d, %Ld\n", filp, buf, count, filp->f_pos));
+	written = umsdos_file_write_kmem (filp, buf, count);
 	Printk (("umsdos_emd_dir_write /mn/: write_kmem returned\n"));
-	if (offs)
-		*offs = myofs;	/* if offs is not NULL, store myofs there */
 
 #ifdef __BIG_ENDIAN
 	d->nlink = le16_to_cpu (d->nlink);
@@ -229,29 +183,27 @@
 	d->mode = le16_to_cpu (d->mode);
 #endif
 
-#if 1
+#if UMS_DEBUG
 	if (written != count)
 		Printk ((KERN_ERR "umsdos_emd_dir_write: ERROR: written (%d) != count (%d)\n", written, count));
 #endif
 
+
 	return written != count ? -EIO : 0;
 }
 
 
 
 /*
- *    Read a block of bytes from one EMD file.
+ *      Read a block of bytes from one EMD file.
  *      The block of data is NOT in user space.
- *      Return 0 if ok, -EIO if any error.
+ *      Return 0 if OK, -EIO if any error.
  */
 
-ssize_t umsdos_emd_dir_read (struct inode * emd_dir,
-			     struct file * filp,
+ssize_t umsdos_emd_dir_read (struct file *filp,
 			     char *buf,		/* buffer in kernel memory, not in user space */
-			     size_t count,
-			     loff_t * offs)
+			     size_t count)
 {
-	loff_t myofs = 0;
 	long int ret = 0;
 	int sizeread;
 
@@ -261,12 +213,10 @@
 
 #endif
 
-	if (offs)
-		myofs = *offs;	/* if offs is not NULL, read it */
 	filp->f_flags = 0;
-	sizeread = umsdos_file_read_kmem (emd_dir, filp, buf, count, &myofs);
+	sizeread = umsdos_file_read_kmem (filp, buf, count);
 	if (sizeread != count) {
-		printk ("UMSDOS: problem with EMD file. Can't read pos = %Ld (%d != %d)\n", filp->f_pos, sizeread, count);
+		printk ("UMSDOS:  problem with EMD file:  can't read pos = %Ld (%d != %d)\n", filp->f_pos, sizeread, count);
 		ret = -EIO;
 	}
 #ifdef __BIG_ENDIAN
@@ -279,42 +229,88 @@
 	d->rdev = le16_to_cpu (d->rdev);
 	d->mode = le16_to_cpu (d->mode);
 #endif
-	if (offs)
-		*offs = myofs;	/* if offs is not NULL, store myofs there */
 	return ret;
 
 }
 
 
 
+/*
+ * this checks weather filp points to directory or file,
+ * and if directory, it assumes that it has not yet been
+ * converted to point to EMD_FILE, and fixes it
+ *
+ * calling code should save old filp->f_dentry, call fix_emd_filp
+ * and if it succeeds (return code 0), do fin_dentry (filp->f_dentry)
+ * when it is over. It should also restore old filp->f_dentry.
+ *
+ */
+
+int fix_emd_filp (struct file *filp)
+{
+	struct inode *dir=filp->f_dentry->d_inode;
+	struct inode *emd_dir;
+  
+	/* is current file (which should be EMD or directory) EMD? */
+	if (dir->u.umsdos_i.i_emd_owner == 0xffffffff) {
+		dget (filp->f_dentry);
+		Printk ((KERN_WARNING "\nfix_emd_filp: EMD already done (should not be !)\n\n"));
+		return 0;
+	}
+	/* it is not, we need to make it so */
+	
+	emd_dir = umsdos_emd_dir_lookup (dir, 0);
+	if (emd_dir == NULL) {
+		Printk ((KERN_ERR "\nfix_emd_filp: EMD not found (should never happen)!!!\n\n"));
+		return -99;
+	}
+  
+	filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir, filp->f_dentry);	/* filp->f_dentry is dir containing EMD file, so it IS the parent dentry... */
+
+	return 0;
+}
+
 
 /*
- * Locate the EMD file in a directory .
+ * Locate the EMD file in a directory.
  * 
- * Return NULL if error. If ok, dir->u.umsdos_i.emd_inode 
+ * Return NULL if error, dir->u.umsdos_i.emd_inode if OK. 
+ * caller must iput() returned inode when finished with it!
  */
 
 struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat)
 {
 	struct inode *ret = NULL;
-	int res;
+	struct dentry *d_dir=NULL, *dlook=NULL;
+	int rv;
 
 	Printk ((KERN_DEBUG "Entering umsdos_emd_dir_lookup\n"));
+	if (!dir) printk (KERN_CRIT "umsdos FATAL: should never happen: dir=NULL!\n");
+	check_inode (dir);
+	
 	if (dir->u.umsdos_i.i_emd_dir != 0) {
 		ret = iget (dir->i_sb, dir->u.umsdos_i.i_emd_dir);
-		Printk (("umsdos_emd_dir_lookup: deja trouve %ld %p\n"
-			 ,dir->u.umsdos_i.i_emd_dir, ret));
+		Printk (("umsdos_emd_dir_lookup: deja trouve %ld %p\n", dir->u.umsdos_i.i_emd_dir, ret));
 	} else {
 		PRINTK ((KERN_DEBUG "umsdos /mn/: Looking for %.*s -", UMSDOS_EMD_NAMELEN, UMSDOS_EMD_FILE));
-		res = compat_umsdos_real_lookup (dir, UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, &ret);
-		PRINTK ((KERN_DEBUG "-returned %d\n", res));
+
+		d_dir = geti_dentry (dir);
+		dlook = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, d_dir);
+		rv = umsdos_real_lookup (dir, dlook);
+		
+		PRINTK ((KERN_DEBUG "-returned %d\n", rv));
 		Printk ((KERN_INFO "emd_dir_lookup "));
-		if (ret != NULL) {
+		
+		ret = dlook->d_inode;
+		if (ret) {
 			Printk (("Found --linux "));
 			dir->u.umsdos_i.i_emd_dir = ret->i_ino;
+			inc_count (ret);	/* we'll need the inode */
+			fin_dentry (dlook);	/* but not dentry */
+			check_inode (ret);
 		} else if (creat) {
 			int code;
-
+			
 			Printk ((" * ERROR * /mn/: creat not yet implemented? not fixed? "));
 			Printk (("avant create "));
 			inc_count (dir);
@@ -330,14 +326,15 @@
 				printk (KERN_WARNING "UMSDOS: Can't create EMD file\n");
 			}
 		}
+		
 		if (ret != NULL) {
 			/* Disable UMSDOS_notify_change() for EMD file */
+			/* inc_count (ret); // we need to return with incremented inode. FIXME: didn't umsdos_real_lookup already did that? and compat_msdos_create ? */
 			ret->u.umsdos_i.i_emd_owner = 0xffffffff;
 		}
-		
 	}
 
-#if 1
+#if UMS_DEBUG
 	Printk ((KERN_DEBUG "umsdos_emd_dir_lookup returning %p /mn/\n", ret));
 	if (ret != NULL)
 		Printk ((KERN_DEBUG " returning ino=%lu\n", ret->i_ino));
@@ -350,7 +347,7 @@
 /*
  * creates an EMD file
  * 
- * Return NULL if error. If ok, dir->u.umsdos_i.emd_inode 
+ * Return NULL if error, dir->u.umsdos_i.emd_inode if OK. 
  */
 
 struct inode *umsdos_emd_dir_create (struct inode *dir, struct dentry *dentry, int mode)
@@ -387,30 +384,33 @@
 /*
  * Read an entry from the EMD file.
  * Support variable length record.
- * Return -EIO if error, 0 if ok.
+ * Return -EIO if error, 0 if OK.
+ *
+ * does not change {d,i}_count
  */
 
-int umsdos_emd_dir_readentry (
-				     struct inode *emd_dir,
-				     struct file *filp,
+int umsdos_emd_dir_readentry (	     struct file *filp,
 				     struct umsdos_dirent *entry)
 {
 	int ret;
 
 	Printk ((KERN_DEBUG "umsdos_emd_dir_readentry /mn/: entering.\n"));
-	Printk (("umsdos_emd_dir_readentry /mn/: trying to lookup %.*s (ino=%lu) using EMD %lu\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name, filp->f_dentry->d_inode->i_ino, emd_dir->i_ino));
+	Printk (("umsdos_emd_dir_readentry /mn/: reading EMD %.*s (ino=%lu) at pos=%d\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name, filp->f_dentry->d_inode->i_ino, (int) filp->f_pos));
 
-	ret = umsdos_emd_dir_read (emd_dir, filp, (char *) entry, UMSDOS_REC_SIZE, NULL);
-	if (ret == 0) {		/* note /mn/: is this wrong? ret is always 0 or -EIO. but who knows. It used to work this way... */
+	ret = umsdos_emd_dir_read (filp, (char *) entry, UMSDOS_REC_SIZE);
+	if (ret == 0) {	/* if no error */
 		/* Variable size record. Maybe, we have to read some more */
 		int recsize = umsdos_evalrecsize (entry->name_len);
 
-		Printk ((KERN_DEBUG "umsdos_emd_dir_readentry /mn/: FIXME if %d > %d?\n", recsize, UMSDOS_REC_SIZE));
 		if (recsize > UMSDOS_REC_SIZE) {
-			ret = umsdos_emd_dir_read (emd_dir, filp, ((char *) entry) + UMSDOS_REC_SIZE, recsize - UMSDOS_REC_SIZE, NULL);
+			Printk ((KERN_DEBUG "umsdos_emd_dir_readentry /mn/: %d > %d!\n", recsize, UMSDOS_REC_SIZE));
+			ret = umsdos_emd_dir_read (filp, ((char *) entry) + UMSDOS_REC_SIZE, recsize - UMSDOS_REC_SIZE);
 		}
 	}
-	Printk (("umsdos_emd_dir_readentry /mn/: returning %d.\n", ret));
+	Printk (("umsdos_emd_dir_readentry /mn/: ret=%d.\n", ret));
+	if (entry && ret == 0) {
+		Printk (("umsdos_emd_dir_readentry /mn/: returning len=%d,name=%.*s\n", (int) entry->name_len, (int) entry->name_len, entry->name));
+	}
 	return ret;
 }
 
@@ -419,16 +419,15 @@
 
 /*
  * Write an entry in the EMD file.
- * Return 0 if ok, -EIO if some error.
+ * Return 0 if OK, -EIO if some error.
  */
 
-int umsdos_writeentry (
-			      struct inode *dir,
+int umsdos_writeentry (	      struct inode *dir,
 			      struct inode *emd_dir,
 			      struct umsdos_info *info,
 			      int free_entry)
 
-{				/* This entry is deleted, so Write all 0's */
+{				/* This entry is deleted, so write all 0's. */
 	int ret = 0;
 	struct dentry *emd_dentry;
 	struct file filp;
@@ -438,11 +437,11 @@
 	fill_new_filp (&filp, NULL);
 
 	Printk (("umsdos_writeentry /mn/: entering...\n"));
-	emd_dentry = creat_dentry ("wremd_mn", 8, emd_dir, NULL);
+	emd_dentry = geti_dentry (emd_dir);
 
 	if (free_entry) {
 		/* #Specification: EMD file / empty entries
-		 * Unused entry in the EMD file are identify
+		 * Unused entry in the EMD file are identified
 		 * by the name_len field equal to 0. However to
 		 * help future extension (or bug correction :-( ),
 		 * empty entries are filled with 0.
@@ -453,26 +452,26 @@
 		memset (entry->name + entry->name_len, '\0', sizeof (entry->name) - entry->name_len);
 		/* #Specification: EMD file / spare bytes
 		 * 10 bytes are unused in each record of the EMD. They
-		 * are set to 0 all the time. So it will be possible
+		 * are set to 0 all the time, so it will be possible
 		 * to do new stuff and rely on the state of those
-		 * bytes in old EMD file around.
+		 * bytes in old EMD files.
 		 */
 		memset (entry->spare, 0, sizeof (entry->spare));
 	}
 	Printk (("umsdos_writeentry /mn/: if passed...\n"));
 
 	if (!info)
-		printk (KERN_ERR "umsdosfs: /mn/ info is empty ! ooops...\n");
+		printk (KERN_ERR "UMSDOS:  /mn/ info is empty!  Oops!\n");
 	filp.f_pos = info->f_pos;
 	filp.f_reada = 0;
 	filp.f_flags = O_RDWR;
 	filp.f_dentry = emd_dentry;
-	filp.f_op = &umsdos_file_operations;	/* /mn/ - we have to fill it with dummy values so we won't segfault */
+	filp.f_op = &umsdos_file_operations;	/* /mn/ - We have to fill it with dummy values so we won't segfault. */
 
-	ret = umsdos_emd_dir_write (emd_dir, &filp, (char *) entry, info->recsize, NULL);
+	ret = umsdos_emd_dir_write (&filp, (char *) entry, info->recsize);
 	Printk (("emd_dir_write returned with %d!\n", ret));
 	if (ret != 0) {
-		printk ("UMSDOS: problem with EMD file. Can't write\n");
+		printk ("UMSDOS:  problem with EMD file:  can't write\n");
 	} else {
 		dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 		/* dir->i_dirt = 1; FIXME iput/dput ??? */
@@ -500,7 +499,7 @@
  * Fill the read buffer and take care of the byte remaining inside.
  * Unread bytes are simply move to the beginning.
  * 
- * Return -ENOENT if EOF, 0 if ok, a negative error code if any problem.
+ * Return -ENOENT if EOF, 0 if OK, a negative error code if any problem.
  */
 
 static int umsdos_fillbuf (
@@ -511,6 +510,7 @@
 	int mustmove = buf->size - buf->pos;
 	int mustread;
 	int remain;
+	struct inode *old_ino;
 
 	PRINTK ((KERN_DEBUG "Entering umsdos_fillbuf, for inode %lu, buf=%p\n", inode->i_ino, buf));
 
@@ -523,7 +523,10 @@
 	if (remain < mustread)
 		mustread = remain;
 	if (mustread > 0) {
-		ret = umsdos_emd_dir_read (inode, &buf->filp, buf->buffer + mustmove, mustread, NULL);
+		old_ino = buf->filp.f_dentry->d_inode;	/* FIXME: do we need to save/restore it ? */
+		buf->filp.f_dentry->d_inode = inode;
+		ret = umsdos_emd_dir_read (&buf->filp, buf->buffer + mustmove, mustread);
+		buf->filp.f_dentry->d_inode = old_ino;
 		if (ret == 0)
 			buf->size = mustmove + mustread;
 	} else if (mustmove) {
@@ -555,43 +558,73 @@
  * 
  * All this to say that umsdos_writeentry must be call after this
  * function since it rely on the f_pos field of info.
+ *
+ * calling code is expected to iput() returned *pt_emd_dir
+ *
  */
 
-static int umsdos_find (
-			       struct inode *dir,
+static int umsdos_find (       struct inode *dir,
 			       struct umsdos_info *info,	/* Hold name and name_len */
 								/* Will hold the entry found */
 			       struct inode **pt_emd_dir)	/* Will hold the emd_dir inode or NULL if not found */
 
 {
 	/* #Specification: EMD file structure
-	 * The EMD file uses a fairly simple layout. It is made of records
-	 * (UMSDOS_REC_SIZE == 64). When a name can't be written is a single
-	 * record, multiple contiguous record are allocated.
+	 * The EMD file uses a fairly simple layout.  It is made of records
+	 * (UMSDOS_REC_SIZE == 64).  When a name can't be written in a single
+	 * record, multiple contiguous records are allocated.
 	 */
 	int ret = -ENOENT;
 	struct inode *emd_dir;
 	struct umsdos_dirent *entry = &info->entry;
 
 	Printk (("umsdos_find: locating %.*s in dir %lu\n", entry->name_len, entry->name, dir->i_ino));
+	check_inode (dir);
 
 	emd_dir = umsdos_emd_dir_lookup (dir, 1);
 	if (emd_dir != NULL) {
 		int recsize = info->recsize;
 		struct {
 			off_t posok;	/* Position available to store the entry */
-			int found;	/* A valid empty position has been found */
+			int found;	/* A valid empty position has been found. */
 			off_t one;	/* One empty position -> maybe <- large enough */
 			int onesize;	/* size of empty region starting at one */
 		} empty;
 
-		/* Read several entries at a time to speed up the search */
+		/* Read several entries at a time to speed up the search. */
 		struct find_buffer buf;
-		struct dentry *dentry;
+		struct dentry *demd;
 
-		dentry = creat_dentry ("umsfind-mn", 10, emd_dir, NULL);
-
-		fill_new_filp (&buf.filp, dentry);
+		Printk (("umsdos_find: check emd_dir...\n"));
+		check_inode (emd_dir);
+		
+#if 0		/* FIXME! not needed. but there are count wraps. somewhere before umsdos_find there should be inc_count/iput pair around umsdos_find call.... */
+		inc_count (emd_dir);	/* since we are going to fin_dentry, and need emd_dir afterwards -- caling code will iput() it */
+#endif		
+		demd = geti_dentry (emd_dir);
+		if (demd) {
+			dget (demd);	/* because we'll have to dput it */
+		} else {
+			/*
+			 * We don't have dentry alias for this inode. Too bad.
+			 * So we'll fake something (as best as we can).
+			 * (maybe we should do it in any case just to keep it simple?)
+			 *
+			 * Note that this is legal for EMD file, since in some places
+			 * we keep inode, but discard dentry (since we would have no way
+			 * to discard it later). Yes, this probably should be fixed somehow,
+			 * it is just that I don't have idea how right now, and I've spent
+			 * quite some time to track it down why it dies here. Maybe new emd_dir_lookup
+			 * which returns dentry ? hmmmm... FIXME...
+			 *
+			 */
+		 	Printk ((KERN_WARNING "umsdos_find: inode has no alias for EMD inode, fake it\n"));
+		 	demd = creat_dentry ("@emd_find@", 10, emd_dir, NULL);
+		}
+		
+		check_dentry_path (demd, " EMD_DIR_DENTRY umsdos_find");
+		
+		fill_new_filp (&buf.filp, demd);
 
 		buf.pos = 0;
 		buf.size = 0;
@@ -613,20 +646,20 @@
 				}
 			} else if (rentry->name_len == 0) {
 				/* We are looking for an empty section at least */
-				/* recsize large */
+				/* as large as recsize. */
 				if (entry->name_len == 0) {
 					info->f_pos = file_pos;
 					ret = 0;
 					break;
 				} else if (!empty.found) {
 					if (empty.onesize == 0) {
-						/* This is the first empty record of a section */
+						/* This is the first empty record of a section. */
 						empty.one = file_pos;
 					}
 					/* grow the empty section */
 					empty.onesize += UMSDOS_REC_SIZE;
 					if (empty.onesize == recsize) {
-						/* here is a large enough section */
+						/* Here is a large enough section. */
 						empty.posok = empty.one;
 						empty.found = 1;
 					}
@@ -643,10 +676,9 @@
 						break;
 					}
 				} else {
-					empty.onesize = 0;	/* Reset the free slot search */
+					empty.onesize = 0;	/* Reset the free slot search. */
 					if (entry->name_len == rentry->name_len
-					    && memcmp (entry->name, rentry->name, rentry->name_len)
-					    == 0) {
+					    && memcmp (entry->name, rentry->name, rentry->name_len) == 0) {
 						info->f_pos = file_pos;
 						*entry = *rentry;
 						ret = 0;
@@ -658,6 +690,7 @@
 			}
 		}
 		umsdos_manglename (info);
+		fin_dentry (demd);
 	}
 	*pt_emd_dir = emd_dir;
 
@@ -667,15 +700,14 @@
 
 
 /*
- * Add a new entry in the emd file
- * Return 0 if ok or a negative error code.
- * Return -EEXIST if the entry already exist.
+ * Add a new entry in the EMD file.
+ * Return 0 if OK or a negative error code.
+ * Return -EEXIST if the entry already exists.
  * 
  * Complete the information missing in info.
  */
 
-int umsdos_newentry (
-			    struct inode *dir,
+int umsdos_newentry (	    struct inode *dir,
 			    struct umsdos_info *info)
 {
 	struct inode *emd_dir;
@@ -687,18 +719,17 @@
 		ret = umsdos_writeentry (dir, emd_dir, info, 0);
 		Printk (("umsdos_writeentry EMD ret = %d\n", ret));
 	}
-	iput (emd_dir); /* FIXME? */
+	iput (emd_dir);
 	return ret;
 }
 
 
 /*
  * Create a new hidden link.
- * Return 0 if ok, an error code if not.
+ * Return 0 if OK, an error code if not.
  */
 
-int umsdos_newhidden (
-			     struct inode *dir,
+int umsdos_newhidden (	     struct inode *dir,
 			     struct umsdos_info *info)
 {
 	struct inode *emd_dir;
@@ -707,28 +738,26 @@
 	umsdos_parse ("..LINK", 6, info);
 	info->entry.name_len = 0;
 	ret = umsdos_find (dir, info, &emd_dir);
-	iput (emd_dir); /* FIXME? */
+	iput (emd_dir);
 	if (ret == -ENOENT || ret == 0) {
 		/* #Specification: hard link / hidden name
 		 * When a hard link is created, the original file is renamed
 		 * to a hidden name. The name is "..LINKNNN" where NNN is a
 		 * number define from the entry offset in the EMD file.
 		 */
-		info->entry.name_len = sprintf (info->entry.name, "..LINK%ld"
-						,info->f_pos);
+		info->entry.name_len = sprintf (info->entry.name, "..LINK%ld", info->f_pos);
 		ret = 0;
 	}
 	return ret;
 }
 /*
- * Remove an entry from the emd file
- * Return 0 if ok, a negative error code otherwise.
+ * Remove an entry from the EMD file.
+ * Return 0 if OK, a negative error code otherwise.
  * 
  * Complete the information missing in info.
  */
 
-int umsdos_delentry (
-			    struct inode *dir,
+int umsdos_delentry (	    struct inode *dir,
 			    struct umsdos_info *info,
 			    int isdir)
 {
@@ -748,38 +777,41 @@
 			}
 		}
 	}
-	iput(emd_dir); /* FIXME? */
+	iput (emd_dir);
 	return ret;
 }
 
 
 /*
- * Verify is a EMD directory is empty.
- * Return 0 if not empty
- * 1 if empty
- * 2 if empty, no EMD file.
+ * Verify that a EMD directory is empty. Return 
+ * 0 if not empty,
+ * 1 if empty,
+ * 2 if empty or no EMD file.
  */
 
 int umsdos_isempty (struct inode *dir)
 {
-	struct dentry *dentry;
+	struct dentry *dentry, *d_dir;
 
 	int ret = 2;
 	struct inode *emd_dir = umsdos_emd_dir_lookup (dir, 0);
 
-	/* If the EMD file does not exist, it is certainly empty :-) */
+	/* If the EMD file does not exist, it is certainly empty. :-) */
 	if (emd_dir != NULL) {
 		struct file filp;
 
-		dentry = creat_dentry ("isempty-mn", 10, dir, NULL);
+		d_dir = geti_dentry (dir);
+		dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir, d_dir);
+		check_dentry_path (dentry, "umsdos_isempty BEGIN");
 		fill_new_filp (&filp, dentry);
+		filp.f_pos = 0;
 		filp.f_flags = O_RDONLY;
 
 		ret = 1;
 		while (filp.f_pos < emd_dir->i_size) {
 			struct umsdos_dirent entry;
 
-			if (umsdos_emd_dir_readentry (emd_dir, &filp, &entry) != 0) {
+			if (umsdos_emd_dir_readentry (&filp, &entry) != 0) {
 				ret = 0;
 				break;
 			} else if (entry.name_len != 0) {
@@ -787,24 +819,28 @@
 				break;
 			}
 		}
-		iput (emd_dir); /* FIXME? */
+		fin_dentry (dentry);
+		check_dentry_path (dentry, "umsdos_isempty END");
+		iput (emd_dir);
 	}
 	return ret;
 }
 
 /*
  * Locate an entry in a EMD directory.
- * Return 0 if ok, errcod if not, generally -ENOENT.
+ * Return 0 if OK, error code if not, generally -ENOENT.
+ *
+ * does not change i_count
  */
 
-int umsdos_findentry (
-			     struct inode *dir,
+int umsdos_findentry (	     struct inode *dir,
 			     struct umsdos_info *info,
 			     int expect)
-{				/* 0: anything */
-	/* 1: file */
-	/* 2: directory */
-	struct inode *emd_dir;
+				/* 0: anything */
+				/* 1: file */
+				/* 2: directory */
+{		
+	struct inode *emd_dir=NULL;
 	int ret = umsdos_find (dir, info, &emd_dir);
 
 	if (ret == 0) {
@@ -817,7 +853,7 @@
 			}
 		}
 	}
-	iput (emd_dir); /* FIXME? */
+	iput (emd_dir);
 	Printk (("umsdos_findentry: returning %d\n", ret));
 	return ret;
 }
diff -u --recursive --new-file v2.1.119/linux/fs/umsdos/file.c linux/fs/umsdos/file.c
--- v2.1.119/linux/fs/umsdos/file.c	Wed Aug 26 11:37:43 1998
+++ linux/fs/umsdos/file.c	Wed Sep  2 16:12:37 1998
@@ -19,10 +19,6 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 
-#define PRINTK(x)
-#define Printk(x)	printk x
-
-
 /*
  * Read a file into user space memory
  */
diff -u --recursive --new-file v2.1.119/linux/fs/umsdos/inode.c linux/fs/umsdos/inode.c
--- v2.1.119/linux/fs/umsdos/inode.c	Wed May 20 19:10:41 1998
+++ linux/fs/umsdos/inode.c	Wed Sep  2 16:12:37 1998
@@ -3,7 +3,6 @@
  *
  *      Written 1993 by Jacques Gelinas
  *      Inspired from linux/fs/msdos/... by Werner Almesberger
- *
  */
 
 #include <linux/module.h>
@@ -24,28 +23,11 @@
 
 
 struct inode *pseudo_root = NULL;	/* Useful to simulate the pseudo DOS */
-
-				/* directory. See UMSDOS_readdir_x() */
-
-/* #Specification: convention / PRINTK Printk and printk
- * Here is the convention for the use of printk inside fs/umsdos
- * 
- * printk carry important message (error or status).
- * Printk is for debugging (it is a macro defined at the beginning of
- * most source.
- * PRINTK is a nulled Printk macro.
- * 
- * This convention makes the source easier to read, and Printk easier
- * to shut off.
- */
-#define PRINTK(x)
-#define Printk(x) printk x
-
-
+					/* directory. See UMSDOS_readdir_x() */
 
 
 /*
- * makes return inode->i_dentry
+ * returns inode->i_dentry
  *
  */
 
@@ -53,14 +35,25 @@
 {
 	struct dentry *ret;
 	if (!inode) {
-		printk (KERN_ERR "geti_dentry: inode is NULL!\n");
+		printk (KERN_ERR "geti_dentry: ERROR: inode is NULL!\n");
 		return NULL;
 	}
-	ret = list_entry (inode->i_dentry.next, struct dentry, d_alias); /* FIXME: does this really work ? :) */
-	Printk ((KERN_DEBUG "geti_dentry : inode %lu: i_dentry is %p\n", inode->i_ino, ret));
+	if (inode->i_dentry.next == inode->i_dentry.next->next) {
+		printk (KERN_WARNING "geti_dentry: WARNING: inode does not have an dentry. returning NULL.\n");
+		return NULL;
+	}
+	ret = list_entry (inode->i_dentry.next, struct dentry, d_alias);
+
+	if (IS_ERR(ret)) {
+		Printk ((KERN_WARNING "geti_dentry: checking dentry... it is ERR(%ld) !\n", PTR_ERR(ret)));
+	}
+	
+	PRINTK ((KERN_DEBUG "geti_dentry : inode %lu: i_dentry is %p\n", inode->i_ino, ret));
 	return ret;
 }
 
+
+
 /*
  * makes inode->i_count++
  *
@@ -69,7 +62,7 @@
 inline void inc_count (struct inode *inode)
 {
 	inode->i_count++;
-	Printk ((KERN_DEBUG "inc_count: inode %lu incremented count to %d\n", inode->i_ino, inode->i_count));
+	PRINTK ((KERN_DEBUG "inc_count: inode %lu incremented count to %d\n", inode->i_ino, inode->i_count));
 }
 
 
@@ -95,6 +88,9 @@
 	filp->f_op = &umsdos_file_operations;	/* /mn/ - we have to fill it with SOMETHING */
 }
 
+
+
+#if UMS_DEBUG
 /*
  * check a superblock
  */
@@ -146,6 +142,56 @@
 	}
 }
 
+/*
+ * checks all inode->i_dentry
+ *
+ */
+void checkd_inode (struct inode *inode)
+{
+	struct dentry *ret;
+	struct list_head *cur;
+	int count = 0;
+	if (!inode) {
+		printk (KERN_ERR "checkd_inode: inode is NULL!\n");
+		return;
+	}
+
+	Printk ((KERN_DEBUG "checkd_inode:  inode %lu\n", inode->i_ino));
+	cur = inode->i_dentry.next;
+	while (count++ < 10) {
+		PRINTK (("1..."));
+		if (!cur) {
+			Printk ((KERN_ERR "checkd_inode: *** NULL reached. exit.\n"));
+			return;
+		}
+		PRINTK (("2..."));
+		ret = list_entry (cur, struct dentry, d_alias);
+		PRINTK (("3..."));
+		if (cur == cur->next) {
+			Printk ((KERN_DEBUG "checkd_inode: *** cur=cur->next: normal exit.\n"));
+			return;
+		}
+		PRINTK (("4..."));
+		if (!ret) {
+			Printk ((KERN_ERR "checkd_inode: *** ret dentry is NULL. exit.\n"));
+			return;
+		}
+		PRINTK (("5... (ret=%p)...", ret));
+		PRINTK (("5.1.. (ret->d_dname=%p)...", &(ret->d_name)));
+		PRINTK (("5.1.1. (ret->d_dname.len=%d)...", (int) ret->d_name.len));
+		PRINTK (("5.1.2. (ret->d_dname.name=%c)...", ret->d_name.name));
+		Printk ((KERN_DEBUG "checkd_inode:   i_dentry is %.*s\n", (int) ret->d_name.len, ret->d_name.name));
+		PRINTK (("6..."));
+		cur = cur->next;
+		PRINTK (("7..."));
+#if 1
+		Printk ((KERN_DEBUG "checkd_inode: *** finished after count 1 (operator forced)\n"));
+		return;
+#endif		
+	}
+	Printk ((KERN_ERR "checkd_inode: *** OVER LIMIT (loop?) !\n"));
+	return;
+}
 
 /*
  * internal part of check_dentry. does the real job.
@@ -155,9 +201,9 @@
 void check_dent_int (struct dentry *dentry, int parent)
 {
 	if (parent) {
-		Printk ((KERN_DEBUG "*  parent dentry: %.*s\n", (int) dentry->d_name.len, dentry->d_name.name));
+		Printk ((KERN_DEBUG "*  parent(%d) dentry: %.*s\n", parent, (int) dentry->d_name.len, dentry->d_name.name));
 	} else {
-		Printk ((KERN_DEBUG "\n*** checking dentry: %.*s\n", (int) dentry->d_name.len, dentry->d_name.name));
+		Printk ((KERN_DEBUG "*  checking dentry: %.*s\n", (int) dentry->d_name.len, dentry->d_name.name));
 	}
 	check_inode (dentry->d_inode);
 	Printk ((KERN_DEBUG "*   d_count=%d", dentry->d_count));
@@ -170,32 +216,53 @@
 }
 
 /*
- * checks dentry and prints info
+ * checks dentry with full traceback to root and prints info. Limited to 10 recursive depths to avoid infinite loops.
  *
  */
 
-void check_dentry (struct dentry *dentry)
+void check_dentry_path (struct dentry *dentry, const char *desc)
 {
+	int count=0;
+	Printk ((KERN_DEBUG "*** check_dentry_path: %.60s\n", desc));
+
 	if (!dentry) {
-		Printk ((KERN_DEBUG "\n*** checking dentry... it is NULL !\n"));
+		Printk ((KERN_DEBUG "*** checking dentry... it is NULL !\n"));
 		return;
 	}
-	check_dent_int (dentry, 0);
-
-	if (dentry->d_parent) {
-		check_dent_int (dentry->d_parent, 1);
-	} else {
-		Printk ((KERN_DEBUG "*   has no parent.\n"));
+	if (IS_ERR(dentry)) {
+		Printk ((KERN_DEBUG "*** checking dentry... it is ERR(%ld) !\n", PTR_ERR(dentry)));
+		return;
+	}
+	
+	while (dentry && count < 10) {
+		check_dent_int (dentry, count++);
+		if (dentry == dentry->d_parent) {
+			Printk ((KERN_DEBUG "*** end checking dentry (root reached ok)\n"));
+			break;
+		}
+		dentry = dentry->d_parent;
 	}
 
-	Printk ((KERN_DEBUG "*** end checking dentry\n"));
+	if (count >= 10) {	/* if infinite loop detected */
+		Printk ((KERN_ERR "*** WARNING ! INFINITE LOOP ! check_dentry_path aborted !\n"));
+	}
+	
+	if (!dentry) {
+		Printk ((KERN_ERR "*** WARNING ! found NULL dentry ! check_dentry_path aborted !\n"));
+	}
 }
+#else
+void check_sb (struct super_block *sb, const char c) {};
+void check_inode (struct inode *inode) {};
+void checkd_inode (struct inode *inode) {};
+void check_dentry_path (struct dentry *dentry, const char *desc) {};
+#endif	/* UMS_DEBUG */
+
 
 
 /*
- * makes dentry. for name name with length len. /mn/
+ * makes dentry. for name name with length len.
  * if inode is not NULL, puts it also.
- *
  */
 
 struct dentry *creat_dentry (const char *name, const int len, struct inode *inode, struct dentry *parent)
@@ -206,68 +273,111 @@
 	struct qstr qname;
 
 	if (inode)
-		Printk ((KERN_DEBUG "/mn/ creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name));
+		Printk ((KERN_DEBUG "creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name));
 	else
-		Printk ((KERN_DEBUG "/mn/ creat_dentry: creating empty dentry for %.*s\n", len, name));
+		Printk ((KERN_DEBUG "creat_dentry: creating empty dentry for %.*s\n", len, name));
 
 	qname.name = name;
 	qname.len = len;
+#if 1
+	#warning is full_name_hash OK for normal filenames? And for MSDOSFS accessed EMD files?
+	qname.hash = full_name_hash (name, len);
+#else
 	qname.hash = 0;
+#endif	
 
 	ret = d_alloc (parent, &qname);		/* create new dentry */
 	ret->d_inode = NULL;
 
-	if (!parent) {
+	if (parent) {
+#if 0
+		Printk ((KERN_DEBUG "creat_dentry: cloning parent d_op !\n"));
+		ret->d_op = parent->d_op;
+#else
+		ret->d_op = NULL;
+#endif		
+	} else {
 		ret->d_parent = ret;
-		Printk ((KERN_WARNING "creat_dentry: WARNING: NO parent! faking! beware !\n"));
+		Printk ((KERN_WARNING "creat_dentry: WARNING: NO parent! faking root! beware !\n"));
 	}
 
 
 	if (inode) {
-		ret->d_sb = inode->i_sb;	/* try to fill it in if avalaible. If avalaible in parent->d_sb, d_alloc will add it automatically */
+		if (!ret->d_sb)	ret->d_sb = inode->i_sb;	/* try to fill it in if available. If available in parent->d_sb, d_alloc will add it automatically */
 		d_add (ret, inode);
 	}
 
+	if (!ret->d_sb) {
+		printk (KERN_ERR "creat_dentry: ERROR: NO d_sb !\n");
+	} else if (!ret->d_sb->s_dev) {
+		printk (KERN_WARNING "creat_dentry: WARNING: NO s_dev. Ugh. !\n");
+	}
+	
 	return ret;
 }
 
 
 /*
  * removes temporary dentry created by creat_dentry
+ * it must have d_count of 1, and associated inode i_count of 1
+ * to be completely cleared.
  *
  */
 
 void kill_dentry (struct dentry *dentry)
 {
 	if (dentry) {
-		Printk (("/mn/ kill_dentry: kill_dentry %.*s :", (int) dentry->d_name.len, dentry->d_name.name));
-		if (dentry->d_inode)
-			Printk (("inode=%lu (i_count=%d)\n", dentry->d_inode->i_ino, dentry->d_inode->i_count));
-		else
-			Printk (("inode is NULL\n"));
-
-		/* FIXME: is this ok ?! /mn/ */
-		/* d_drop (dentry); */
-		/* d_invalidate (dentry); */
-		/*dput (dentry); */
-		/*d_delete (dentry) */
+		check_dentry_path (dentry, "KILL_DENTRY B4");
+		/* this idea for killing dentry (d_drop/dput pair) from NFS code. dcache.c code&comments seems to agree */
+#if 0
+		d_drop (dentry);
+		dput (dentry);	/* we are done with it */
+#endif		
+		check_dentry_path (dentry, "KILL_DENTRY AFT");
 	} else {
-		Printk (("/mn/ kill_dentry: dentry is NULL ?!\n"));
+		Printk (("kill_dentry: dentry is NULL ?!\n"));
 	}
 
 
-	Printk ((KERN_DEBUG "/mn/ kill_dentry: exiting...\n"));
+	Printk ((KERN_DEBUG "kill_dentry: exiting...\n"));
 	return;
 }
 
 
+/*
+ * finishes work with dentry
+ * it must have d_count of 1, and associated inode i_count of 1
+ * to be completely cleared.
+ *
+ * Currently, this is same as kill_dentry, but this may (will) change.
+ * kill_dentry will eventualy be killed (he who lives by the sword, dies
+ * by the sword :-) when all faked dentries are nuked out...
+ *
+ */
 
+void fin_dentry (struct dentry *dentry)
+{
+	if (dentry) {
+		if (IS_ERR(dentry)) {
+			Printk ((KERN_WARNING "fin_dentry: dentry is IS_ERR (%ld)?!\n", PTR_ERR (dentry)));
+		} else {
+			/* this idea for killing dentry (d_drop/dput pair) from NFS code. dcache.c code&comments seems to agree */
+			d_drop (dentry);
+			dput (dentry);	/* we are done with it */
+		}
+	} else {
+		Printk ((KERN_WARNING "fin_dentry: dentry is NULL ?!\n"));
+	}
 
+	PRINTK ((KERN_DEBUG "fin_dentry: exiting...\n"));
+	return;
+}
 
 
 void UMSDOS_put_inode (struct inode *inode)
 {
-	PRINTK ((KERN_DEBUG "put inode %p (%lu) owner %lu pos %lu dir %lu count=%d\n", inode, inode->i_ino
+	PRINTK ((KERN_DEBUG "put inode %p (%lu) owner %lu pos %lu dir %lu count=%d\n", inode
+		 ,inode->i_ino
 		 ,inode->u.umsdos_i.i_emd_owner, inode->u.umsdos_i.pos
 		 ,inode->u.umsdos_i.i_emd_dir, inode->i_count));
 
@@ -282,6 +392,7 @@
 void UMSDOS_put_super (struct super_block *sb)
 {
 	Printk ((KERN_DEBUG "UMSDOS_put_super: entering\n"));
+	check_dentry_path (sb->s_root, "put_super: START");
 	msdos_put_super (sb);
 	MOD_DEC_USE_COUNT;
 }
@@ -290,18 +401,18 @@
 
 /*
  * Call msdos_lookup, but set back the original msdos function table.
- * Return 0 if ok, or a negative error code if not.
+ * Return 0 if OK, or a negative error code if not.
  */
-int umsdos_real_lookup (
-			       struct inode *dir,
-			       struct dentry *dentry
-)
-{				/* Will hold inode of the file, if successful */
+int umsdos_real_lookup (       struct inode *dir,
+			       struct dentry *dentry)	/* Will hold inode of the file, if successful */
+{
 	int ret;
 
-	PRINTK ((KERN_DEBUG "umsdos_real_lookup: looking for %s /", dentry->d_name.name));
-	inc_count (dir);	/* should be here ? Causes all kind of missing iput()'s all around, but panics w/o it /mn/ */
+	PRINTK ((KERN_DEBUG "umsdos_real_lookup: looking for %.*s /", dentry->d_name.len, dentry->d_name.name));
+	inc_count (dir);
 	ret = msdos_lookup (dir, dentry);
+	dentry->d_op = NULL;	/* FIXME: Not needed? - if it was good once for MSDOS, it will be good any other time also. I hope :) */
+	iput (dir);		/* pair to inc_count(dir) above */
 	PRINTK (("/ returned %d\n", ret));
 
 	return ret;
@@ -312,6 +423,8 @@
  * First, it completes the function pointers, then
  * it locates the EMD file. If the EMD is there, then plug the
  * umsdos function table. If not, use the msdos one.
+ *
+ * {i,d}_counts are untouched by this function.
  */
 void umsdos_setup_dir_inode (struct inode *inode)
 {
@@ -319,8 +432,12 @@
 	{
 		struct inode *emd_dir;
 
+		Printk ((KERN_DEBUG "umsdos_setup_dir_inode: Entering for inode=%lu\n", inode->i_ino));
+		check_inode (inode);
 		emd_dir = umsdos_emd_dir_lookup (inode, 0);
-		Printk ((KERN_DEBUG "umsdos_setup_dir_inode: umsdos_emd_dir_lookup for inode=%p returned %p\n", inode, emd_dir));
+		Printk ((KERN_DEBUG "umsdos_setup_dir_inode: umsdos_emd_dir_lookup for inode=%lu returned %p\n", inode->i_ino, emd_dir));
+		check_inode (inode);
+		check_inode (emd_dir);
 
 		if (emd_dir == NULL) {
 			Printk ((KERN_DEBUG "umsdos_setup_dir_inode /mn/: Setting up rdir_inode_ops --> eg. NOT using EMD.\n"));
@@ -328,9 +445,8 @@
 		} else {
 			Printk ((KERN_DEBUG "umsdos_setup_dir_inode /mn/: Setting up dir_inode_ops --> eg. using EMD.\n"));
 			inode->i_op = &umsdos_dir_inode_operations;
+			iput (emd_dir);
 		}
-
-		iput (emd_dir);	/* FIXME? OK? */
 	}
 }
 
@@ -338,20 +454,19 @@
 /*
  * Add some info into an inode so it can find its owner quickly
  */
-void umsdos_set_dirinfo (
-				struct inode *inode,
+void umsdos_set_dirinfo (	struct inode *inode,
 				struct inode *dir,
 				off_t f_pos)
 {
 	struct inode *emd_owner;
 
-	/* FIXME, I don't have a clue on this one - /mn/ hmmm ? ok ? */
+	/* FIXME, I don't have a clue on this one - /mn/ Hmmm?  OK? */
 /*    Printk ((KERN_WARNING "umsdos_set_dirinfo: /mn/ FIXME: no clue. inode=%lu dir=%lu\n", inode->i_ino, dir->i_ino)); */
 	emd_owner = umsdos_emd_dir_lookup (dir, 1);
 	Printk (("umsdos_set_dirinfo: emd_owner is %lu for dir %lu\n", emd_owner->i_ino, dir->i_ino));
 	inode->u.umsdos_i.i_dir_owner = dir->i_ino;
 	inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
-	iput (emd_owner); /* FIXME? */
+	/* iput (emd_owner); / * FIXME? */
 	inode->u.umsdos_i.pos = f_pos;
 }
 
@@ -374,16 +489,17 @@
 
 /*
  * Connect the proper tables in the inode and add some info.
+ * i_counts is not changed.
  */
-void umsdos_patch_inode (
-				struct inode *inode,
+ 
+void umsdos_patch_inode (	struct inode *inode,
 				struct inode *dir,	/* May be NULL */
 				off_t f_pos)
 {
 	/*
 	 * This function is called very early to setup the inode, somewhat
 	 * too early (called by UMSDOS_read_inode). At this point, we can't
-	 * do to much, such as lookup up EMD files and so on. This causes
+	 * do too much, such as lookup up EMD files and so on. This causes
 	 * confusion in the kernel. This is why some initialisation
 	 * will be done when dir != NULL only.
 	 * 
@@ -404,18 +520,18 @@
 		if (S_ISREG (inode->i_mode)) {
 			if (MSDOS_SB (inode->i_sb)->cvf_format) {
 				if (MSDOS_SB (inode->i_sb)->cvf_format->flags & CVF_USE_READPAGE) {
-					Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations_readpage\n"));
+					Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = umsdos_file_inode_operations_readpage\n"));
 					inode->i_op = &umsdos_file_inode_operations_readpage;
 				} else {
-					Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations_no_bmap\n"));
+					Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = umsdos_file_inode_operations_no_bmap\n"));
 					inode->i_op = &umsdos_file_inode_operations_no_bmap;
 				}
 			} else {
 				if (inode->i_op->bmap != NULL) {
-					Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations\n"));
+					Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = umsdos_file_inode_operations\n"));
 					inode->i_op = &umsdos_file_inode_operations;
 				} else {
-					Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations_no_bmap\n"));
+					Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = umsdos_file_inode_operations_no_bmap\n"));
 					inode->i_op = &umsdos_file_inode_operations_no_bmap;
 				}
 			}
@@ -424,13 +540,13 @@
 				umsdos_setup_dir_inode (inode);
 			}
 		} else if (S_ISLNK (inode->i_mode)) {
-			Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_symlink_inode_operations\n"));
+			Printk ((KERN_DEBUG "umsdos_patch_inode /mn/:  setting i_op = umsdos_symlink_inode_operations\n"));
 			inode->i_op = &umsdos_symlink_inode_operations;
 		} else if (S_ISCHR (inode->i_mode)) {
-			Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = chrdev_inode_operations\n"));
+			Printk ((KERN_DEBUG "umsdos_patch_inode /mn/:  setting i_op = chrdev_inode_operations\n"));
 			inode->i_op = &chrdev_inode_operations;
 		} else if (S_ISBLK (inode->i_mode)) {
-			Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = blkdev_inode_operations\n"));
+			Printk ((KERN_DEBUG "umsdos_patch_inode /mn/:  setting i_op = blkdev_inode_operations\n"));
 			inode->i_op = &blkdev_inode_operations;
 		} else if (S_ISFIFO (inode->i_mode)) {
 			Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: uhm, init_fifo\n"));
@@ -460,7 +576,7 @@
 
 		Printk ((KERN_WARNING "umsdos_patch_inode: /mn/ Warning: untested emd_owner thingy...\n"));
 		emd_owner = umsdos_emd_dir_lookup (dir, 1);
-		iput (emd_owner); /* FIXME? */
+		/* iput (emd_owner); / * FIXME? */
 		if (emd_owner->i_ino != inode->u.umsdos_i.i_emd_owner) {
 			printk ("UMSDOS: *** EMD_OWNER ??? *** ino = %ld %ld <> %ld "
 				,inode->i_ino, emd_owner->i_ino, inode->u.umsdos_i.i_emd_owner);
@@ -471,12 +587,11 @@
 
 /*
  * Get the inode of the directory which owns this inode.
- * Return 0 if ok, -EIO if error.
+ * Return 0 if OK, -EIO if error.
  */
-int umsdos_get_dirowner (
-				struct inode *inode,
-				struct inode **result)
-{				/* Hold NULL if any error */
+int umsdos_get_dirowner (	struct inode *inode,
+				struct inode **result)	/* Hold NULL if any error */
+{				
 	/* else, the inode of the directory */
 	int ret = -EIO;
 	unsigned long ino = inode->u.umsdos_i.i_dir_owner;
@@ -489,7 +604,7 @@
 
 		if (dir != NULL) {
 			umsdos_patch_inode (dir, NULL, 0);
-			/* iput (dir);	/ * FIXME: /mn/ added this. Is it ok ? */
+			/* iput (dir);	/ * FIXME: /mn/ added this. Is it OK? */
 			ret = 0;
 		}
 	}
@@ -503,9 +618,9 @@
  */
 void UMSDOS_read_inode (struct inode *inode)
 {
-	Printk ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ", inode, inode->i_ino));
+	PRINTK ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ", inode, inode->i_ino));
 	msdos_read_inode (inode);
-	Printk (("ino after msdos_read_inode= %lu i_count=%d\n", inode->i_ino, inode->i_count));
+	PRINTK (("ino after msdos_read_inode= %lu i_count=%d\n", inode->i_ino, inode->i_count));
 	if (S_ISDIR (inode->i_mode)
 	    && (inode->u.umsdos_i.u.dir_info.creating != 0
 		|| inode->u.umsdos_i.u.dir_info.looking != 0
@@ -537,7 +652,7 @@
 	int ret = 0;
 	struct inode *root;
 
-	PRINTK ((KERN_DEBUG "UMSDOS_notify_change: entering\n"));
+	Printk ((KERN_DEBUG "UMSDOS_notify_change: entering\n"));
 
 	if ((ret = inode_change_ok (inode, attr)) != 0)
 		return ret;
@@ -564,7 +679,7 @@
 			 * UMSDOS_notify_change() (right here).
 			 * 
 			 * I am not sure of the behavior of the root inode for
-			 * a real UNIX file system. For now, this is a nop.
+			 * a real Unix file system. For now, this is a nop.
 			 */
 		} else if (i_emd_owner != 0xffffffff && i_emd_owner != 0) {
 			/* This inode is not a EMD file nor an inode used internally
@@ -582,18 +697,16 @@
 				struct file filp;
 				struct umsdos_dirent entry;
 				struct dentry *emd_dentry;
-				loff_t offs;
 
-				emd_dentry = creat_dentry ("notify_emd", 10, emd_owner, NULL);
+				emd_dentry = geti_dentry (emd_owner); /* FIXME? */
 				fill_new_filp (&filp, emd_dentry);
 
 				filp.f_pos = inode->u.umsdos_i.pos;
 				filp.f_reada = 0;
-				offs = filp.f_pos;	/* FIXME: /mn/ is this ok ? */
 				Printk (("pos = %Lu ", filp.f_pos));
 				/* Read only the start of the entry since we don't touch */
 				/* the name */
-				ret = umsdos_emd_dir_read (emd_owner, &filp, (char *) &entry, UMSDOS_REC_SIZE, &offs);
+				ret = umsdos_emd_dir_read (&filp, (char *) &entry, UMSDOS_REC_SIZE);
 				if (ret == 0) {
 					if (attr->ia_valid & ATTR_UID)
 						entry.uid = attr->ia_uid;
@@ -610,22 +723,19 @@
 
 					entry.nlink = inode->i_nlink;
 					filp.f_pos = inode->u.umsdos_i.pos;
-					offs = filp.f_pos;	/* FIXME: /mn/ is this ok ? */
-					ret = umsdos_emd_dir_write (emd_owner, &filp, (char *) &entry, UMSDOS_REC_SIZE, &offs);
+					ret = umsdos_emd_dir_write (&filp, (char *) &entry, UMSDOS_REC_SIZE);
 
-					Printk (("notify pos %lu ret %d nlink %d "
-						 ,inode->u.umsdos_i.pos
-						 ,ret, entry.nlink));
+					Printk (("notify pos %lu ret %d nlink %d ", inode->u.umsdos_i.pos, ret, entry.nlink));
 					/* #Specification: notify_change / msdos fs
 					 * notify_change operation are done only on the
 					 * EMD file. The msdos fs is not even called.
 					 */
 				}
-				iput (emd_owner);	/* FIXME? /mn/ */
+				/* iput (emd_owner);	/ * FIXME? /mn/ */
 			}
 			Printk (("\n"));
 		}
-		iput (root);	/* FIXME - /mn/ this is hopefully ok */
+		/* iput (root);	/ * FIXME - /mn/ This is should be OK. */
 	}
 	if (ret == 0)
 		inode_setattr (inode, attr);
@@ -660,7 +770,7 @@
 
 /*      
  * internal_notify_change (inode, &newattrs);
- * inode->i_state &= ~I_DIRTY; / * FIXME: this doesn't work... we need to remove ourselfs from list on dirty inodes /mn/ */
+ * inode->i_state &= ~I_DIRTY; / * FIXME: this doesn't work.  We need to remove ourselves from list on dirty inodes. /mn/ */
 }
 
 
@@ -668,13 +778,13 @@
 
 
 /* #Specification: function name / convention
- * A simple convention for function name has been used in
- * the UMSDOS file system. First all function use the prefix
- * umsdos_ to avoid name clash with other part of the kernel.
+ * A simple convention for function names has been used in
+ * the UMSDOS filesystem. First, all functions use the prefix
+ * umsdos_ to avoid name clashes with other parts of the kernel.
  * 
- * And standard VFS entry point use the prefix UMSDOS (upper case)
+ * Standard VFS entry points use the prefix UMSDOS (upper case)
  * so it's easier to tell them apart.
- * N.B. (FIXME) PTW, the order and contents of this struct changed
+ * N.B. (FIXME) PTW, the order and contents of this struct changed.
  */
 
 static struct super_operations umsdos_sops =
@@ -693,8 +803,7 @@
 /*
  * Read the super block of an Extended MS-DOS FS.
  */
-struct super_block *UMSDOS_read_super (
-					      struct super_block *sb,
+struct super_block *UMSDOS_read_super (	      struct super_block *sb,
 					      void *data,
 					      int silent)
 {
@@ -716,27 +825,39 @@
 
 	Printk ((KERN_DEBUG "UMSDOS /mn/: starting UMSDOS_read_super\n"));
 	MOD_INC_USE_COUNT;
-	PRINTK ((KERN_DEBUG "UMSDOS /mn/: sb = %p\n", sb));
+	Printk ((KERN_DEBUG "UMSDOS /mn/: sb = %p\n", sb));
+
+	MSDOS_SB(sb)->options.isvfat = 0;
 	res = msdos_read_super (sb, data, silent);
-	PRINTK ((KERN_DEBUG "UMSDOS /mn/: res = %p\n", res));
-	printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-4 (compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE);
+	sb->s_op = &umsdos_sops;
+	Printk ((KERN_DEBUG "UMSDOS /mn/: res = %p\n", res));
+	printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-7 (compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE);
 
 	if (res == NULL) {
+		sb->s_dev = 0;
 		MOD_DEC_USE_COUNT;
+		Printk ((KERN_DEBUG "UMSDOS: msdos_read_super failed ! mount aborted.\n"));
 		return NULL;
 	}
+	
 	MSDOS_SB (res)->options.dotsOK = 0;	/* disable hidden==dotfile */
-	res->s_op = &umsdos_sops;
+#if 1
+	res->s_root->d_op = NULL;	/* FIXME:?? clear d_op on root so it will not be inherited */
+#endif	
+	
 	Printk ((KERN_DEBUG "umsdos /mn/: here goes the iget ROOT_INO\n"));
 
-	pseudo = iget (res, UMSDOS_ROOT_INO);
-	Printk ((KERN_DEBUG "umsdos_read_super %p\n", pseudo));
+/*	pseudo = iget (res, UMSDOS_ROOT_INO);	// we probably could do it as below (and remove iput() below), but we want use_count to go up. Do we ? :) */
+	pseudo = res->s_root->d_inode;	/* msdos_read_super already did iget() it */
+	
+	Printk ((KERN_DEBUG "umsdos_read_super pseudo=%p\n", pseudo));
 
 	umsdos_setup_dir_inode (pseudo);
 	Printk ((KERN_DEBUG "umsdos_setup_dir_inode passed. pseudo i_count=%d\n", pseudo->i_count));
 
 	/* if (s == super_blocks){ FIXME, super_blocks no longer exported */
 	if (pseudo) {
+#if 0	/* FIXME URGENT: disable pseudo root-for the moment of testing. re-enable this before release ! */
 		/* #Specification: pseudo root / mount
 		 * When a umsdos fs is mounted, a special handling is done
 		 * if it is the root partition. We check for the presence
@@ -769,8 +890,8 @@
 		 */
 		struct dentry *root, *etc, *etc_rc, *sbin, *init = NULL;
 
-		root = creat_dentry (UMSDOS_PSDROOT_NAME, strlen (UMSDOS_PSDROOT_NAME), NULL, NULL);
-		sbin = creat_dentry ("sbin", 4, NULL, NULL);
+		root = creat_dentry (UMSDOS_PSDROOT_NAME, strlen (UMSDOS_PSDROOT_NAME), NULL, res->s_root);
+		sbin = creat_dentry ("sbin", 4, NULL, NULL); /* FIXME: should last NULL be root or res->s_root ?  Not NULL in any case.. */
 
 		Printk ((KERN_DEBUG "Mounting root\n"));
 		if (umsdos_real_lookup (pseudo, root) == 0
@@ -791,14 +912,14 @@
 				init = creat_dentry ("init", 4, NULL, etc);
 				etc_rc = creat_dentry ("rc", 2, NULL, etc);
 
-				/* if ((umsdos_real_lookup (etc,"init",4,init)==0 */
 				if ((umsdos_real_lookup (pseudo, init) == 0
 				     && S_ISREG (init->d_inode->i_mode))
-				/*       || (umsdos_real_lookup (etc,"rc",2,&rc)==0 */
 				    || (umsdos_real_lookup (pseudo, etc_rc) == 0
 				 && S_ISREG (etc_rc->d_inode->i_mode))) {
 					pseudo_ok = 1;
 				}
+				/* iput (pseudo); iput (pseudo);	/ * because msdos_real_lookup does inc_count (pseudo) */
+
 				/* FIXME !!!!!! */
 				/* iput(init); */
 				/* iput(rc); */
@@ -809,11 +930,11 @@
 			    && S_ISDIR (sbin->d_inode->i_mode)) {
 
 				Printk ((KERN_DEBUG "/%s/sbin is there\n", UMSDOS_PSDROOT_NAME));
-				/* if (umsdos_real_lookup (sbin,"init",4,init)==0 */
 				if (umsdos_real_lookup (pseudo, init) == 0
 				    && S_ISREG (init->d_inode->i_mode)) {
 					pseudo_ok = 1;
 				}
+				/*iput (pseudo);*/
 				/* FIXME !!! 
 				 * iput (init); */
 			}
@@ -830,8 +951,17 @@
 			 * iput (etc);
 			 */
 		}
-		iput (pseudo);
+#endif	
+		/*iput (pseudo); // iget was removed... so this no longer needed ? */
+	}
+#if 1
+	#warning UMSDOS: using ugly mount kludge only if necessary (DEBUG)
+	if (res->s_root->d_count != 1) {	/* if it is not 1, mount will fail with -EBUSY! */
+		printk (KERN_ERR "UMSDOS: mount kludge activated: root d_count was %d !\n", res->s_root->d_count);
+		res->s_root->d_count = 1;
 	}
+#endif	
+	check_dentry_path (res->s_root, "ROOT dentry check");
 	Printk ((KERN_DEBUG "umsdos_read_super /mn/: (pseudo=%lu, i_count=%d) returning %p\n", pseudo->i_ino, pseudo->i_count, res));
 	return res;
 }
diff -u --recursive --new-file v2.1.119/linux/fs/umsdos/ioctl.c linux/fs/umsdos/ioctl.c
--- v2.1.119/linux/fs/umsdos/ioctl.c	Wed May 20 19:10:41 1998
+++ linux/fs/umsdos/ioctl.c	Wed Sep  2 16:12:37 1998
@@ -15,9 +15,6 @@
 #include <linux/msdos_fs.h>
 #include <linux/umsdos_fs.h>
 
-#define PRINTK(x)
-#define Printk(x) printk x
-
 struct UMSDOS_DIR_ONCE {
 	struct dirent *ent;
 	int count;
@@ -53,14 +50,14 @@
 /*
  * Perform special function on a directory
  */
-int UMSDOS_ioctl_dir (
-			     struct inode *dir,
+int UMSDOS_ioctl_dir (	     struct inode *dir,
 			     struct file *filp,
 			     unsigned int cmd,
 			     unsigned long data)
 {
 	int ret = -EPERM;
 	int err;
+	struct dentry *old_dent;
 
 	/* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */
 	if (cmd != UMSDOS_GETVERSION
@@ -154,18 +151,18 @@
 			 * 
 			 * Return > 0 if success.
 			 */
-			struct inode *emd_dir = umsdos_emd_dir_lookup (dir, 0);
 
-			if (emd_dir != NULL) {
+			old_dent = filp->f_dentry;
+			if (fix_emd_filp (filp) == 0) {
 				while (1) {
-					if (filp->f_pos >= emd_dir->i_size) {
+					if (filp->f_pos >= filp->f_dentry->d_inode->i_size) {
 						ret = 0;
 						break;
 					} else {
 						struct umsdos_dirent entry;
 						off_t f_pos = filp->f_pos;
 
-						ret = umsdos_emd_dir_readentry (emd_dir, filp, &entry);
+						ret = umsdos_emd_dir_readentry (filp, &entry);
 						if (ret < 0) {
 							break;
 						} else if (entry.name_len > 0) {
@@ -175,15 +172,15 @@
 							umsdos_parse (entry.name, entry.name_len, &info);
 							info.f_pos = f_pos;
 							umsdos_manglename (&info);
-							copy_to_user (&idata->umsdos_dirent, &entry
-							,sizeof (entry));
-							copy_to_user (&idata->dos_dirent.d_name
-								      ,info.fake.fname, info.fake.len + 1);
+							copy_to_user (&idata->umsdos_dirent, &entry, sizeof (entry));
+							copy_to_user (&idata->dos_dirent.d_name, info.fake.fname, info.fake.len + 1);
 							break;
 						}
 					}
 				}
-				iput (emd_dir); /* FIXME? */
+				fin_dentry (filp->f_dentry);
+				filp->f_dentry = old_dent;
+				/* iput (filp->f_dentry->d_inode); / * FIXME? */
 			} else {
 				/* The absence of the EMD is simply seen as an EOF */
 				ret = 0;
@@ -204,7 +201,7 @@
 			struct inode *emd_dir = umsdos_emd_dir_lookup (dir, 1);
 
 			ret = emd_dir != NULL;
-			iput (emd_dir); /* FIXME?? */
+			/* iput (emd_dir); / * FIXME?? */
 
 			dir->i_op = ret
 			    ? &umsdos_dir_inode_operations
@@ -251,7 +248,7 @@
 				 * ,dir
 				 * ,data.umsdos_dirent.name,data.umsdos_dirent.name_len);
 				 */
-				old_dentry = creat_dentry (data.dos_dirent.d_name, data.dos_dirent.d_reclen, NULL, geti_dentry (dir));	/* FIXME: prolly should fill inode part */
+				old_dentry = creat_dentry (data.dos_dirent.d_name, data.dos_dirent.d_reclen, NULL, geti_dentry (dir));	/* FIXME: probably should fill inode part */
 				new_dentry = creat_dentry (data.umsdos_dirent.name, data.umsdos_dirent.name_len, NULL, geti_dentry (dir));
 				ret = msdos_rename (dir, old_dentry, dir, new_dentry);
 			} else if (cmd == UMSDOS_UNLINK_EMD) {
@@ -283,10 +280,10 @@
 				 * 
 				 * Return 0 if success.
 				 */
-				inc_count (dir);
-				dp = creat_dentry (data.dos_dirent.d_name, data.dos_dirent.d_reclen, dir, NULL);
-				dentry = creat_dentry ("ioctl_unlink", 12, NULL, dp);
+				dp = geti_dentry (dir);
+				dentry = compat_umsdos_real_lookup (dp, data.dos_dirent.d_name, data.dos_dirent.d_reclen);
 				ret = msdos_unlink (dir, dentry);
+				dput (dentry);	/* FIXME: is this OK now? */
 
 			} else if (cmd == UMSDOS_RMDIR_DOS) {
 				struct dentry *dentry, *dp;
@@ -298,10 +295,10 @@
 				 * 
 				 * Return 0 if success.
 				 */
-				inc_count (dir);
-				dp = creat_dentry (data.dos_dirent.d_name, data.dos_dirent.d_reclen, dir, NULL);
-				dentry = creat_dentry ("ioctl_unlink", 12, NULL, dp);
+				dp = geti_dentry (dir);
+				dentry = compat_umsdos_real_lookup (dp, data.dos_dirent.d_name, data.dos_dirent.d_reclen);
 				ret = msdos_rmdir (dir, dentry);
+				dput (dentry);	/* FIXME: is this OK now? */
 
 			} else if (cmd == UMSDOS_STAT_DOS) {
 				/* #Specification: ioctl / UMSDOS_STAT_DOS
@@ -315,9 +312,12 @@
 				 * Return 0 if success.
 				 */
 				struct inode *inode;
+				struct dentry *d_dir, *dret;
 
-				ret = compat_umsdos_real_lookup (dir, data.dos_dirent.d_name, data.dos_dirent.d_reclen, &inode);
-				if (ret == 0) {
+				d_dir = geti_dentry (dir);
+				dret = compat_umsdos_real_lookup (d_dir, data.dos_dirent.d_name, data.dos_dirent.d_reclen);
+				if (dret) {
+					inode = dret->d_inode;
 					data.stat.st_ino = inode->i_ino;
 					data.stat.st_mode = inode->i_mode;
 					data.stat.st_size = inode->i_size;
@@ -325,20 +325,20 @@
 					data.stat.st_ctime = inode->i_ctime;
 					data.stat.st_mtime = inode->i_mtime;
 					copy_to_user (&idata->stat, &data.stat, sizeof (data.stat));
-					/* iput (inode); FIXME */
+					fin_dentry (dret);
 				}
 			} else if (cmd == UMSDOS_DOS_SETUP) {
 				/* #Specification: ioctl / UMSDOS_DOS_SETUP
 				 * The UMSDOS_DOS_SETUP ioctl allow changing the
-				 * default permission of the MsDOS file system driver
-				 * on the fly. The MsDOS driver apply global permission
+				 * default permission of the MS-DOS filesystem driver
+				 * on the fly.  The MS-DOS driver applies global permissions
 				 * to every file and directory. Normally these permissions
 				 * are controlled by a mount option. This is not
 				 * available for root partition, so a special utility
 				 * (umssetup) is provided to do this, normally in
 				 * /etc/rc.local.
 				 * 
-				 * Be aware that this apply ONLY to MsDOS directory
+				 * Be aware that this applies ONLY to MS-DOS directories
 				 * (those without EMD --linux-.---). Umsdos directory
 				 * have independent (standard) permission for each
 				 * and every file.
diff -u --recursive --new-file v2.1.119/linux/fs/umsdos/mangle.c linux/fs/umsdos/mangle.c
--- v2.1.119/linux/fs/umsdos/mangle.c	Wed May 20 19:10:41 1998
+++ linux/fs/umsdos/mangle.c	Wed Sep  2 16:12:37 1998
@@ -4,7 +4,7 @@
  *      Written 1993 by Jacques Gelinas 
  *
  * Control the mangling of file name to fit msdos name space.
- * Many optimisation by GLU == dglaude@is1.vub.ac.be (GLAUDE DAVID)
+ * Many optimisations by GLU == dglaude@is1.vub.ac.be (Glaude David)
  */
 
 #include <linux/errno.h>
@@ -47,7 +47,7 @@
 		 * different extensions which should not clash with any useful
 		 * extension already popular or meaningful. Since most directory
 		 * have much less than 32 * 32 files in it, the first character
-		 * of the extension of any mangle name will be {.
+		 * of the extension of any mangled name will be {.
 		 * 
 		 * Here are the reason to do this (this kind of mangling).
 		 * 
@@ -72,10 +72,11 @@
 		} u;
 		char *pt = info->fake.fname + info->fake.len;
 
-		/* lookup for encoding the last character of the extension */
-		/* It contain valid character after the ugly one to make sure */
-		/* even if someone overflow the 32 * 32 * 9 limit, it still do */
-		/* something */
+		/* lookup for encoding the last character of the extension 
+		 * It contains valid character after the ugly one to make sure 
+		 * even if someone overflows the 32 * 32 * 9 limit, it still 
+		 * does something 
+		 */
 #define SPECIAL_MANGLING '{','}','(',')','!','`','^','&','@'
 		static char lookup3[] =
 		{
@@ -89,7 +90,7 @@
 #define lookup12 (lookup3+9)
 		u.entry_num = info->f_pos / UMSDOS_REC_SIZE;
 		if (u.entry_num > (9 * 32 * 32)) {
-			printk ("UMSDOS: More than 9216 file in a directory.\n"
+			printk (KERN_WARNING "UMSDOS: more than 9216 files in a directory.\n"
 				"This may break the mangling strategy.\n"
 				"Not a killer problem. See doc.\n");
 		}
@@ -136,7 +137,7 @@
 
 /*
  * Fill the struct info with the full and msdos name of a file
- * Return 0 if all is ok, a negative error code otherwise.
+ * Return 0 if all is OK, a negative error code otherwise.
  */
 int umsdos_parse (
 			 const char *fname,
@@ -153,10 +154,10 @@
 	if (len > UMSDOS_MAXNAME)
 		len = UMSDOS_MAXNAME;
 	{
-		const char *firstpt = NULL;	/* First place we saw a . in fname */
+		const char *firstpt = NULL;	/* First place we saw a "." in fname */
 
 		/* #Specification: file name / non MSDOS conforming / base length 0
-		 * file name beginning with a period '.' are invalid for MsDOS.
+		 * file names beginning with a period '.' are invalid for MS-DOS.
 		 * It needs absolutely a base name. So the file name is mangled
 		 */
 		int ivldchar = fname[0] == '.';		/* At least one invalid character */
@@ -164,8 +165,8 @@
 		int base_len;
 
 		/*
-		 * cardinal_per_size tells if there exist at least one
-		 * DOS pseudo devices on length n. See the test below.
+		 * cardinal_per_size tells if there exists at least one
+		 * DOS pseudo device on length n.  See the test below.
 		 */
 		static const char cardinal_per_size[9] =
 		{
@@ -175,15 +176,15 @@
 		/*
 		 * lkp translate all character to acceptable character (for DOS).
 		 * When lkp[n] == n, it means also it is an acceptable one.
-		 * So it serve both as a flag and as a translator.
+		 * So it serves both as a flag and as a translator.
 		 */
 		static char lkp[256];
 		static char is_init = 0;
 
 		if (!is_init) {
 			/*
-			 * Initialisation of the array is easier and less error prone
-			 * like this.
+			 * Initialisation of the array is easier and less error
+                         * prone like this.
 			 */
 			int i;
 			static const char *spc = "\"*+,/:;<=>?[\\]|~";
@@ -205,9 +206,9 @@
 				lkp[(unsigned char) (*spc++)] = '#';
 		}
 		/*  GLU
-		 * file name which are longer than 8+'.'+3 are invalid for MsDOS.
-		 * So the file name is to be mangled no more test needed.
-		 * This Speed Up for long and very long name.
+		 * File names longer than 8+'.'+3 are invalid for MS-DOS,
+		 * so the file name is to be mangled--no further test is needed.
+		 * This speeds up handling of long names.
 		 * The position of the last point is no more necessary anyway.
 		 */
 		if (len <= (8 + 1 + 3)) {
@@ -235,8 +236,8 @@
 						} else if (extlen == 1) {
 							/* #Specification: file name / non MSDOS conforming / last char == .
 							 * If the last character of a file name is
-							 * a period, mangling is applied. MsDOS do
-							 * not support those file name.
+							 * a period, mangling is applied. MS-DOS does
+							 * not support those file names.
 							 */
 							ivldchar = 1;
 							break;
@@ -244,7 +245,7 @@
 							/* #Specification: file name / non MSDOS conforming / mangling clash
 							 * To avoid clash with    the umsdos mangling, any file
 							 * with a special character as the first character
-							 * of the extension will be mangled. This solve the
+							 * of the extension will be mangled. This solves the
 							 * following problem:
 							 * 
 							 * #
@@ -285,7 +286,7 @@
 			 * name. So UMSDOS does not restrict its use.
 			 */
 			/* #Specification: file name / non MSDOS conforming / mangling
-			 * Non MSDOS conforming file name must use some alias to fit
+			 * Non MSDOS conforming file names must use some alias to fit
 			 * in the MSDOS name space.
 			 * 
 			 * The strategy is simple. The name is simply truncated to
@@ -294,15 +295,15 @@
 			 * to the entry number in the EMD file. The EMD file
 			 * only need to carry the real name.
 			 * 
-			 * Upper case is also convert to lower case.
+			 * Upper case is also converted to lower case.
 			 * Control character are converted to #.
-			 * Space are converted to #.
-			 * The following character are also converted to #.
+			 * Spaces are converted to #.
+			 * The following characters are also converted to #.
 			 * #
 			 * " * + , / : ; < = > ? [ \ ] | ~
 			 * #
 			 * 
-			 * Sometime, the problem is not in MsDOS itself but in
+			 * Sometimes the problem is not in MS-DOS itself but in
 			 * command.com.
 			 */
 			int i;
@@ -316,7 +317,7 @@
 			memcpy (info->fake.fname, fname, msdos_len);
 			for (i = 0; i < msdos_len; i++, pt++)
 				*pt = lkp[(unsigned char) (*pt)];
-			*pt = '\0';	/* GLU  C'est sur on a un 0 a la fin */
+			*pt = '\0';	/* GLU  We force null termination. */
 			info->msdos_reject = 1;
 			/*
 			 * The numeric extension is added only when we know
@@ -332,15 +333,15 @@
 		}
 		if (cardinal_per_size[base_len]) {
 			/* #Specification: file name / MSDOS devices / mangling
-			 * To avoid unreachable file from MsDOS, any MsDOS conforming
-			 * file with a basename equal to one of the MsDOS pseudo
+			 * To avoid unreachable file from MS-DOS, any MS-DOS conforming
+			 * file with a basename equal to one of the MS-DOS pseudo
 			 * devices will be mangled.
 			 * 
 			 * If a file such as "prn" was created, it would be unreachable
-			 * under MsDOS because prn is assumed to be the printer, even
+			 * under MS-DOS because "prn" is assumed to be the printer, even
 			 * if the file does have an extension.
 			 * 
-			 * Since the extension is unimportant to MsDOS, we must patch
+			 * Since the extension is unimportant to MS-DOS, we must patch
 			 * the basename also. We simply insert a minus '-'. To avoid
 			 * conflict with valid file with a minus in front (such as
 			 * "-prn"), we add an mangled extension like any other
@@ -359,10 +360,10 @@
 			 * 
 			 * "emmxxxx0","xmsxxxx0","setverxx"
 			 * 
-			 * (Thanks to Chris Hall <CAH17@PHOENIX.CAMBRIDGE.AC.UK>
-			 * for pointing these to me).
+			 * (Thanks to Chris Hall <cah17@phoenix.cambridge.ac.uk>
+			 * for pointing these out to me).
 			 * 
-			 * Is there one missing ?
+			 * Is there one missing?
 			 */
 			/* This table must be ordered by length */
 			static const char *tbdev[] =
@@ -386,13 +387,13 @@
 			for (i = start_ind_dev[base_len - 1]; i < start_ind_dev[base_len]; i++) {
 				if (memcmp (info->fake.fname, tbdev[i], base_len) == 0) {
 					memcpy (basen, info->fake.fname, base_len);
-					basen[base_len] = '\0';		/* GLU  C'est sur on a un 0 a la fin */
+					basen[base_len] = '\0';		/* GLU  We force null termination. */
 					/*
-					 * GLU        On ne fait cela que si necessaire, on essaye d'etre le
-					 * GLU        simple dans le cas general (le plus frequent).
+					 * GLU        We do that only if necessary; we try to do the
+					 * GLU        simple thing in the usual circumstance. 
 					 */
 					info->fake.fname[0] = '-';
-					strcpy (info->fake.fname + 1, basen);	/* GLU  C'est sur on a un 0 a la fin */
+					strcpy (info->fake.fname + 1, basen);	/* GLU  We already guaranteed a null would be at the end. */
 					msdos_len = (base_len == 8) ? 8 : base_len + 1;
 					info->msdos_reject = 1;
 					break;
@@ -400,15 +401,16 @@
 			}
 		}
 		info->fake.fname[msdos_len] = '\0';	/* Help doing printk */
-		/* GLU      Ce zero devrais deja y etre ! (invariant ?) */
+		/* GLU      This zero should (always?) be there already. */
 		info->fake.len = msdos_len;
-		/* Pourquoi ne pas utiliser info->fake.len partout ??? plus long ? */
+		/* Why not use info->fake.len everywhere? Is it longer?
+                 */
 		memcpy (info->entry.name, fname, len);
 		info->entry.name_len = len;
 		ret = 0;
 	}
 	/*
-	 * Evaluate how many record are needed to store this entry.
+	 * Evaluate how many records are needed to store this entry.
 	 */
 	info->recsize = umsdos_evalrecsize (len);
 	return ret;
@@ -435,15 +437,15 @@
 	"Hello.c", 1, "hello.c",
 #elseif
 /*
- * Je trouve les trois exemples ci-dessous tres "malheureux".
- * Je propose de mettre en minuscule dans un passe preliminaire,
- * et de tester apres si il y a d'autres caracters "mechants".
- * Bon, je ne l'ai pas fait, parceque ce n'est pas si facilement
- * modifiable que ca. Mais c'est pour le principe.
- * Evidemment cela augmente les chances de "Collision",
- * par exemple: entre "HELLO" et "Hello", mais ces problemes
- * peuvent etre traiter ailleur avec les autres collisions.
+ * I find the three examples below very unfortunate.  I propose to
+ * convert them to lower case in a quick preliminary pass, then test
+ * whether there are other troublesome characters.  I have not made
+ * this change, because it is not easy, but I wanted to mention the 
+ * principle.  Obviously something like that would increase the chance
+ * of collisions, for example between "HELLO" and "Hello", but these
+ * can be treated elsewhere along with the other collisions.
  */
+
 	"HELLO", 1, "hello",
 	"Hello.1", 1, "hello_1",
 	"Hello.c", 1, "hello_c",
@@ -465,11 +467,12 @@
 	"prn.abc", 1, "-prn",
 	"PRN", 1, "-prn",
   /* 
-   * GLU        ATTENTION : Le resultat de ceux-ci sont differents avec ma version
-   * GLU        du mangle par rapport au mangle originale.
-   * GLU        CAUSE: La maniere de calculer la variable baselen. 
-   * GLU                Pour toi c'est toujours 3
-   * GLU                Pour moi c'est respectivement 7, 8 et 8
+   * GLU        WARNING:  the results of these are different with my version
+   * GLU        of mangling compared to the original one.
+   * GLU        CAUSE:  the manner of calculating the baselen variable.
+   * GLU                For you they are always 3.
+   * GLU                For me they are respectively 7, 8, and 8.
+
    */
 	"PRN.abc", 1, "prn_abc",
 	"Prn.abcd", 1, "prn_abcd",
diff -u --recursive --new-file v2.1.119/linux/fs/umsdos/namei.c linux/fs/umsdos/namei.c
--- v2.1.119/linux/fs/umsdos/namei.c	Wed May 20 19:10:41 1998
+++ linux/fs/umsdos/namei.c	Wed Sep  2 16:12:37 1998
@@ -18,9 +18,6 @@
 #include <linux/umsdos_fs.h>
 #include <linux/malloc.h>
 
-#define PRINTK(x)
-#define Printk(x)	printk x
-
 #if 1
 /*
  * Wait for creation exclusivity.
@@ -230,7 +227,7 @@
 	struct dentry *fake;
 
 	Printk (("umsdos_create_any /mn/: create %.*s in dir=%lu - nevercreat=/", (int) dentry->d_name.len, dentry->d_name.name, dir->i_ino));
-	check_dentry (dentry);
+	check_dentry_path (dentry, "umsdos_create_any");
 	ret = umsdos_nevercreat (dir, dentry, -EEXIST);
 	Printk (("%d/\n", ret));
 	if (ret == 0) {
@@ -262,8 +259,14 @@
 					Printk (("Creation OK: [dir %lu] %.*s pid=%d pos %ld\n", dir->i_ino,
 						 info.fake.len, info.fake.fname, current->pid, info.f_pos));
 
+					check_dentry_path (dentry, "umsdos_create_any: BEG dentry");
+					check_dentry_path (fake, "umsdos_create_any: BEG fake");
 					d_instantiate (dentry, inode);	/* long name also gets inode info */
-					dput (fake);	/* FIXME: is this ok ? we try to kill short name dentry that we don't need */
+					inc_count (fake->d_inode); /* we must do it, since dput(fake) will iput(our_inode) which we still need for long name (dentry) */
+					/* dput (fake);	/ * FIXME: is this OK ? we try to kill short name dentry that we don't need */
+					check_dentry_path (dentry, "umsdos_create_any: END dentry");
+					check_dentry_path (fake, "umsdos_create_any: END fake");
+
 				} else {
 					/* #Specification: create / file exist in DOS
 					 * Here is a situation. Trying to create a file with
@@ -350,14 +353,12 @@
 				/* != 0, this is the value of flags */
 	int ret = -EPERM;
 	struct umsdos_info old_info;
-	int old_ret = umsdos_parse (old_dentry->d_name.name,
-				    old_dentry->d_name.len, &old_info);
+	int old_ret = umsdos_parse (old_dentry->d_name.name, old_dentry->d_name.len, &old_info);
 	struct umsdos_info new_info;
-	int new_ret = umsdos_parse (new_dentry->d_name.name,
-				    new_dentry->d_name.len, &new_info);
+	int new_ret = umsdos_parse (new_dentry->d_name.name, new_dentry->d_name.len, &new_info);
 
-	check_dentry (old_dentry);
-	check_dentry (new_dentry);
+	check_dentry_path (old_dentry, "umsdos_rename_f OLD");
+	check_dentry_path (new_dentry, "umsdos_rename_f OLD");
 
 	chkstk ();
 	Printk (("umsdos_rename %d %d ", old_ret, new_ret));
@@ -386,10 +387,12 @@
 					chkstk ();
 					Printk (("ret %d %d ", ret, new_info.fake.len));
 					if (ret == 0) {
-						struct dentry *old, *new;
+						struct dentry *old, *new, *d_old_dir, *dret;
 						struct inode *oldid = NULL;
 
-						ret = compat_umsdos_real_lookup (old_dir, old_info.fake.fname, old_info.fake.len, &oldid);
+						d_old_dir = creat_dentry ("@d_old_dir@", 11, old_dir, NULL);
+						dret = compat_umsdos_real_lookup (d_old_dir, old_info.fake.fname, old_info.fake.len);
+						if (dret) oldid = dret->d_inode;
 						old = creat_dentry (old_info.fake.fname, old_info.fake.len, oldid, old_dentry->d_parent);
 
 						new = creat_dentry (new_info.fake.fname, new_info.fake.len, NULL, new_dentry->d_parent);
@@ -398,7 +401,9 @@
 						inc_count (old_dir);
 						inc_count (new_dir);	/* Both inode are needed later */
 						
-						check_dentry (old); check_dentry (new);	/* FIXME: debug only */
+						check_dentry_path (old, "umsdos_rename_f OLD2");
+						check_dentry_path (new, "umsdos_rename_f NEW2");
+						
 						ret = msdos_rename (old_dir, old, new_dir, new);
 						chkstk ();
 						Printk (("after m_rename ret %d ", ret));
@@ -441,6 +446,7 @@
 									/* iput (inode); FIXME */
 								}
 							}
+							fin_dentry (dret);
 						}
 					}
 				} else {
@@ -458,8 +464,8 @@
 		umsdos_unlockcreate (old_dir);
 		umsdos_unlockcreate (new_dir);
 	}
-	check_dentry (old_dentry);
-	check_dentry (new_dentry);
+	check_dentry_path (old_dentry, "umsdos_rename_f OLD3");
+	check_dentry_path (new_dentry, "umsdos_rename_f NEW3");
 
 	Printk ((" _ret=%d\n", ret));
 	return ret;
@@ -467,7 +473,7 @@
 
 /*
  * Setup un Symbolic link or a (pseudo) hard link
- * Return a negative error code or 0 if ok.
+ * Return a negative error code or 0 if OK.
  */
 static int umsdos_symlink_x (
 				    struct inode *dir,
@@ -497,14 +503,14 @@
 	if (ret == 0) {
 		int len = strlen (symname);
 		struct file filp;
-		loff_t myofs = 0;
 
 		fill_new_filp (&filp, dentry);
+		filp.f_pos = 0;
 
 		/* Make the inode acceptable to MSDOS FIXME */
-		Printk ((KERN_WARNING "umsdos_symlink_x: /mn/ Is this ok?\n"));
+		Printk ((KERN_WARNING "umsdos_symlink_x:  /mn/ is this OK?\n"));
 		Printk ((KERN_WARNING "   symname=%s ; dentry name=%.*s (ino=%lu)\n", symname, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino));
-		ret = umsdos_file_write_kmem_real (&filp, symname, len, &myofs);
+		ret = umsdos_file_write_kmem_real (&filp, symname, len);
 		/* dput(dentry); ?? where did this come from FIXME */
 		
 		if (ret >= 0) {
@@ -528,7 +534,7 @@
 
 /*
  * Setup un Symbolic link.
- * Return a negative error code or 0 if ok.
+ * Return a negative error code or 0 if OK.
  */
 int UMSDOS_symlink (
 			   struct inode *dir,
@@ -550,19 +556,18 @@
 	struct inode *oldinode = olddentry->d_inode;
 
 	/* #Specification: hard link / strategy
-	 * Well ... hard link are difficult to implement on top of an
-	 * MsDOS fat file system. Unlike UNIX file systems, there are no
-	 * inode. A directory entry hold the functionality of the inode
-	 * and the entry.
+	 * Hard links are difficult to implement on top of an MS-DOS FAT file
+	 * system. Unlike Unix file systems, there are no inodes. A directory
+	 * entry holds the functionality of the inode and the entry.
 	 * 
 	 * We will used the same strategy as a normal Unix file system
-	 * (with inode) except we will do it symbolically (using paths).
+	 * (with inodes) except we will do it symbolically (using paths).
 	 * 
 	 * Because anything can happen during a DOS session (defragment,
-	 * directory sorting, etc...), we can't rely on MsDOS pseudo
+	 * directory sorting, etc.), we can't rely on an MS-DOS pseudo
 	 * inode number to record the link. For this reason, the link
 	 * will be done using hidden symbolic links. The following
-	 * scenario illustrate how it work.
+	 * scenario illustrates how it works.
 	 * 
 	 * Given a file /foo/file
 	 * 
@@ -733,9 +738,12 @@
 			  int mode	/* Permission bit + file type ??? */
 )
 {				/* Will hold the inode of the newly created file */
+	int ret;
 	Printk ((KERN_ERR "UMSDOS_create: entering\n"));
-	check_dentry (dentry);
-	return umsdos_create_any (dir, dentry, mode, 0, 0);
+	check_dentry_path (dentry, "UMSDOS_create START");
+	ret = umsdos_create_any (dir, dentry, mode, 0, 0);
+	check_dentry_path (dentry, "UMSDOS_create END");
+	return ret;
 }
 
 
@@ -771,7 +779,7 @@
 			if (ret == 0) {
 				struct dentry *temp, *tdir;
 
-				tdir = creat_dentry ("mkd-dir", 7, dir, NULL);
+				tdir = creat_dentry ("@mkd-dir@", 9, dir, NULL);
 				temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir);
 				inc_count (dir);
 				ret = msdos_mkdir (dir, temp, mode);
@@ -789,15 +797,17 @@
 					 * create immediately an EMD file in the new
 					 * sub-directory so it inherit UMSDOS semantic.
 					 */
-					struct inode *subdir;
+					struct inode *subdir=NULL;
+					struct dentry *d_dir, *dret;
 
-					ret = compat_umsdos_real_lookup (dir, info.fake.fname,
-						 info.fake.len, &subdir);
-					if (ret == 0) {
-						struct dentry *tdentry,
-						*tdsub;
+					d_dir = creat_dentry ("@d_dir5@", 7, dir, NULL);
+					dret = compat_umsdos_real_lookup (d_dir, info.fake.fname, info.fake.len);
+					if (dret) {
+						struct dentry *tdentry,	*tdsub;
+
+						subdir = dret->d_inode;
 
-						tdsub = creat_dentry ("mkd-emd", 7, subdir, NULL);
+						tdsub = creat_dentry ("@mkd-emd@", 9, subdir, NULL);
 						tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, tdsub);
 						ret = msdos_create (subdir, tdentry, S_IFREG | 0777);
 						kill_dentry (tdentry);	/* we don't want empty EMD file to be visible ! too bad kill_dentry does nothing at the moment :-)  FIXME */
@@ -806,11 +816,12 @@
 						subdir = NULL;
 						d_instantiate (dentry, temp->d_inode);
 						/* iput (result); FIXME */
+						fin_dentry (dret);
 					}
 					if (ret < 0) {
 						printk ("UMSDOS: Can't create empty --linux-.---\n");
 					}
-					/* iput (subdir); FIXME */
+					/*iput (subdir); FIXME*/
 				}
 			}
 			umsdos_unlockcreate (dir);
@@ -847,9 +858,9 @@
 
 	int ret;
 	
-	check_dentry (dentry);
+	check_dentry_path (dentry, "UMSDOS_mknod START");
 	ret = umsdos_create_any (dir, dentry, mode, rdev, 0);
-	check_dentry (dentry);
+	check_dentry_path (dentry, "UMSDOS_mknod END");
 
 	/* dput(dentry); / * /mn/ FIXME! */
 	return ret;
@@ -864,19 +875,19 @@
 {
 	/* #Specification: style / iput strategy
 	 * In the UMSDOS project, I am trying to apply a single
-	 * programming style regarding inode management. Many
-	 * entry point are receiving an inode to act on, and must
+	 * programming style regarding inode management.  Many
+	 * entry points are receiving an inode to act on, and must
 	 * do an iput() as soon as they are finished with
 	 * the inode.
 	 * 
-	 * For simple case, there is no problem. When you introduce
-	 * error checking, you end up with many iput placed around the
+	 * For simple cases, there is no problem.  When you introduce
+	 * error checking, you end up with many iput() calls in the
 	 * code.
 	 * 
 	 * The coding style I use all around is one where I am trying
 	 * to provide independent flow logic (I don't know how to
-	 * name this). With this style, code is easier to understand
-	 * but you rapidly get iput() all around. Here is an exemple
+	 * name this).  With this style, code is easier to understand
+	 * but you must do iput() everywhere.  Here is an example
 	 * of what I am trying to avoid.
 	 * 
 	 * #
@@ -887,7 +898,7 @@
 	 * }
 	 * ...
 	 * if (c){
-	 * // Complex state. Was b true ? 
+	 * // Complex state. Was b true? 
 	 * ...
 	 * }
 	 * ...
@@ -896,19 +907,19 @@
 	 * if (d){
 	 * // ...
 	 * }
-	 * // Was iput finally done ?
+	 * // Was iput finally done?
 	 * return status;
 	 * #
 	 * 
-	 * Here is the style I am using. Still sometime I do the
-	 * first when things are very simple (or very complicated :-( )
+	 * Here is the style I am using.  Still sometimes I do the
+	 * first when things are very simple (or very complicated :-( ).
 	 * 
 	 * #
 	 * if (a){
 	 * if (b){
 	 * ...
 	 * }else if (c){
-	 * // A single state gets here
+	 * // A single state gets here.
 	 * }
 	 * }else if (d){
 	 * ...
@@ -926,7 +937,7 @@
 	 * to do an iput()). One iput by inode. There is also one iput()
 	 * at the places where a successful operation is achieved. This
 	 * iput() is often done by a sub-function (often from the msdos
-	 * file system). So I get one too many iput() ? At the place
+	 * file system).  So I get one too many iput()?  At the place
 	 * where an iput() is done, the inode is simply nulled, disabling
 	 * the last one.
 	 * 
@@ -954,7 +965,7 @@
 
 	ret = umsdos_nevercreat (dir, dentry, -EPERM);
 	if (ret == 0) {
-		volatile struct inode *sdir;
+		/* volatile - DELME: I see no reason vor volatile /mn/ */ struct inode *sdir;
 
 		inc_count (dir);
 		ret = umsdos_lookup_x (dir, dentry, 0);
@@ -974,7 +985,7 @@
 			} else if ((empty = umsdos_isempty (sdir)) != 0) {
 				struct dentry *tdentry, *tedir;
 
-				tedir = creat_dentry ("emd-rmd", 7, dir, NULL);
+				tedir = creat_dentry ("@emd-rmd@", 9, dir, NULL);
 				tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, tedir);
 				umsdos_real_lookup (dir, tdentry);	/* fill inode part */
 				Printk (("isempty %d i_count %d ", empty, sdir->i_count));
@@ -1002,7 +1013,7 @@
 						/* the mangling */
 						umsdos_findentry (dir, &info, 2);
 
-						tdir = creat_dentry ("dir-rmd", 7, dir, NULL);
+						tdir = creat_dentry ("@dir-rmd@", 9, dir, NULL);
 						temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir);
 						umsdos_real_lookup (dir, temp);		/* fill inode part */
 
@@ -1078,7 +1089,7 @@
 				    current->fsuid == dir->i_uid) {
 					if (info.entry.flags & UMSDOS_HLINK) {
 						/* #Specification: hard link / deleting a link
-						 * When we deletes a file, and this file is a link
+						 * When we delete a file, and this file is a link
 						 * we must subtract 1 to the nlink field of the
 						 * hidden link.
 						 * 
@@ -1123,9 +1134,9 @@
 							struct dentry *temp,
 							*tdir;
 
-							Printk (("Avant msdos_unlink %.*s ", info.fake.len, info.fake.fname));
-							inc_count (dir);	/* FIXME /mn/ is this needed anymore now that msdos_unlink locks dir using d_parent ? */
-							tdir = creat_dentry ("dir-del", 7, dir, NULL);	/* FIXME /mn/: do we need iget(dir->i_ino) or would dir itself suffice ? */
+							Printk (("Before msdos_unlink %.*s ", info.fake.len, info.fake.fname));
+							/* inc_count (dir);	/ * FIXME /mn/ is this needed any more now that msdos_unlink locks directories using d_parent ? */
+							tdir = creat_dentry ("@dir-del@", 9, dir, NULL);	/* FIXME /mn/: do we need iget(dir->i_ino) or would dir itself suffice ? */
 							temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir);
 							umsdos_real_lookup (dir, temp);		/* fill inode part */
 
@@ -1141,7 +1152,7 @@
 					}
 				} else {
 					/* sticky bit set and we've not got permission */
-					Printk (("sticky set "));
+					Printk (("Sticky bit set. "));
 					ret = -EPERM;
 				}
 			}
@@ -1156,7 +1167,7 @@
 
 
 /*
- * Rename a file (move) in the file system.
+ * Rename (move) a file.
  */
 int UMSDOS_rename (
 			  struct inode *old_dir,
@@ -1166,14 +1177,14 @@
 {
 	/* #Specification: weakness / rename
 	 * There is a case where UMSDOS rename has a different behavior
-	 * than normal UNIX file system. Renaming an open file across
-	 * directory boundary does not work. Renaming an open file within
-	 * a directory does work however.
+	 * than a normal Unix file system.  Renaming an open file across
+	 * directory boundary does not work.  Renaming an open file within
+	 * a directory does work, however.
 	 * 
-	 * The problem (not sure) is in the linux VFS msdos driver.
+	 * The problem may is in Linux VFS driver for msdos.
 	 * I believe this is not a bug but a design feature, because
-	 * an inode number represent some sort of directory address
-	 * in the MSDOS directory structure. So moving the file into
+	 * an inode number represents some sort of directory address
+	 * in the MSDOS directory structure, so moving the file into
 	 * another directory does not preserve the inode number.
 	 */
 	int ret = umsdos_nevercreat (new_dir, new_dentry, -EEXIST);
@@ -1185,9 +1196,9 @@
 		ret = umsdos_rename_f (old_dir, old_dentry, new_dir, new_dentry, 0);
 		if (ret == -EEXIST) {
 			/* #Specification: rename / new name exist
-			 * If the destination name already exist, it will
-			 * silently be removed. EXT2 does it this way
-			 * and this is the spec of SUNOS. So does UMSDOS.
+			 * If the destination name already exists, it will
+			 * silently be removed.  EXT2 does it this way
+			 * and this is the spec of SunOS.  So does UMSDOS.
 			 * 
 			 * If the destination is an empty directory it will
 			 * also be removed.
@@ -1195,19 +1206,19 @@
 			/* #Specification: rename / new name exist / possible flaw
 			 * The code to handle the deletion of the target (file
 			 * and directory) use to be in umsdos_rename_f, surrounded
-			 * by proper directory locking. This was insuring that only
+			 * by proper directory locking.  This was ensuring that only
 			 * one process could achieve a rename (modification) operation
-			 * in the source and destination directory. This was also
-			 * insuring the operation was "atomic".
+			 * in the source and destination directory.  This was also
+			 * ensuring the operation was "atomic".
 			 * 
-			 * This has been changed because this was creating a kernel
-			 * stack overflow (stack is only 4k in the kernel). To avoid
+			 * This has been changed because this was creating a
+			 * stack overflow (the stack is only 4 kB) in the kernel.  To avoid
 			 * the code doing the deletion of the target (if exist) has
 			 * been moved to a upper layer. umsdos_rename_f is tried
 			 * once and if it fails with EEXIST, the target is removed
 			 * and umsdos_rename_f is done again.
 			 * 
-			 * This makes the code cleaner and (not sure) solve a
+			 * This makes the code cleaner and may solve a
 			 * deadlock problem one tester was experiencing.
 			 * 
 			 * The point is to mention that possibly, the semantic of
@@ -1217,7 +1228,7 @@
 			 * same target at the same time. Again, I am not sure it
 			 * is a problem at all.
 			 */
-			/* This is not super efficient but should work */
+			/* This is not terribly efficient but should work. */
 			inc_count (new_dir);
 			ret = UMSDOS_unlink (new_dir, new_dentry);
 			chkstk ();
diff -u --recursive --new-file v2.1.119/linux/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c
--- v2.1.119/linux/fs/umsdos/rdir.c	Wed Aug 26 11:37:43 1998
+++ linux/fs/umsdos/rdir.c	Wed Sep  2 16:12:37 1998
@@ -30,8 +30,7 @@
 	int real_root;
 };
 
-static int rdir_filldir (
-				void *buf,
+static int rdir_filldir (	void *buf,
 				const char *name,
 				int name_len,
 				off_t offset,
@@ -116,6 +115,7 @@
 		 * is the pseudo root (c:\linux).
 		 */
 	} else {
+		inc_count (dir);
 		ret = umsdos_real_lookup (dir, dentry);
 		inode = dentry->d_inode;
 
@@ -142,13 +142,13 @@
 
 			} else if (S_ISDIR (inode->i_mode)) {
 				/* We must place the proper function table */
-				/* depending if this is a MsDOS directory or an UMSDOS directory */
+				/* depending on whether this is an MS-DOS or a UMSDOS directory */
 				Printk ((KERN_DEBUG "umsdos_rlookup_x: setting up setup_dir_inode %lu...\n", inode->i_ino));
 				umsdos_setup_dir_inode (inode);
 			}
 		}
+		iput (dir);
 	}
-	iput (dir); /* FIXME? */
 	PRINTK ((KERN_DEBUG "umsdos_rlookup_x: returning %d\n", ret));
 	return ret;
 }
@@ -170,7 +170,7 @@
 {
 	/* #Specification: dual mode / rmdir in a DOS directory
 	 * In a DOS (not EMD in it) directory, we use a reverse strategy
-	 * compared with an Umsdos directory. We assume that a subdirectory
+	 * compared with a UMSDOS directory. We assume that a subdirectory
 	 * of a DOS directory is also a DOS directory. This is not always
 	 * true (umssync may be used anywhere), but make sense.
 	 * 
@@ -189,7 +189,7 @@
 		 * The pseudo sub-directory /DOS can't be removed!
 		 * This is done even if the pseudo root is not a Umsdos
 		 * directory anymore (very unlikely), but an accident (under
-		 * MsDOS) is always possible.
+		 * MS-DOS) is always possible.
 		 * 
 		 * EPERM is returned.
 		 */
@@ -219,11 +219,11 @@
 						 */
 						ret = -ENOTEMPTY;
 					} else if (empty == 1) {
-						/* We have to removed the EMD file */
+						/* We have to remove the EMD file. */
 						struct dentry *temp;
 
-						Printk ((KERN_WARNING "UMSDOS_rmdir: hmmm... whatabout inode ? FIXME\n"));
-						temp = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, NULL);	/* FIXME: prolly should fill inode part ? */
+						Printk ((KERN_WARNING "UMSDOS_rmdir: hmmm... what about inode? FIXME\n"));
+						temp = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, NULL);	/* FIXME: probably should fill inode part ? */
 						ret = msdos_unlink (sdir, temp);
 						sdir = NULL;
 						if (ret == 0) {
@@ -240,17 +240,18 @@
 		umsdos_unlockcreate (dir);
 	}
 	/* iput (dir); FIXME */
+	check_inode (dir);
 	return ret;
 }
 
 /* #Specification: dual mode / introduction
  * One goal of UMSDOS is to allow a practical and simple coexistence
- * between MsDOS and Linux in a single partition. Using the EMD file
- * in each directory, UMSDOS add Unix semantics and capabilities to
- * normal DOS file system. To help and simplify coexistence, here is
+ * between MS-DOS and Linux in a single partition. Using the EMD file
+ * in each directory, UMSDOS adds Unix semantics and capabilities to
+ * a normal DOS filesystem. To help and simplify coexistence, here is
  * the logic related to the EMD file.
  * 
- * If it is missing, then the directory is managed by the MsDOS driver.
+ * If it is missing, then the directory is managed by the MS-DOS driver.
  * The names are limited to DOS limits (8.3). No links, no device special
  * and pipe and so on.
  * 
@@ -259,12 +260,12 @@
  * of the real DOS directory and the EMD.
  * 
  * Whenever umssync is applied to a directory without EMD, one is
- * created on the fly. The directory is promoted to full unix semantic.
+ * created on the fly.  The directory is promoted to full Unix semantics.
  * Of course, the ls command will show exactly the same content as before
  * the umssync session.
  * 
- * It is believed that the user/admin will promote directories to unix
- * semantic as needed.
+ * It is believed that the user/admin will promote directories to Unix
+ * semantics as needed.
  * 
  * The strategy to implement this is to use two function table (struct
  * inode_operations). One for true UMSDOS directory and one for directory
@@ -272,12 +273,12 @@
  * 
  * Functions related to the DOS semantic (but aware of UMSDOS) generally
  * have a "r" prefix (r for real) such as UMSDOS_rlookup, to differentiate
- * from the one with full UMSDOS semantic.
+ * from the one with full UMSDOS semantics.
  */
 static struct file_operations umsdos_rdir_operations =
 {
 	NULL,			/* lseek - default */
-	UMSDOS_dir_read,	/* read */
+	dummy_dir_read,		/* read */
 	NULL,			/* write - bad */
 	UMSDOS_rreaddir,	/* readdir */
 	NULL,			/* poll - default */
diff -u --recursive --new-file v2.1.119/linux/fs/umsdos/symlink.c linux/fs/umsdos/symlink.c
--- v2.1.119/linux/fs/umsdos/symlink.c	Wed Aug 26 11:37:43 1998
+++ linux/fs/umsdos/symlink.c	Wed Sep  2 16:12:37 1998
@@ -19,9 +19,6 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 
-#define PRINTK(x)
-#define Printk(x)	printk x
-
 static struct file_operations umsdos_symlink_operations;
 
 
@@ -31,10 +28,9 @@
  * 
  */
 
-static int umsdos_readlink_x (
-				     struct dentry *dentry,
-				     char *buffer,
-				     int bufsiz)
+int umsdos_readlink_x (	     struct dentry *dentry,
+			     char *buffer,
+			     int bufsiz)
 {
 	int ret;
 	loff_t loffs = 0;
@@ -42,7 +38,10 @@
 
 	ret = dentry->d_inode->i_size;
 
-	check_dentry (dentry);
+	if (!(dentry->d_inode)) {
+		return -EBADF;
+	}
+
 	fill_new_filp (&filp, dentry);
 
 	filp.f_reada = 0;
@@ -68,7 +67,7 @@
 	if (fat_file_read (&filp, buffer, (size_t) ret, &loffs) != ret) {
 		ret = -EIO;
 	}
-#if 0				/* DEBUG */
+#if 0
 	{
 		struct umsdos_dirent *mydirent = buffer;
 
@@ -89,9 +88,11 @@
 	int ret;
 
 	PRINTK ((KERN_DEBUG "UMSDOS_readlink: calling umsdos_readlink_x for %.*s\n", (int) dentry->d_name.len, dentry->d_name.name));
+
 	ret = umsdos_readlink_x (dentry, buffer, buflen);
 	PRINTK ((KERN_DEBUG "readlink %d bufsiz %d\n", ret, buflen));
-	/* dput(dentry); / * FIXME /mn/ */
+	/* dput(dentry); / * FIXME /mn/? It seems it is unneeded. d_count is not changed by umsdos_readlink_x */
+
 	Printk ((KERN_WARNING "UMSDOS_readlink /mn/: FIXME! skipped dput(dentry). returning %d\n", ret));
 	return ret;
 
diff -u --recursive --new-file v2.1.119/linux/include/asm-i386/checksum.h linux/include/asm-i386/checksum.h
--- v2.1.119/linux/include/asm-i386/checksum.h	Sat May  2 14:19:54 1998
+++ linux/include/asm-i386/checksum.h	Sun Aug 30 14:31:13 1998
@@ -195,4 +195,22 @@
 	return csum_fold(sum);
 }
 
+/* 
+ *	Copy and checksum to user
+ */
+#define HAVE_CSUM_COPY_USER
+static __inline__ unsigned int csum_and_copy_to_user (const char *src, char *dst,
+				    int len, int sum, int *err_ptr)
+{
+	int *src_err_ptr=NULL;
+
+	if (verify_area(VERIFY_WRITE, dst, len) == 0)
+		return csum_partial_copy_generic(src, dst, len, sum, src_err_ptr, err_ptr);
+
+	if (len)
+		*err_ptr = -EFAULT;
+
+	return sum;
+}
+
 #endif
diff -u --recursive --new-file v2.1.119/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- v2.1.119/linux/include/asm-i386/processor.h	Tue Aug 18 22:02:07 1998
+++ linux/include/asm-i386/processor.h	Fri Sep  4 18:37:04 1998
@@ -31,6 +31,8 @@
 	__u32	x86_capability;
 	char	x86_vendor_id[16];
 	char	x86_model_id[64];
+	int 	x86_cache_size;  /* in KB - valid for CPUS which support this
+				    call  */
 	int	fdiv_bug;
 	int	f00f_bug;
 	unsigned long loops_per_sec;
diff -u --recursive --new-file v2.1.119/linux/include/asm-m68k/adb_mouse.h linux/include/asm-m68k/adb_mouse.h
--- v2.1.119/linux/include/asm-m68k/adb_mouse.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-m68k/adb_mouse.h	Wed Sep  2 09:39:18 1998
@@ -0,0 +1,23 @@
+#ifndef _ASM_ADB_MOUSE_H
+#define _ASM_ADB_MOUSE_H
+
+/*
+ * linux/include/asm-m68k/adb_mouse.h
+ * header file for Macintosh ADB mouse driver
+ * 27-10-97 Michael Schmitz
+ * copied from:
+ * header file for Atari Mouse driver
+ * by Robert de Vries (robert@and.nl) on 19Jul93
+ */
+
+struct mouse_status {
+	char		buttons;
+	short		dx;
+	short		dy;
+	int		ready;
+	int		active;
+	struct wait_queue *wait;
+	struct fasync_struct *fasyncptr;
+};
+
+#endif
diff -u --recursive --new-file v2.1.119/linux/include/asm-m68k/amigahw.h linux/include/asm-m68k/amigahw.h
--- v2.1.119/linux/include/asm-m68k/amigahw.h	Tue Jun 23 10:01:28 1998
+++ linux/include/asm-m68k/amigahw.h	Wed Sep  2 09:39:18 1998
@@ -20,7 +20,7 @@
      *  Different Amiga models
      */
 
-extern u_long amiga_model;
+extern unsigned long amiga_model;
 
 #define AMI_UNKNOWN	(0)
 #define AMI_500		(1)
@@ -44,7 +44,7 @@
      *  Chipsets
      */
 
-extern u_long amiga_chipset;
+extern unsigned long amiga_chipset;
 
 #define CS_STONEAGE	(0)
 #define CS_OCS		(1)
@@ -56,12 +56,12 @@
      *  Miscellaneous
      */
 
-extern u_long amiga_eclock;		/* 700 kHz E Peripheral Clock */
-extern u_long amiga_masterclock;	/* 28 MHz Master Clock */
-extern u_long amiga_colorclock;		/* 3.5 MHz Color Clock */
-extern u_long amiga_chip_size;		/* Chip RAM Size (bytes) */
-extern u_char amiga_vblank;		/* VBLANK Frequency */
-extern u_char amiga_psfreq;		/* Power Supply Frequency */
+extern unsigned long amiga_eclock;	/* 700 kHz E Peripheral Clock */
+extern unsigned long amiga_masterclock;	/* 28 MHz Master Clock */
+extern unsigned long amiga_colorclock;	/* 3.5 MHz Color Clock */
+extern unsigned long amiga_chip_size;	/* Chip RAM Size (bytes) */
+extern unsigned char amiga_vblank;	/* VBLANK Frequency */
+extern unsigned char amiga_psfreq;	/* Power Supply Frequency */
 
 
 #define AMIGAHW_DECLARE(name)	unsigned name : 1
@@ -111,122 +111,122 @@
 extern struct amiga_hw_present amiga_hw_present;
 
 struct CUSTOM {
-    u_short bltddat;
-    u_short dmaconr;
-    u_short vposr;
-    u_short vhposr;
-    u_short dskdatr;
-    u_short joy0dat;
-    u_short joy1dat;
-    u_short clxdat;
-    u_short adkconr;
-    u_short pot0dat;
-    u_short pot1dat;
-    u_short potgor;
-    u_short serdatr;
-    u_short dskbytr;
-    u_short intenar;
-    u_short intreqr;
-    u_char  *dskptr;
-    u_short dsklen;
-    u_short dskdat;
-    u_short refptr;
-    u_short vposw;
-    u_short vhposw;
-    u_short copcon;
-    u_short serdat;
-    u_short serper;
-    u_short potgo;
-    u_short joytest;
-    u_short strequ;
-    u_short strvbl;
-    u_short strhor;
-    u_short strlong;
-    u_short bltcon0;
-    u_short bltcon1;
-    u_short bltafwm;
-    u_short bltalwm;
-    u_char  *bltcpt;
-    u_char  *bltbpt;
-    u_char  *bltapt;
-    u_char  *bltdpt;
-    u_short bltsize;
-    u_char  pad2d;
-    u_char  bltcon0l;
-    u_short bltsizv;
-    u_short bltsizh;
-    u_short bltcmod;
-    u_short bltbmod;
-    u_short bltamod;
-    u_short bltdmod;
-    u_short spare2[4];
-    u_short bltcdat;
-    u_short bltbdat;
-    u_short bltadat;
-    u_short spare3[3];
-    u_short deniseid;
-    u_short dsksync;
-    u_short *cop1lc;
-    u_short *cop2lc;
-    u_short copjmp1;
-    u_short copjmp2;
-    u_short copins;
-    u_short diwstrt;
-    u_short diwstop;
-    u_short ddfstrt;
-    u_short ddfstop;
-    u_short dmacon;
-    u_short clxcon;
-    u_short intena;
-    u_short intreq;
-    u_short adkcon;
+    unsigned short bltddat;
+    unsigned short dmaconr;
+    unsigned short vposr;
+    unsigned short vhposr;
+    unsigned short dskdatr;
+    unsigned short joy0dat;
+    unsigned short joy1dat;
+    unsigned short clxdat;
+    unsigned short adkconr;
+    unsigned short pot0dat;
+    unsigned short pot1dat;
+    unsigned short potgor;
+    unsigned short serdatr;
+    unsigned short dskbytr;
+    unsigned short intenar;
+    unsigned short intreqr;
+    unsigned char  *dskptr;
+    unsigned short dsklen;
+    unsigned short dskdat;
+    unsigned short refptr;
+    unsigned short vposw;
+    unsigned short vhposw;
+    unsigned short copcon;
+    unsigned short serdat;
+    unsigned short serper;
+    unsigned short potgo;
+    unsigned short joytest;
+    unsigned short strequ;
+    unsigned short strvbl;
+    unsigned short strhor;
+    unsigned short strlong;
+    unsigned short bltcon0;
+    unsigned short bltcon1;
+    unsigned short bltafwm;
+    unsigned short bltalwm;
+    unsigned char  *bltcpt;
+    unsigned char  *bltbpt;
+    unsigned char  *bltapt;
+    unsigned char  *bltdpt;
+    unsigned short bltsize;
+    unsigned char  pad2d;
+    unsigned char  bltcon0l;
+    unsigned short bltsizv;
+    unsigned short bltsizh;
+    unsigned short bltcmod;
+    unsigned short bltbmod;
+    unsigned short bltamod;
+    unsigned short bltdmod;
+    unsigned short spare2[4];
+    unsigned short bltcdat;
+    unsigned short bltbdat;
+    unsigned short bltadat;
+    unsigned short spare3[3];
+    unsigned short deniseid;
+    unsigned short dsksync;
+    unsigned short *cop1lc;
+    unsigned short *cop2lc;
+    unsigned short copjmp1;
+    unsigned short copjmp2;
+    unsigned short copins;
+    unsigned short diwstrt;
+    unsigned short diwstop;
+    unsigned short ddfstrt;
+    unsigned short ddfstop;
+    unsigned short dmacon;
+    unsigned short clxcon;
+    unsigned short intena;
+    unsigned short intreq;
+    unsigned short adkcon;
     struct {
-	u_short	*audlc;
-	u_short audlen;
-	u_short audper;
-	u_short audvol;
-	u_short auddat;
-	u_short audspare[2];
+	unsigned short	*audlc;
+	unsigned short audlen;
+	unsigned short audper;
+	unsigned short audvol;
+	unsigned short auddat;
+	unsigned short audspare[2];
     } aud[4];
-    u_char  *bplpt[8];
-    u_short bplcon0;
-    u_short bplcon1;
-    u_short bplcon2;
-    u_short bplcon3;
-    u_short bpl1mod;
-    u_short bpl2mod;
-    u_short bplcon4;
-    u_short clxcon2;
-    u_short bpldat[8];
-    u_char  *sprpt[8];
+    unsigned char  *bplpt[8];
+    unsigned short bplcon0;
+    unsigned short bplcon1;
+    unsigned short bplcon2;
+    unsigned short bplcon3;
+    unsigned short bpl1mod;
+    unsigned short bpl2mod;
+    unsigned short bplcon4;
+    unsigned short clxcon2;
+    unsigned short bpldat[8];
+    unsigned char  *sprpt[8];
     struct {
-	u_short pos;
-	u_short ctl;
-	u_short dataa;
-	u_short datab;
+	unsigned short pos;
+	unsigned short ctl;
+	unsigned short dataa;
+	unsigned short datab;
     } spr[8];
-    u_short color[32];
-    u_short htotal;
-    u_short hsstop;
-    u_short hbstrt;
-    u_short hbstop;
-    u_short vtotal;
-    u_short vsstop;
-    u_short vbstrt;
-    u_short vbstop;
-    u_short sprhstrt;
-    u_short sprhstop;
-    u_short bplhstrt;
-    u_short bplhstop;
-    u_short hhposw;
-    u_short hhposr;
-    u_short beamcon0;
-    u_short hsstrt;
-    u_short vsstrt;
-    u_short hcenter;
-    u_short diwhigh;
-    u_short spare4[11];
-    u_short fmode;
+    unsigned short color[32];
+    unsigned short htotal;
+    unsigned short hsstop;
+    unsigned short hbstrt;
+    unsigned short hbstop;
+    unsigned short vtotal;
+    unsigned short vsstop;
+    unsigned short vbstrt;
+    unsigned short vbstop;
+    unsigned short sprhstrt;
+    unsigned short sprhstop;
+    unsigned short bplhstrt;
+    unsigned short bplhstop;
+    unsigned short hhposw;
+    unsigned short hhposr;
+    unsigned short beamcon0;
+    unsigned short hsstrt;
+    unsigned short vsstrt;
+    unsigned short hcenter;
+    unsigned short diwhigh;
+    unsigned short spare4[11];
+    unsigned short fmode;
 };
 
 /*
@@ -249,21 +249,21 @@
 #define DMAF_ALL		(0x01FF)
 
 struct CIA {
-    u_char pra; 		char pad0[0xff];
-    u_char prb; 		char pad1[0xff];
-    u_char ddra;		char pad2[0xff];
-    u_char ddrb;		char pad3[0xff];
-    u_char talo;		char pad4[0xff];
-    u_char tahi;		char pad5[0xff];
-    u_char tblo;		char pad6[0xff];
-    u_char tbhi;		char pad7[0xff];
-    u_char todlo;		char pad8[0xff];
-    u_char todmid;		char pad9[0xff];
-    u_char todhi;		char pada[0x1ff];
-    u_char sdr; 		char padb[0xff];
-    u_char icr; 		char padc[0xff];
-    u_char cra; 		char padd[0xff];
-    u_char crb; 		char pade[0xff];
+    unsigned char pra; 		char pad0[0xff];
+    unsigned char prb; 		char pad1[0xff];
+    unsigned char ddra;		char pad2[0xff];
+    unsigned char ddrb;		char pad3[0xff];
+    unsigned char talo;		char pad4[0xff];
+    unsigned char tahi;		char pad5[0xff];
+    unsigned char tblo;		char pad6[0xff];
+    unsigned char tbhi;		char pad7[0xff];
+    unsigned char todlo;	char pad8[0xff];
+    unsigned char todmid;	char pad9[0xff];
+    unsigned char todhi;	char pada[0x1ff];
+    unsigned char sdr; 		char padb[0xff];
+    unsigned char icr; 		char padc[0xff];
+    unsigned char cra; 		char padd[0xff];
+    unsigned char crb; 		char pade[0xff];
 };
 
 #define zTwoBase (0x80000000)
diff -u --recursive --new-file v2.1.119/linux/include/asm-m68k/bitops.h linux/include/asm-m68k/bitops.h
--- v2.1.119/linux/include/asm-m68k/bitops.h	Tue Aug 18 22:02:07 1998
+++ linux/include/asm-m68k/bitops.h	Wed Sep  2 09:39:18 1998
@@ -236,8 +236,6 @@
 #define hweight16(x) generic_hweight16(x)
 #define hweight8(x) generic_hweight8(x)
 
-#endif /* __KERNEL__ */
-
 /* Bitmap functions for the minix filesystem */
 
 extern __inline__ int
@@ -365,5 +363,7 @@
 	res = ext2_find_first_zero_bit (p, size - 32 * (p - addr));
 	return (p - addr) * 32 + res;
 }
+
+#endif /* __KERNEL__ */
 
 #endif /* _M68K_BITOPS_H */
diff -u --recursive --new-file v2.1.119/linux/include/asm-m68k/checksum.h linux/include/asm-m68k/checksum.h
--- v2.1.119/linux/include/asm-m68k/checksum.h	Tue Jun 23 10:01:28 1998
+++ linux/include/asm-m68k/checksum.h	Wed Sep  2 09:39:18 1998
@@ -16,7 +16,7 @@
 unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum);
 
 /*
- * the same as csum_partial_copy, but copies from src while it
+ * the same as csum_partial, but copies from src while it
  * checksums
  *
  * here even more important to align src and dst on a 32-bit (or even
@@ -33,13 +33,9 @@
  * better 64-bit) boundary
  */
 
-unsigned int csum_partial_copy_fromuser(const char *src, char *dst, int len, int sum);
+extern unsigned int csum_partial_copy_from_user(const char *src, char *dst,
+						int len, int sum, int *csum_err);
 
-extern unsigned int
-csum_partial_copy_from_user ( const char *src, char *dst,
-			      int len, int sum, int *csum_err);
-
-/* FIXME: this needs to be written to really do no check -- Cort */
 #define csum_partial_copy_nocheck(src, dst, len, sum)	\
 	csum_partial_copy((src), (dst), (len), (sum))
 
diff -u --recursive --new-file v2.1.119/linux/include/asm-m68k/hardirq.h linux/include/asm-m68k/hardirq.h
--- v2.1.119/linux/include/asm-m68k/hardirq.h	Tue Jun 23 10:01:28 1998
+++ linux/include/asm-m68k/hardirq.h	Wed Sep  2 09:39:18 1998
@@ -7,12 +7,12 @@
 
 #define in_interrupt() (local_irq_count[smp_processor_id()] + local_bh_count[smp_processor_id()] != 0)
 
-#define hardirq_trylock(cpu)	(++local_irq_count[cpu], (cpu) == 0)
-#define hardirq_endlock(cpu)	(--local_irq_count[cpu])
+#define hardirq_trylock(cpu)	(local_irq_count[cpu] == 0)
+#define hardirq_endlock(cpu)	do { } while (0)
 
 #define hardirq_enter(cpu)	(local_irq_count[cpu]++)
 #define hardirq_exit(cpu)	(local_irq_count[cpu]--)
 
-#define synchronize_irq()	do { } while (0)
+#define synchronize_irq()	barrier()
 
 #endif
diff -u --recursive --new-file v2.1.119/linux/include/asm-m68k/io.h linux/include/asm-m68k/io.h
--- v2.1.119/linux/include/asm-m68k/io.h	Tue Jun 23 10:01:28 1998
+++ linux/include/asm-m68k/io.h	Wed Sep  2 09:39:18 1998
@@ -3,8 +3,7 @@
 
 #ifdef __KERNEL__
 
-#include <linux/config.h> /* CONFIG_ATARI, CONFIG_HADES */
-#include <asm/byteorder.h>
+#include <linux/config.h>
 
 #ifdef CONFIG_ATARI
 #include <asm/atarihw.h>
@@ -12,6 +11,8 @@
 #define SLOW_DOWN_IO	do { if (MACH_IS_ATARI) MFPDELAY(); } while (0)
 #endif
 
+#include <asm/virtconvert.h>
+
 /*
  * readX/writeX() are used to access memory mapped devices. On some
  * architectures the memory mapped IO stuff needs to be accessed
@@ -41,34 +42,6 @@
 
 #define outb(x,addr) ((void) writeb(x,addr))
 #define outb_p(x,addr) outb(x,addr)
-
-/*
- * Change virtual addresses to physical addresses and vv.
- */
-extern unsigned long mm_vtop(unsigned long addr) __attribute__ ((const));
-extern unsigned long mm_ptov(unsigned long addr) __attribute__ ((const));
-
-extern inline unsigned long virt_to_phys(volatile void * address)
-{
-	return mm_vtop((unsigned long)address);
-}
-
-extern inline void * phys_to_virt(unsigned long address)
-{
-	return (void *) mm_ptov(address);
-}
-
-/*
- * IO bus memory addresses are 1:1 with the physical address,
- * except on the PCI bus of the Hades.
- */
-#ifdef CONFIG_HADES
-#define virt_to_bus(a) (virt_to_phys(a) + (MACH_IS_HADES ? 0x80000000 : 0))
-#define bus_to_virt(a) (phys_to_virt((a) - (MACH_IS_HADES ? 0x80000000 : 0)))
-#else
-#define virt_to_bus virt_to_phys
-#define bus_to_virt phys_to_virt
-#endif
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.119/linux/include/asm-m68k/linux_logo.h linux/include/asm-m68k/linux_logo.h
--- v2.1.119/linux/include/asm-m68k/linux_logo.h	Thu Jul 16 18:09:29 1998
+++ linux/include/asm-m68k/linux_logo.h	Wed Sep  2 09:39:18 1998
@@ -26,9 +26,1422 @@
 #define LINUX_LOGO_COLORS 221
 
 #ifdef INCLUDE_LINUX_LOGO_DATA
+unsigned char linux_logo_red[] __initdata = {
+  0xF3, 0xF6, 0xF8, 0xF7, 0xEF, 0xE7, 0xE5, 0xE3,
+  0xCA, 0xD4, 0xDD, 0xC8, 0xC7, 0xC4, 0xC2, 0xE5,
+  0xF1, 0xED, 0xEE, 0xE6, 0xC6, 0xDA, 0xDD, 0xE5,
+  0xD9, 0xC6, 0xE3, 0xD0, 0xC6, 0xBA, 0xB0, 0xB6,
+  0xBB, 0xBE, 0xB9, 0xB8, 0xB3, 0xB2, 0xB0, 0xAD,
+  0xAC, 0xA9, 0xA8, 0xA6, 0xA4, 0xA1, 0xA0, 0x9D,
+  0xA0, 0x9F, 0x9E, 0x9C, 0x9B, 0x99, 0x9A, 0x99,
+  0x98, 0x95, 0x96, 0x94, 0x93, 0x92, 0x8F, 0x8D,
+  0x8C, 0x8A, 0x87, 0x86, 0x83, 0x81, 0x0D, 0x03,
+  0x66, 0x44, 0x24, 0x08, 0xD6, 0xE6, 0xE9, 0xE6,
+  0xE7, 0xCA, 0xDC, 0xDB, 0xD5, 0xD0, 0xC9, 0xE2,
+  0xD5, 0xC6, 0xC4, 0xB3, 0xB2, 0xB9, 0xA9, 0x9A,
+  0xB2, 0x9D, 0xE8, 0xEC, 0xF5, 0xF5, 0xF4, 0xF4,
+  0xEC, 0xEE, 0xF0, 0xF5, 0xE0, 0xD6, 0xC5, 0xC2,
+  0xD9, 0xD5, 0xD8, 0xD6, 0xF6, 0xF4, 0xED, 0xEC,
+  0xEB, 0xF1, 0xF6, 0xF5, 0xF5, 0xEE, 0xEF, 0xEC,
+  0xE7, 0xE3, 0xE6, 0xD6, 0xDD, 0xC3, 0xD6, 0xD7,
+  0xCD, 0xCA, 0xC3, 0xAC, 0x95, 0x99, 0xB7, 0xA3,
+  0x8B, 0x88, 0x95, 0x8A, 0x94, 0xD2, 0xCC, 0xC4,
+  0xA8, 0x8E, 0x8F, 0xAE, 0xB8, 0xAC, 0xB6, 0xB4,
+  0xAD, 0xA5, 0xA0, 0x9B, 0x8B, 0xA3, 0x94, 0x87,
+  0x85, 0x89, 0x53, 0x80, 0x7D, 0x7C, 0x7A, 0x78,
+  0x76, 0x71, 0x73, 0x6E, 0x6B, 0x67, 0x65, 0x62,
+  0x4B, 0x5B, 0x5F, 0x53, 0x56, 0x52, 0x4F, 0x46,
+  0x42, 0x0F, 0x75, 0x78, 0x7D, 0x72, 0x5F, 0x6E,
+  0x7A, 0x75, 0x6A, 0x58, 0x48, 0x4F, 0x00, 0x2B,
+  0x37, 0x3E, 0x32, 0x33, 0x25, 0x2C, 0x3B, 0x11,
+  0x1D, 0x14, 0x06, 0x02, 0x00
+};
 
-#define INCLUDE_LINUX_LOGO16
-#include <linux/linux_logo.h>
+unsigned char linux_logo_green[] __initdata = {
+  0xF3, 0xF6, 0xF8, 0xF7, 0xEF, 0xE7, 0xE5, 0xE3,
+  0xCA, 0xD4, 0xDD, 0xC8, 0xC7, 0xC4, 0xC2, 0xD3,
+  0xDA, 0xD4, 0xD7, 0xCC, 0xC1, 0xCC, 0xCB, 0xC9,
+  0xC5, 0xBC, 0xBC, 0xBB, 0xB7, 0xA5, 0xB0, 0xB6,
+  0xBB, 0xBE, 0xB9, 0xB8, 0xB3, 0xB2, 0xAD, 0xAD,
+  0xAC, 0xA9, 0xA8, 0xA6, 0xA4, 0xA1, 0xA0, 0x95,
+  0xA0, 0x9F, 0x9E, 0x9C, 0x9B, 0x99, 0x9A, 0x99,
+  0x98, 0x95, 0x96, 0x94, 0x93, 0x92, 0x8F, 0x8D,
+  0x8C, 0x8A, 0x87, 0x86, 0x83, 0x81, 0x08, 0x02,
+  0x53, 0x2E, 0x19, 0x06, 0xC6, 0xC8, 0xCF, 0xBD,
+  0xB3, 0xB6, 0xB4, 0xAB, 0xA5, 0xA3, 0x9B, 0xB6,
+  0xA7, 0x99, 0x92, 0xA4, 0x9E, 0x9D, 0x98, 0x8C,
+  0x8A, 0x86, 0xCD, 0xCC, 0xC9, 0xD7, 0xCA, 0xC4,
+  0xCA, 0xC3, 0xC7, 0xC3, 0xC8, 0xB4, 0x91, 0x8E,
+  0x8A, 0x82, 0x87, 0x85, 0xBD, 0xBF, 0xB6, 0xBC,
+  0xAE, 0xB7, 0xBC, 0xB8, 0xBF, 0xB6, 0xBC, 0xB5,
+  0xAB, 0xA6, 0xAD, 0xB2, 0xA5, 0x87, 0x9C, 0x96,
+  0x95, 0x8E, 0x87, 0x8F, 0x86, 0x86, 0x8E, 0x80,
+  0x7A, 0x70, 0x7B, 0x78, 0x78, 0x7F, 0x77, 0x6F,
+  0x70, 0x76, 0x59, 0x77, 0x68, 0x64, 0x7B, 0x7C,
+  0x75, 0x6D, 0x77, 0x69, 0x65, 0x5F, 0x5B, 0x54,
+  0x4F, 0x5B, 0x39, 0x80, 0x7D, 0x7C, 0x7A, 0x78,
+  0x76, 0x71, 0x73, 0x6E, 0x6B, 0x67, 0x65, 0x62,
+  0x4B, 0x5B, 0x5F, 0x53, 0x56, 0x52, 0x4F, 0x46,
+  0x42, 0x0B, 0x69, 0x66, 0x64, 0x57, 0x4A, 0x4E,
+  0x55, 0x4B, 0x46, 0x3B, 0x30, 0x33, 0x00, 0x2B,
+  0x37, 0x3E, 0x32, 0x33, 0x25, 0x2C, 0x29, 0x0D,
+  0x1D, 0x14, 0x06, 0x02, 0x00
+};
+
+unsigned char linux_logo_blue[] __initdata = {
+  0xF3, 0xF6, 0xF8, 0xF7, 0xEF, 0xEE, 0xE5, 0xDE,
+  0xD7, 0xD3, 0xDD, 0xC8, 0xC7, 0xC4, 0xC2, 0xB5,
+  0xB0, 0xA6, 0xAC, 0x9B, 0xB5, 0xB5, 0xAE, 0x84,
+  0x90, 0xA9, 0x81, 0x8D, 0x96, 0x86, 0xB0, 0xB6,
+  0xBB, 0xBE, 0xB9, 0xB8, 0xB3, 0xB2, 0xA7, 0xAD,
+  0xAC, 0xA9, 0xA8, 0xA6, 0xA4, 0xA1, 0xA5, 0x87,
+  0xA0, 0x9F, 0x9E, 0x9C, 0x9B, 0x9A, 0x9A, 0x99,
+  0x98, 0x95, 0x96, 0x94, 0x93, 0x92, 0x8F, 0x8D,
+  0x8C, 0x8A, 0x87, 0x86, 0x83, 0x81, 0xC8, 0xD7,
+  0x9B, 0x8E, 0x8C, 0xB2, 0x77, 0x77, 0x4E, 0x77,
+  0x69, 0x71, 0x78, 0x6B, 0x65, 0x66, 0x64, 0x59,
+  0x5C, 0x5A, 0x48, 0x72, 0x7B, 0x6B, 0x67, 0x6E,
+  0x42, 0x5B, 0x29, 0x36, 0x25, 0x10, 0x17, 0x14,
+  0x19, 0x16, 0x13, 0x0E, 0x08, 0x2E, 0x2E, 0x3D,
+  0x24, 0x24, 0x24, 0x24, 0x13, 0x12, 0x14, 0x14,
+  0x0E, 0x08, 0x0D, 0x0F, 0x08, 0x0D, 0x0E, 0x08,
+  0x08, 0x0C, 0x06, 0x06, 0x07, 0x16, 0x07, 0x0E,
+  0x08, 0x0A, 0x07, 0x0D, 0x2D, 0x3E, 0x09, 0x4E,
+  0x68, 0x52, 0x56, 0x58, 0x4B, 0x22, 0x20, 0x20,
+  0x27, 0x39, 0x28, 0x19, 0x1E, 0x1E, 0x08, 0x06,
+  0x07, 0x09, 0x08, 0x08, 0x05, 0x1D, 0x1F, 0x17,
+  0x18, 0x06, 0x79, 0x80, 0x7D, 0x7C, 0x7A, 0x78,
+  0x76, 0x71, 0x73, 0x6E, 0x6B, 0x68, 0x65, 0x62,
+  0x4B, 0x5B, 0x5F, 0x55, 0x56, 0x52, 0x4F, 0x46,
+  0x42, 0x5A, 0x14, 0x23, 0x3D, 0x2B, 0x21, 0x14,
+  0x06, 0x04, 0x03, 0x07, 0x09, 0x13, 0x2A, 0x3A,
+  0x37, 0x3E, 0x32, 0x33, 0x25, 0x2C, 0x07, 0x09,
+  0x1D, 0x14, 0x06, 0x02, 0x00
+};
+
+unsigned char linux_logo[] __initdata = {
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x58, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x57,
+  0x58, 0x58, 0x59, 0x5C, 0x5D, 0x5F, 0x60, 0x61,
+  0x62, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63,
+  0x61, 0x61, 0x61, 0x61, 0x61, 0x60, 0x5E, 0x5E,
+  0x5E, 0x5D, 0x5D, 0x5C, 0x5D, 0x5B, 0x58, 0x58,
+  0x58, 0x57, 0x58, 0x57, 0x57, 0x57, 0x57, 0x58,
+  0x57, 0x57, 0x58, 0x57, 0x57, 0x58, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x58, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x57,
+  0x54, 0x56, 0x57, 0x67, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x67, 0x4C,
+  0x4A, 0x49, 0x4A, 0x49, 0x4A, 0x49, 0x49, 0x4A,
+  0x4A, 0x4B, 0x4B, 0x4B, 0x4C, 0x50, 0x51, 0x52,
+  0x54, 0x54, 0x56, 0x57, 0x57, 0x57, 0x57, 0x58,
+  0x57, 0x57, 0x58, 0x57, 0x57, 0x58, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x58, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x58, 0x56, 0x56, 0x53,
+  0x52, 0x53, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFB, 0xFB, 0xFB,
+  0x4B, 0x4B, 0x4B, 0x4A, 0x49, 0x4A, 0x4A, 0x49,
+  0x49, 0x49, 0x48, 0x49, 0x49, 0x4A, 0x4A, 0x4B,
+  0x4C, 0x4D, 0x52, 0x54, 0x56, 0x55, 0x57, 0x58,
+  0x57, 0x57, 0x58, 0x57, 0x57, 0x58, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x58, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50,
+  0x50, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF0, 0xF4, 0xFB,
+  0xFC, 0x67, 0x53, 0x50, 0x4D, 0x4C, 0x4C, 0x4C,
+  0x4B, 0x4A, 0x4A, 0x48, 0x49, 0x48, 0x48, 0x49,
+  0x49, 0x49, 0x4B, 0x4C, 0x50, 0x52, 0x53, 0x56,
+  0x57, 0x57, 0x58, 0x57, 0x57, 0x58, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x58, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x55, 0x54, 0x53, 0x51, 0x51, 0x50, 0x4C, 0x4D,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xF4, 0xD2, 0xD7, 0xF5,
+  0xFC, 0xFC, 0x5D, 0x5D, 0x5C, 0x5C, 0x59, 0x58,
+  0x58, 0x56, 0x52, 0x4C, 0x4B, 0x4A, 0x4A, 0x48,
+  0x48, 0x48, 0x48, 0x48, 0x49, 0x4B, 0x4D, 0x51,
+  0x54, 0x56, 0x58, 0x57, 0x57, 0x58, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x58, 0x57, 0x57, 0x57, 0x55, 0x54,
+  0x53, 0x52, 0x51, 0x4D, 0x4D, 0x4D, 0x50, 0x50,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xF4, 0x64, 0xD9, 0xF5,
+  0xF9, 0xFC, 0xFC, 0x64, 0x63, 0x62, 0x61, 0x61,
+  0x61, 0x60, 0x5E, 0x5B, 0x5A, 0x54, 0x52, 0x4C,
+  0x4B, 0x49, 0x49, 0x47, 0x47, 0x48, 0x49, 0x4B,
+  0x4C, 0x51, 0x53, 0x56, 0x57, 0x58, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x58, 0x57, 0x57, 0x55, 0x53, 0x53,
+  0x51, 0x50, 0x50, 0x50, 0x50, 0x50, 0x53, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xF4, 0xF5, 0xF9, 0xFC,
+  0xFC, 0xFC, 0xFC, 0x64, 0x64, 0x64, 0x64, 0x64,
+  0x64, 0x64, 0x64, 0x63, 0x61, 0x61, 0x5E, 0x59,
+  0x55, 0x52, 0x4C, 0x4A, 0x49, 0x47, 0x48, 0x48,
+  0x49, 0x4B, 0x4D, 0x51, 0x54, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x58, 0x55, 0x54, 0x54, 0x52, 0x51,
+  0x51, 0x51, 0x51, 0x51, 0x53, 0x54, 0x59, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xF7, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0x60, 0x60, 0x60, 0x61,
+  0x62, 0x63, 0x64, 0x64, 0x65, 0x65, 0x64, 0x63,
+  0x61, 0x5E, 0x59, 0x56, 0x4D, 0x4B, 0x48, 0x48,
+  0x48, 0x48, 0x49, 0x4B, 0x50, 0x53, 0x56, 0x56,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x56, 0x54, 0x53, 0x52, 0x51, 0x51,
+  0x51, 0x52, 0x53, 0x55, 0x59, 0x5D, 0x5E, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFB, 0xFB, 0xFB, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0x4C, 0x4E, 0x51, 0x52,
+  0x57, 0x5A, 0x5E, 0x60, 0x61, 0x63, 0x65, 0xCB,
+  0x64, 0x64, 0x63, 0x60, 0x5C, 0x57, 0x50, 0x4B,
+  0x48, 0x47, 0x47, 0x47, 0x4A, 0x4C, 0x52, 0x53,
+  0x54, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x55, 0x54, 0x53, 0x53, 0x51, 0x52, 0x52, 0x53,
+  0x53, 0x57, 0x5A, 0x5D, 0x5E, 0x5E, 0x60, 0xFC,
+  0xFC, 0xFC, 0xFB, 0xF9, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFA, 0xF9, 0xF5, 0xFB, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFB, 0x45, 0x3F, 0x3F,
+  0x45, 0x48, 0x4B, 0x4D, 0x54, 0x5A, 0x5E, 0x61,
+  0x63, 0xCB, 0xCB, 0x65, 0x64, 0x62, 0x5E, 0x57,
+  0x50, 0x4B, 0x48, 0x47, 0x47, 0x48, 0x4B, 0x4D,
+  0x51, 0x56, 0x56, 0x57, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x55,
+  0x54, 0x54, 0x53, 0x53, 0x52, 0x53, 0x54, 0x57,
+  0x59, 0x5C, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0xFC,
+  0xFC, 0xFA, 0xFC, 0xFA, 0xE0, 0xFC, 0xFC, 0xFC,
+  0xFB, 0xFB, 0xFB, 0xDF, 0xD8, 0xF9, 0xE0, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFB, 0x4C, 0x4A, 0x48,
+  0x48, 0x3E, 0x44, 0x43, 0x3F, 0x47, 0x4B, 0x52,
+  0x5A, 0x5E, 0x62, 0x64, 0xCB, 0xCB, 0x64, 0x61,
+  0x5E, 0x57, 0x4D, 0x49, 0x47, 0x47, 0x48, 0x4A,
+  0x4C, 0x52, 0x54, 0x56, 0x57, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x55,
+  0x54, 0x53, 0x53, 0x54, 0x54, 0x55, 0x58, 0x5B,
+  0x5C, 0x5D, 0x5E, 0x5D, 0x5D, 0x5B, 0x58, 0xFC,
+  0xFC, 0xD8, 0x4C, 0x60, 0xFC, 0xF5, 0xFC, 0xFC,
+  0xFC, 0xF7, 0x5F, 0x48, 0x48, 0x2C, 0xF8, 0xF9,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x4B, 0x4A, 0x49,
+  0x49, 0x49, 0x49, 0x47, 0x3E, 0x44, 0x42, 0x3F,
+  0x3E, 0x4B, 0x54, 0x5C, 0x61, 0x64, 0xCB, 0xCB,
+  0x64, 0x61, 0x5D, 0x53, 0x4B, 0x49, 0x47, 0x47,
+  0x49, 0x4B, 0x50, 0x53, 0x56, 0x57, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x57, 0x55, 0x55, 0x54,
+  0x53, 0x53, 0x54, 0x56, 0x58, 0x5A, 0x5B, 0x5D,
+  0x5D, 0x5D, 0x5C, 0x5A, 0x54, 0x52, 0x4C, 0xFC,
+  0xF7, 0x4E, 0x2D, 0x29, 0x4E, 0xFC, 0xFC, 0xFC,
+  0xFB, 0x5F, 0x26, 0x24, 0x20, 0x2E, 0x65, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x45, 0x3F, 0x45,
+  0x3E, 0x47, 0x47, 0x47, 0x47, 0x47, 0x3E, 0x44,
+  0x43, 0x40, 0x44, 0x49, 0x51, 0x5C, 0x62, 0x64,
+  0xCB, 0xCB, 0x63, 0x60, 0x58, 0x50, 0x49, 0x48,
+  0x48, 0x48, 0x4A, 0x4D, 0x53, 0x54, 0x57, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x55, 0x54, 0x54, 0x54,
+  0x54, 0x54, 0x55, 0x57, 0x59, 0x5B, 0x5C, 0x5D,
+  0x5C, 0x5A, 0x54, 0x51, 0x4C, 0x4C, 0x54, 0xFC,
+  0xF9, 0x23, 0xDB, 0x2D, 0x23, 0xFA, 0xFB, 0xFA,
+  0xF5, 0x27, 0x21, 0xD9, 0xF8, 0x20, 0x21, 0xFB,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x5D, 0x58, 0x55,
+  0x50, 0x48, 0x45, 0x43, 0x44, 0x44, 0x45, 0x45,
+  0x3E, 0x3F, 0x43, 0x41, 0x3F, 0x48, 0x52, 0x5D,
+  0x63, 0x65, 0xCB, 0x65, 0x61, 0x5D, 0x52, 0x4B,
+  0x48, 0x47, 0x47, 0x49, 0x4C, 0x51, 0x54, 0x57,
+  0x57, 0x57, 0x57, 0x57, 0x55, 0x54, 0x54, 0x54,
+  0x54, 0x58, 0x5A, 0x59, 0x5B, 0x5B, 0x5B, 0x5A,
+  0x55, 0x52, 0x4D, 0x4D, 0x55, 0x5B, 0x5D, 0xFC,
+  0xF1, 0xF9, 0xFC, 0xD4, 0x21, 0xCC, 0xF7, 0xF8,
+  0xF2, 0x21, 0xD9, 0xFC, 0xF2, 0xFB, 0x21, 0x45,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFB, 0xD1, 0xD0, 0xCD,
+  0xCC, 0x63, 0x5E, 0x58, 0x50, 0x47, 0x43, 0x3F,
+  0x3F, 0x3F, 0x3F, 0x3F, 0x40, 0x41, 0x3F, 0x4A,
+  0x56, 0x5E, 0x64, 0xCB, 0x65, 0x63, 0x5E, 0x56,
+  0x4C, 0x48, 0x47, 0x47, 0x49, 0x4C, 0x51, 0x54,
+  0x58, 0x57, 0x57, 0x57, 0x57, 0x55, 0x54, 0x54,
+  0x57, 0x5A, 0x5A, 0x5C, 0x5B, 0x5A, 0x58, 0x54,
+  0x51, 0x4C, 0x55, 0x5D, 0x5D, 0x5B, 0x54, 0xFC,
+  0xF0, 0xF9, 0xFC, 0x65, 0x45, 0xCD, 0xFB, 0xFB,
+  0xF8, 0x26, 0xFB, 0xFC, 0xFC, 0xFC, 0x21, 0x27,
+  0xFB, 0xFC, 0xFC, 0xFC, 0xFB, 0xD7, 0x35, 0x34,
+  0x2F, 0x35, 0x36, 0x2F, 0x2F, 0x36, 0x2F, 0x2F,
+  0x36, 0x36, 0x35, 0x35, 0x43, 0x42, 0x41, 0x2E,
+  0x45, 0x4C, 0x5B, 0x62, 0x65, 0xCC, 0x64, 0x60,
+  0x58, 0x4D, 0x49, 0x47, 0x47, 0x49, 0x4C, 0x51,
+  0x58, 0x57, 0x57, 0x57, 0x57, 0x57, 0x55, 0x57,
+  0x58, 0x5A, 0x5A, 0x5B, 0x5A, 0x55, 0x54, 0x51,
+  0x53, 0x5C, 0x5D, 0x5D, 0x54, 0x4B, 0x4D, 0xFC,
+  0xFC, 0x44, 0xFC, 0xFB, 0x7B, 0xAB, 0xA8, 0xAE,
+  0xAB, 0x7F, 0xFC, 0xFC, 0xFB, 0xFB, 0x22, 0x2A,
+  0xFC, 0xFC, 0xFC, 0xFC, 0x36, 0x2F, 0x30, 0x30,
+  0x32, 0x30, 0x32, 0x30, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x30, 0x30, 0x30, 0x2F, 0x2F, 0x40, 0x41,
+  0x2E, 0x40, 0x48, 0x56, 0x5F, 0x64, 0xCC, 0x65,
+  0x61, 0x59, 0x50, 0x49, 0x47, 0x47, 0x49, 0x4C,
+  0x5A, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58,
+  0x5A, 0x5A, 0x5A, 0x58, 0x55, 0x52, 0x51, 0x5A,
+  0x5D, 0x5D, 0x57, 0x4C, 0x51, 0x54, 0x5D, 0xFC,
+  0xFC, 0x2A, 0xFC, 0xC9, 0xAA, 0x8B, 0x8A, 0x8C,
+  0xAB, 0x8C, 0x8C, 0xFB, 0xFB, 0x23, 0x20, 0xF1,
+  0xFC, 0xFC, 0xFC, 0x3B, 0x33, 0x33, 0x32, 0x32,
+  0x31, 0x32, 0x30, 0x32, 0x32, 0x32, 0x32, 0x30,
+  0x31, 0x31, 0x31, 0x32, 0x33, 0x33, 0x3C, 0x41,
+  0x41, 0x2E, 0x2D, 0x45, 0x4D, 0x5D, 0x63, 0xCC,
+  0x65, 0x62, 0x5D, 0x51, 0x49, 0x47, 0x47, 0x4A,
+  0x59, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58,
+  0x5A, 0x5A, 0x58, 0x55, 0x53, 0x53, 0x5C, 0x5E,
+  0x59, 0x51, 0x4E, 0x54, 0x59, 0x5E, 0x62, 0xFC,
+  0xFC, 0xDB, 0xAA, 0xA1, 0x95, 0x9C, 0x8C, 0x88,
+  0x82, 0x83, 0x83, 0x8C, 0x88, 0xAE, 0xB9, 0xFB,
+  0xFC, 0xFC, 0xFC, 0x3C, 0x3B, 0x72, 0x38, 0x33,
+  0x33, 0x33, 0x31, 0x33, 0x31, 0x31, 0x31, 0x31,
+  0x33, 0x33, 0x38, 0x33, 0x72, 0x3B, 0x44, 0x2E,
+  0x41, 0x2E, 0x2E, 0x2D, 0x43, 0x4B, 0x5B, 0x63,
+  0xCB, 0xCC, 0x63, 0x5D, 0x51, 0x49, 0x47, 0x49,
+  0x5C, 0x58, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58,
+  0x58, 0x58, 0x57, 0x53, 0x58, 0x5D, 0x5E, 0x55,
+  0x51, 0x53, 0x58, 0x5E, 0x60, 0x63, 0x64, 0xFC,
+  0xFC, 0xC0, 0xA6, 0x9D, 0x8B, 0x9C, 0x8C, 0x8C,
+  0x6E, 0x83, 0x88, 0x8C, 0x8C, 0x8C, 0x83, 0xE8,
+  0xFB, 0xFC, 0xFC, 0xFC, 0x33, 0x70, 0x70, 0x6F,
+  0x6F, 0x6F, 0x6F, 0x3A, 0x6F, 0x6D, 0x6F, 0x6F,
+  0x70, 0x6F, 0x6F, 0x70, 0x6F, 0x32, 0x5A, 0x48,
+  0x41, 0x2D, 0x2D, 0x2D, 0x2C, 0x41, 0x49, 0x5A,
+  0x62, 0xCB, 0xCB, 0x63, 0x5D, 0x50, 0x49, 0x4A,
+  0x5C, 0x58, 0x58, 0x57, 0x55, 0x57, 0x57, 0x57,
+  0x57, 0x55, 0x56, 0x59, 0x5E, 0x5C, 0x52, 0x53,
+  0x55, 0x5B, 0x5E, 0x61, 0x63, 0x64, 0x63, 0xFC,
+  0xE8, 0xBF, 0xA4, 0x99, 0x9C, 0x8C, 0x88, 0x88,
+  0x6E, 0x88, 0x8C, 0x8C, 0x8C, 0xC2, 0xA6, 0xC4,
+  0xFC, 0xFC, 0xFC, 0xFC, 0x36, 0x3A, 0x6F, 0x70,
+  0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+  0x70, 0x70, 0x70, 0x70, 0x37, 0x32, 0xCD, 0x5E,
+  0x4C, 0x43, 0x2C, 0x2D, 0x2D, 0x2C, 0x2E, 0x47,
+  0x57, 0x61, 0x65, 0xCC, 0x63, 0x5C, 0x50, 0x4D,
+  0x5C, 0x5A, 0x57, 0x55, 0x55, 0x55, 0x58, 0x58,
+  0x55, 0x54, 0x5B, 0x5E, 0x5D, 0x53, 0x53, 0x55,
+  0x5D, 0x5E, 0x61, 0x61, 0x61, 0x61, 0x5E, 0xFC,
+  0xEA, 0xBE, 0xA4, 0x9B, 0x8B, 0x85, 0x8C, 0x6E,
+  0x8C, 0x8C, 0x8C, 0xA3, 0xAA, 0xA4, 0xA4, 0xE9,
+  0xFB, 0xFC, 0xFC, 0xFC, 0x36, 0x6D, 0x70, 0x73,
+  0x70, 0x70, 0x70, 0x73, 0x73, 0x73, 0x73, 0x70,
+  0x70, 0x70, 0x73, 0x70, 0x37, 0x38, 0xD1, 0xCF,
+  0x61, 0x4D, 0x44, 0x2C, 0x2D, 0x2E, 0x2C, 0x2E,
+  0x3E, 0x56, 0x61, 0xCB, 0xCC, 0x62, 0x5B, 0x57,
+  0x59, 0x58, 0x55, 0x54, 0x54, 0x55, 0x58, 0x58,
+  0x58, 0x5B, 0x5E, 0x5B, 0x53, 0x55, 0x55, 0x5C,
+  0x5E, 0x61, 0x61, 0x60, 0x5D, 0x5A, 0x4E, 0xFC,
+  0xFC, 0xEA, 0xAA, 0x9C, 0x8A, 0x85, 0x82, 0x8C,
+  0x8C, 0xA8, 0xEB, 0xA8, 0xA4, 0xA4, 0xAA, 0xFC,
+  0xFC, 0xFC, 0x64, 0xFB, 0x39, 0x31, 0x72, 0x78,
+  0x73, 0x78, 0x73, 0x74, 0x74, 0x74, 0x74, 0x73,
+  0x78, 0x70, 0x73, 0x73, 0x33, 0xCC, 0xD2, 0xD1,
+  0xCE, 0x62, 0x53, 0x3F, 0x2D, 0x2D, 0x41, 0x2C,
+  0x2E, 0x3E, 0x56, 0x62, 0xCB, 0xCB, 0x61, 0x5D,
+  0x54, 0x54, 0x54, 0x54, 0x56, 0x58, 0x58, 0x58,
+  0x5C, 0x5E, 0x5A, 0x55, 0x58, 0x58, 0x5B, 0x5E,
+  0x61, 0x5E, 0x5D, 0x5A, 0x52, 0x55, 0xCD, 0xFC,
+  0xFC, 0x34, 0xC9, 0xE8, 0xA8, 0xAE, 0xC2, 0xE8,
+  0xC3, 0xA6, 0xA7, 0xA6, 0xAA, 0x78, 0x2E, 0x42,
+  0xFC, 0xFC, 0xD2, 0x64, 0xF8, 0x31, 0x72, 0x73,
+  0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x74, 0x73,
+  0x73, 0x73, 0x73, 0x72, 0x33, 0x5C, 0x64, 0xD2,
+  0xD1, 0xCF, 0x63, 0x54, 0x3F, 0x2C, 0x41, 0x41,
+  0x2C, 0x2E, 0x47, 0x58, 0x63, 0xCB, 0xCB, 0x62,
+  0x52, 0x53, 0x53, 0x56, 0x58, 0x58, 0x5A, 0x5B,
+  0x5E, 0x5A, 0x57, 0x58, 0x58, 0x58, 0x60, 0x60,
+  0x5D, 0x5A, 0x55, 0x4E, 0x64, 0xD2, 0xD1, 0xFC,
+  0xFC, 0x41, 0x3E, 0xC1, 0xC0, 0xA3, 0xA6, 0xA7,
+  0xA7, 0xA9, 0xAA, 0xB8, 0x2E, 0x3F, 0x2C, 0x41,
+  0xFC, 0xFC, 0xF7, 0xCE, 0xCD, 0x36, 0x72, 0x73,
+  0x74, 0x75, 0x78, 0x75, 0x75, 0x75, 0x74, 0x74,
+  0x74, 0x74, 0x78, 0x72, 0x6D, 0x49, 0x59, 0xCB,
+  0xD1, 0xD1, 0xD2, 0xCB, 0x56, 0x3F, 0x2C, 0x41,
+  0x40, 0x2D, 0x2E, 0x49, 0x5B, 0x64, 0xCC, 0x64,
+  0x51, 0x53, 0x53, 0x55, 0x58, 0x59, 0x5B, 0x5E,
+  0x59, 0x58, 0x58, 0x58, 0x55, 0x60, 0x60, 0x5C,
+  0x5A, 0x53, 0x5B, 0xD0, 0xD3, 0xD3, 0xD3, 0xFB,
+  0xFC, 0x40, 0x41, 0x45, 0xC4, 0xC0, 0xBE, 0xBE,
+  0xC1, 0xC0, 0x3C, 0x47, 0x2E, 0x21, 0x22, 0x20,
+  0x65, 0xFC, 0xFC, 0xFC, 0xFC, 0x6D, 0x72, 0x75,
+  0x78, 0x76, 0x75, 0x79, 0x76, 0x76, 0x76, 0x76,
+  0x75, 0x75, 0x75, 0x72, 0x6D, 0x2E, 0x48, 0x5D,
+  0xCE, 0xD1, 0xD4, 0xD3, 0xCB, 0x56, 0x43, 0x2C,
+  0x42, 0x43, 0x2E, 0x2E, 0x4A, 0x5D, 0x64, 0x64,
+  0x50, 0x52, 0x56, 0x58, 0x5C, 0x5D, 0x5E, 0x5D,
+  0x5A, 0x58, 0x58, 0x55, 0x61, 0x60, 0x58, 0x58,
+  0x4E, 0x61, 0xD1, 0xD4, 0xD4, 0xD1, 0xEE, 0xFC,
+  0xFC, 0x2B, 0x29, 0x2E, 0x3F, 0xB0, 0xAD, 0x81,
+  0x46, 0x2D, 0x46, 0x2C, 0x24, 0x22, 0x22, 0x23,
+  0x25, 0xFC, 0xFC, 0xFC, 0xFC, 0x6E, 0x73, 0x76,
+  0x76, 0x79, 0x79, 0x79, 0x76, 0x76, 0x79, 0x76,
+  0x79, 0x79, 0x79, 0x74, 0x3F, 0x41, 0x2C, 0x48,
+  0x5F, 0xCF, 0xD5, 0xD7, 0xD6, 0xCD, 0x57, 0x40,
+  0x2E, 0x3F, 0x44, 0x2E, 0x41, 0x4C, 0x60, 0x61,
+  0x51, 0x53, 0x58, 0x5C, 0x5D, 0x5E, 0x5D, 0x5C,
+  0x58, 0x57, 0x54, 0x5F, 0x5E, 0x55, 0x55, 0x52,
+  0x64, 0xD4, 0xD5, 0xD4, 0xD1, 0x5D, 0xFA, 0xFB,
+  0xF4, 0x21, 0x24, 0x41, 0x40, 0x44, 0x2E, 0x2E,
+  0x42, 0x41, 0x2A, 0x24, 0x22, 0x22, 0x22, 0x22,
+  0x23, 0xD9, 0xFC, 0xFC, 0xFC, 0xFC, 0xE5, 0xB8,
+  0x8F, 0x8F, 0x7A, 0x8F, 0x7A, 0x8F, 0x7A, 0x8F,
+  0x8F, 0x8F, 0xB8, 0xE5, 0x3F, 0x3E, 0x43, 0x2C,
+  0x48, 0x61, 0xD1, 0xD7, 0xD9, 0xD7, 0xD0, 0x57,
+  0x41, 0x2E, 0x3E, 0x44, 0x2D, 0x40, 0x52, 0x5D,
+  0x53, 0x55, 0x59, 0x5D, 0x5E, 0x5E, 0x5D, 0x5A,
+  0x57, 0x53, 0x5E, 0x5E, 0x54, 0x53, 0x54, 0x65,
+  0xD5, 0xD6, 0xD4, 0xCE, 0x53, 0xFB, 0xF9, 0xFC,
+  0x24, 0x22, 0x23, 0x23, 0x41, 0x42, 0x2E, 0x40,
+  0x2B, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+  0x23, 0x23, 0xFC, 0xFC, 0xFC, 0xFC, 0xE7, 0xBD,
+  0xB5, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+  0x93, 0xB5, 0xC6, 0xEB, 0x2D, 0x47, 0x4A, 0x47,
+  0x2C, 0x3E, 0x61, 0xD4, 0xDC, 0xDC, 0xDA, 0xCF,
+  0x54, 0x41, 0x41, 0x3E, 0x45, 0x2C, 0x3F, 0x4A,
+  0x58, 0x5A, 0x5C, 0x5F, 0x60, 0x5E, 0x5D, 0x57,
+  0x51, 0x5D, 0x5D, 0x51, 0x53, 0x53, 0xCB, 0xD5,
+  0xD6, 0xD5, 0x63, 0x55, 0xFC, 0xFC, 0xFC, 0x2C,
+  0x23, 0x22, 0x23, 0x22, 0x20, 0x2D, 0x2C, 0x26,
+  0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+  0x22, 0x21, 0xF0, 0xFC, 0xFC, 0xFC, 0xE2, 0xC6,
+  0xB5, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+  0x93, 0x93, 0xC7, 0xE3, 0x3E, 0x2E, 0x49, 0x52,
+  0x4C, 0x41, 0x44, 0x62, 0xD6, 0xDE, 0xDE, 0xD9,
+  0xD0, 0x51, 0x2E, 0x40, 0x47, 0x44, 0x2C, 0x42,
+  0x5D, 0x5D, 0x5F, 0x60, 0x60, 0x5D, 0x57, 0x51,
+  0x58, 0x5D, 0x4E, 0x52, 0x55, 0x64, 0xD5, 0xD6,
+  0xD4, 0x61, 0x59, 0x6B, 0xFC, 0xFC, 0xFC, 0x21,
+  0x23, 0x22, 0x23, 0x22, 0x23, 0x21, 0x23, 0x22,
+  0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+  0x22, 0x21, 0x24, 0xFC, 0xFC, 0xFC, 0xE2, 0xC7,
+  0xB5, 0x90, 0x93, 0x93, 0x93, 0x90, 0x93, 0x93,
+  0x90, 0xB5, 0xC8, 0xE4, 0x5F, 0x45, 0x2E, 0x4D,
+  0x57, 0x57, 0x44, 0x43, 0x63, 0xDA, 0xDF, 0xDF,
+  0xD9, 0xCE, 0x4C, 0x2C, 0x3F, 0x3E, 0x40, 0x40,
+  0x60, 0x5E, 0x61, 0x61, 0x5E, 0x5B, 0x53, 0x52,
+  0x5C, 0x52, 0x52, 0x55, 0x61, 0xD4, 0xD5, 0xD1,
+  0x5E, 0x5B, 0x5C, 0xFB, 0xFC, 0xFC, 0x2A, 0x21,
+  0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x23, 0x22,
+  0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+  0x22, 0x22, 0x22, 0xFB, 0xFC, 0xFC, 0xB3, 0xC8,
+  0xB5, 0x90, 0x92, 0xB5, 0x93, 0x93, 0xB5, 0x93,
+  0x92, 0xB5, 0xC8, 0xB9, 0xD0, 0x5E, 0x44, 0x40,
+  0x52, 0x58, 0x57, 0x48, 0x40, 0x63, 0xD9, 0xE0,
+  0xE0, 0xD9, 0xCB, 0x49, 0x2D, 0x3F, 0x45, 0x3F,
+  0x63, 0x61, 0x62, 0x60, 0x5E, 0x55, 0x4D, 0x59,
+  0x53, 0x4E, 0x54, 0x5D, 0xD2, 0xD4, 0xD2, 0x5E,
+  0x5C, 0x5D, 0xFC, 0xFC, 0xFC, 0xF8, 0x29, 0x23,
+  0x23, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+  0x23, 0x22, 0x22, 0x23, 0x23, 0x23, 0x22, 0x22,
+  0x22, 0x22, 0x22, 0xF0, 0xFC, 0xFC, 0xB3, 0xC7,
+  0xB5, 0x93, 0xB5, 0x93, 0x93, 0x91, 0x93, 0x93,
+  0x91, 0xB5, 0xC7, 0xAD, 0xD6, 0xD2, 0x5E, 0x3F,
+  0x3F, 0x57, 0x57, 0x58, 0x4A, 0x41, 0x64, 0xDC,
+  0xF1, 0xDF, 0xDA, 0x61, 0x45, 0x2E, 0x43, 0x47,
+  0xCB, 0x63, 0x62, 0x5F, 0x58, 0x51, 0x53, 0x54,
+  0x4C, 0x52, 0x5C, 0xCD, 0xD3, 0xD2, 0x60, 0x5D,
+  0x5D, 0xFB, 0xFC, 0xFC, 0xFC, 0xDB, 0x49, 0x24,
+  0x21, 0x23, 0x23, 0x22, 0x26, 0x26, 0x2A, 0x24,
+  0x22, 0x23, 0x22, 0x21, 0x24, 0x26, 0x26, 0x2A,
+  0x29, 0x2B, 0x24, 0x25, 0xFC, 0xFC, 0xB3, 0xC5,
+  0x91, 0x91, 0x92, 0x91, 0x92, 0x92, 0x93, 0x93,
+  0x91, 0x93, 0xC6, 0xAD, 0xDC, 0xD9, 0xD4, 0x60,
+  0x43, 0x45, 0x58, 0x58, 0x57, 0x4B, 0x43, 0xCC,
+  0xDD, 0xF1, 0xD8, 0xD5, 0x5D, 0x43, 0x41, 0x47,
+  0xCD, 0x63, 0x62, 0x5D, 0x54, 0x4C, 0x55, 0x4B,
+  0x51, 0x58, 0x62, 0xD0, 0xD0, 0x62, 0x5D, 0x5D,
+  0x67, 0xFC, 0xFC, 0xFC, 0xFC, 0x58, 0x4E, 0x28,
+  0x2A, 0x20, 0x23, 0x22, 0x23, 0x2A, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x23, 0x25, 0x2A, 0x2E, 0x2D,
+  0x2E, 0x2E, 0x2E, 0x23, 0xFA, 0xFC, 0xB2, 0xBD,
+  0xB5, 0x90, 0x91, 0x93, 0x92, 0x90, 0x91, 0x93,
+  0x92, 0x91, 0xBD, 0xAD, 0xDE, 0xE0, 0xD8, 0xD7,
+  0x61, 0x40, 0x48, 0x58, 0x58, 0x58, 0x48, 0x44,
+  0xCF, 0xDE, 0xE0, 0xDD, 0xD0, 0x52, 0x41, 0x45,
+  0xCD, 0x63, 0x61, 0x58, 0x4D, 0x51, 0x4C, 0x4B,
+  0x54, 0x5D, 0xCC, 0xCE, 0x63, 0x61, 0x5D, 0x5D,
+  0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0x4B, 0x27, 0x21,
+  0x22, 0x22, 0x23, 0x22, 0x22, 0x24, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x20,
+  0x27, 0x2B, 0x41, 0x2B, 0x23, 0xFC, 0xB2, 0xB6,
+  0x93, 0x90, 0x92, 0xB5, 0x92, 0x90, 0xB5, 0x90,
+  0x92, 0x93, 0xBC, 0xAD, 0xDC, 0xF1, 0xF3, 0xF0,
+  0xD9, 0x61, 0x41, 0x4A, 0x58, 0x57, 0x57, 0x44,
+  0x49, 0xD2, 0xDD, 0xD8, 0xDA, 0x63, 0x4A, 0x45,
+  0xCC, 0x63, 0x5E, 0x52, 0x4B, 0x4C, 0x49, 0x51,
+  0x5C, 0x61, 0xCD, 0x65, 0x63, 0x5E, 0x4E, 0xCF,
+  0xFB, 0xFB, 0xF0, 0xFC, 0xD2, 0x2A, 0x22, 0x23,
+  0x22, 0x22, 0x23, 0x22, 0x22, 0x21, 0x22, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+  0x23, 0x22, 0x26, 0x41, 0x27, 0xF9, 0x81, 0xB7,
+  0xB5, 0x91, 0x92, 0xB5, 0x91, 0xB5, 0x93, 0xB5,
+  0x93, 0xB6, 0xB7, 0xB9, 0xCB, 0xD8, 0xF3, 0xF2,
+  0xF2, 0xDB, 0x61, 0x2D, 0x51, 0x58, 0x57, 0x58,
+  0x41, 0x51, 0xD4, 0xDB, 0xDC, 0xD1, 0x5B, 0x4C,
+  0xCB, 0x62, 0x59, 0x4C, 0x4A, 0x49, 0x4B, 0x55,
+  0x60, 0x64, 0xCC, 0x64, 0x5E, 0x55, 0x60, 0xE1,
+  0xFB, 0xF8, 0xFC, 0xFC, 0x21, 0x22, 0x22, 0x23,
+  0x22, 0x22, 0x23, 0x22, 0x22, 0x21, 0x22, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+  0x23, 0x22, 0x21, 0x24, 0x2D, 0x21, 0xB4, 0xBB,
+  0xB6, 0xB5, 0xB6, 0xB7, 0xB7, 0xB7, 0xB7, 0xB6,
+  0xB6, 0xB6, 0xBB, 0xB9, 0x45, 0xCB, 0xDF, 0xF3,
+  0xF3, 0xF3, 0xDB, 0x5E, 0x2C, 0x51, 0x58, 0x58,
+  0x52, 0x2D, 0x5C, 0xD4, 0xD9, 0xD5, 0x63, 0x58,
+  0x64, 0x60, 0x53, 0x49, 0x4A, 0x49, 0x52, 0x5C,
+  0x63, 0xCD, 0xCD, 0x63, 0x5C, 0x4E, 0x65, 0xFC,
+  0xFC, 0xF5, 0xFC, 0xD2, 0x23, 0x22, 0x22, 0x23,
+  0x22, 0x22, 0x23, 0x22, 0x22, 0x21, 0x22, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+  0x23, 0x22, 0x21, 0x22, 0x25, 0x29, 0xB3, 0xC7,
+  0xB5, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6,
+  0xB6, 0xB5, 0xC7, 0xAD, 0x57, 0x3F, 0xCB, 0xF0,
+  0xF3, 0xF3, 0xF2, 0xD9, 0x58, 0x41, 0x4C, 0x58,
+  0x57, 0x47, 0x42, 0x62, 0xD4, 0xD4, 0xCC, 0x60,
+  0x63, 0x5D, 0x50, 0x47, 0x48, 0x4B, 0x58, 0x60,
+  0xCC, 0xCE, 0xCD, 0x60, 0x53, 0x5C, 0x62, 0xFB,
+  0xF9, 0xFC, 0xFC, 0x21, 0x23, 0x22, 0x22, 0x23,
+  0x22, 0x22, 0x23, 0x23, 0x23, 0x21, 0x22, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+  0x23, 0x22, 0x23, 0x23, 0x22, 0x23, 0x81, 0xC7,
+  0xB7, 0xB7, 0xBC, 0xB7, 0xBC, 0xBC, 0xBC, 0xB7,
+  0xB7, 0xB7, 0xC8, 0x80, 0x58, 0x57, 0x40, 0xCE,
+  0xF3, 0xF2, 0xF2, 0xF0, 0xD5, 0x4C, 0x3F, 0x4B,
+  0x52, 0x50, 0x2D, 0x4B, 0x64, 0xD2, 0xCC, 0x61,
+  0x60, 0x58, 0x4A, 0x47, 0x47, 0x4C, 0x59, 0x64,
+  0xD0, 0xD0, 0x64, 0x59, 0x49, 0x5D, 0xFB, 0xFC,
+  0xD9, 0xFC, 0xD6, 0x23, 0x22, 0x22, 0x22, 0x23,
+  0x22, 0x22, 0x23, 0x23, 0x21, 0x21, 0x22, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+  0x23, 0x22, 0x23, 0x23, 0x22, 0x23, 0xB4, 0xC8,
+  0xBD, 0xB7, 0xBD, 0xBC, 0xBD, 0xC5, 0xBC, 0xC5,
+  0xBC, 0xBD, 0xC7, 0xAC, 0x58, 0x57, 0x58, 0x2C,
+  0xD1, 0xF0, 0xF3, 0xF3, 0xE0, 0xCD, 0x45, 0x3E,
+  0x48, 0x4B, 0x3F, 0x41, 0x56, 0x64, 0x65, 0x62,
+  0x5D, 0x52, 0x47, 0x48, 0x48, 0x53, 0x60, 0xCC,
+  0xD2, 0xD0, 0x63, 0x52, 0x4E, 0x53, 0xFB, 0xFB,
+  0xFC, 0xFC, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23,
+  0x22, 0x22, 0x23, 0x23, 0x20, 0x21, 0x22, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+  0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0xB4, 0xC7,
+  0xC5, 0xBC, 0xC5, 0xBD, 0xC5, 0xC5, 0xBD, 0xC5,
+  0xBC, 0xC6, 0xC7, 0xB9, 0x58, 0x57, 0x58, 0x57,
+  0x2D, 0xD4, 0xF1, 0xF2, 0xF0, 0xD9, 0x5D, 0x47,
+  0x48, 0x3F, 0x42, 0x2C, 0x48, 0x5C, 0x5F, 0x60,
+  0x58, 0x50, 0x47, 0x4A, 0x49, 0x55, 0x63, 0xD0,
+  0xD2, 0xCD, 0x5D, 0x49, 0x4E, 0xE1, 0xFC, 0xF0,
+  0xFC, 0xF8, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23,
+  0x22, 0x22, 0x23, 0x20, 0x21, 0x21, 0x22, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x23, 0x22,
+  0x23, 0x22, 0x23, 0x23, 0x23, 0x22, 0xC4, 0xC8,
+  0xBD, 0xBD, 0xC6, 0xBD, 0xC6, 0xC6, 0xC5, 0xC6,
+  0xBD, 0xC6, 0xC7, 0xE4, 0x54, 0x57, 0x58, 0x57,
+  0x57, 0x43, 0xD7, 0xE0, 0xF1, 0xD8, 0xCD, 0x4B,
+  0x4A, 0x47, 0x42, 0x2C, 0x3F, 0x4D, 0x58, 0x5C,
+  0x52, 0x4B, 0x48, 0x4B, 0x4A, 0x58, 0xCB, 0xD3,
+  0xD2, 0xCD, 0x58, 0x47, 0x4A, 0xFC, 0xFC, 0xFB,
+  0xFC, 0x2B, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23,
+  0x22, 0x22, 0x23, 0x26, 0x21, 0x21, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x23,
+  0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0xE5, 0xC8,
+  0xBA, 0xC5, 0xC6, 0xC6, 0xC6, 0xC7, 0xC6, 0xC7,
+  0xC5, 0xC6, 0xC8, 0xE5, 0x2E, 0x54, 0x58, 0x57,
+  0x57, 0x4C, 0x4D, 0xDA, 0xD8, 0xD8, 0xD4, 0x5C,
+  0x4B, 0x4B, 0x3F, 0x42, 0x44, 0x4A, 0x51, 0x58,
+  0x4B, 0x48, 0x4B, 0x51, 0x4D, 0x5F, 0xD0, 0xD1,
+  0xD0, 0x64, 0x51, 0x44, 0x6B, 0xFC, 0xFB, 0xFC,
+  0xFC, 0x21, 0x23, 0x22, 0x22, 0x23, 0x22, 0x23,
+  0x22, 0x22, 0x23, 0x26, 0x21, 0x23, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x23,
+  0x22, 0x23, 0x22, 0x23, 0x23, 0x23, 0xE5, 0xED,
+  0xE7, 0xBA, 0xC8, 0xC6, 0xC6, 0xC6, 0xC6, 0xC7,
+  0xC7, 0xE5, 0xED, 0xE6, 0x61, 0x41, 0x52, 0x58,
+  0x58, 0x57, 0x45, 0x5E, 0xD7, 0xDD, 0xD5, 0x60,
+  0x4B, 0x4C, 0x48, 0x4D, 0x4D, 0x50, 0x4D, 0x56,
+  0x4A, 0x3E, 0x53, 0x53, 0x52, 0x63, 0xD3, 0xD0,
+  0xCE, 0x60, 0x4A, 0x45, 0xFC, 0xFC, 0xF7, 0xFC,
+  0xFC, 0x21, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23,
+  0x22, 0x23, 0x21, 0x2A, 0x20, 0x23, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+  0x23, 0x22, 0x23, 0x22, 0x21, 0x23, 0xEB, 0xF6,
+  0xF6, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED,
+  0xF6, 0xF6, 0xF6, 0xE6, 0xDB, 0x58, 0x45, 0x4B,
+  0x58, 0x57, 0x4D, 0x4B, 0x64, 0xD4, 0xD0, 0x5C,
+  0x48, 0x51, 0x4C, 0x5D, 0x5E, 0x5C, 0x56, 0x59,
+  0x3E, 0x4A, 0x58, 0x54, 0x52, 0x65, 0xD3, 0xD0,
+  0xCF, 0x5D, 0x48, 0xFC, 0xFC, 0xFC, 0xFA, 0xFC,
+  0xFC, 0x21, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23,
+  0x22, 0x23, 0x21, 0x2A, 0x21, 0x23, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+  0x23, 0x22, 0x23, 0x22, 0x21, 0x4F, 0xE6, 0xC6,
+  0xC6, 0xBD, 0xC6, 0xBD, 0xBD, 0xBD, 0xBD, 0xC6,
+  0xC5, 0xBA, 0xC7, 0xE6, 0xF2, 0xD4, 0x49, 0x4B,
+  0x3E, 0x4D, 0x52, 0x3E, 0x52, 0x63, 0x64, 0x56,
+  0x48, 0x54, 0x4D, 0x61, 0xCC, 0xCC, 0x60, 0x60,
+  0x47, 0x4D, 0x5C, 0x53, 0x58, 0xCF, 0xD1, 0xCF,
+  0xD0, 0x59, 0x45, 0xFC, 0xFC, 0xFC, 0xEF, 0xF9,
+  0xFC, 0x21, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22,
+  0x23, 0x22, 0x23, 0x2A, 0x21, 0x23, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+  0x23, 0x22, 0x23, 0x22, 0x23, 0x4F, 0xE4, 0xB9,
+  0xAF, 0x80, 0x80, 0x8E, 0x8E, 0x8E, 0x8E, 0x8F,
+  0x80, 0xB4, 0xB9, 0xE4, 0x7F, 0xDE, 0x61, 0x52,
+  0x54, 0x48, 0x3F, 0x43, 0x4D, 0x56, 0x59, 0x4B,
+  0x3E, 0x58, 0x53, 0x61, 0xD3, 0xD4, 0xCF, 0xCD,
+  0x4C, 0x58, 0x5F, 0x53, 0x5E, 0xD3, 0xD0, 0xCE,
+  0xCE, 0x52, 0x3F, 0xFC, 0xFC, 0xFC, 0xF7, 0x65,
+  0xFA, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22,
+  0x23, 0x22, 0x21, 0x2A, 0x23, 0x23, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+  0x23, 0x22, 0x23, 0x22, 0x21, 0xB1, 0xE4, 0xE6,
+  0x7C, 0xB1, 0x7C, 0xB1, 0xB2, 0xB2, 0xB3, 0x3D,
+  0xB3, 0x3C, 0xE5, 0xB3, 0xB0, 0xF1, 0xD0, 0x58,
+  0x5D, 0x4D, 0x40, 0x41, 0x48, 0x51, 0x4C, 0x3F,
+  0x3F, 0x4D, 0x5A, 0x5A, 0xD5, 0xD9, 0xD7, 0xD4,
+  0x57, 0x5E, 0x61, 0x4C, 0x63, 0xD4, 0xCF, 0xCE,
+  0xCB, 0x4D, 0x4A, 0xFC, 0xFC, 0xFC, 0xFC, 0xF0,
+  0xFB, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22,
+  0x23, 0x22, 0x23, 0x2A, 0x21, 0x23, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+  0x22, 0x23, 0x22, 0x23, 0x23, 0xB1, 0x81, 0x7D,
+  0x39, 0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0x36,
+  0x36, 0x36, 0x7C, 0xB2, 0xB0, 0xDF, 0xD2, 0x57,
+  0x60, 0x59, 0x5B, 0x59, 0x52, 0x4C, 0x4A, 0x40,
+  0x42, 0x4A, 0x53, 0x4D, 0xD2, 0xDE, 0xDE, 0xD9,
+  0x5E, 0x5E, 0x60, 0x4A, 0xCD, 0xD1, 0xCF, 0xCE,
+  0x63, 0x49, 0x5C, 0xFB, 0xE8, 0x89, 0x9F, 0xFC,
+  0xD6, 0x21, 0x21, 0x23, 0x22, 0x22, 0x23, 0x22,
+  0x23, 0x22, 0x21, 0x2A, 0x22, 0x23, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+  0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x7F, 0xB9,
+  0x71, 0x6C, 0x38, 0x38, 0x33, 0x33, 0x33, 0x38,
+  0x38, 0x71, 0xAD, 0xE4, 0xD3, 0xDA, 0xCC, 0x52,
+  0x63, 0x60, 0xCE, 0xD4, 0xCF, 0x60, 0x4C, 0x40,
+  0x3F, 0x45, 0x4B, 0x5A, 0xCB, 0xD8, 0xDE, 0xDC,
+  0x5E, 0x5E, 0x5F, 0x4C, 0xD2, 0xD2, 0xCF, 0xCF,
+  0x61, 0x45, 0x5E, 0xA7, 0x9D, 0x95, 0x8B, 0x99,
+  0xFC, 0x41, 0x21, 0x23, 0x23, 0x22, 0x23, 0x22,
+  0x23, 0x22, 0x23, 0x2A, 0x23, 0x23, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x77, 0x77, 0xF6,
+  0xFC, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D,
+  0x7D, 0xFC, 0x47, 0x64, 0xD0, 0xD0, 0x5D, 0x4B,
+  0x62, 0xCC, 0xD1, 0xDE, 0xDE, 0xD4, 0x5E, 0x43,
+  0x3F, 0x3E, 0x48, 0x53, 0x58, 0xDB, 0xD8, 0xDC,
+  0x5E, 0x5E, 0x5E, 0x53, 0xD4, 0xD2, 0xD0, 0xD0,
+  0x5E, 0x49, 0xA7, 0xA6, 0x89, 0x95, 0x8B, 0x9C,
+  0x9C, 0xFB, 0xD4, 0x22, 0x22, 0x22, 0x22, 0x23,
+  0x22, 0x23, 0x23, 0x2A, 0x22, 0x23, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x23,
+  0x23, 0x22, 0x23, 0x23, 0x98, 0x8C, 0x8C, 0x88,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8,
+  0xE9, 0x9C, 0x48, 0x5C, 0xD0, 0xCB, 0x48, 0x49,
+  0x5B, 0xCB, 0xCD, 0xE0, 0xF1, 0xDD, 0xD0, 0x4A,
+  0x41, 0x47, 0x45, 0x4C, 0x48, 0xD7, 0xDE, 0xDC,
+  0x5E, 0x5E, 0x5A, 0x58, 0xD1, 0xD0, 0xD0, 0xD2,
+  0x5C, 0x55, 0xA7, 0xA6, 0x87, 0x86, 0x89, 0x94,
+  0x9C, 0xA9, 0xFC, 0xF4, 0x22, 0x23, 0x22, 0x23,
+  0x22, 0x23, 0x22, 0x2A, 0x21, 0x23, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x23,
+  0x22, 0x23, 0x22, 0x23, 0xA4, 0x89, 0x8C, 0xAA,
+  0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF7,
+  0x85, 0x88, 0x8D, 0x59, 0x64, 0x63, 0x47, 0x3E,
+  0x4C, 0x60, 0x61, 0xE0, 0xF0, 0xDF, 0xD9, 0x5D,
+  0x2E, 0x3E, 0x3E, 0x47, 0x4D, 0xCD, 0xDE, 0xDC,
+  0x5D, 0x5C, 0x51, 0x5D, 0xD1, 0xD2, 0xD2, 0xD4,
+  0x5A, 0xBE, 0xA7, 0x98, 0x8A, 0x8A, 0xA0, 0x8B,
+  0x86, 0x86, 0xF7, 0xFC, 0xF7, 0x26, 0x23, 0x23,
+  0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x23,
+  0x22, 0x21, 0x21, 0x21, 0xA1, 0x98, 0x9F, 0xBF,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xA7,
+  0x8C, 0x86, 0x8D, 0x59, 0x5E, 0x5D, 0x3F, 0x3E,
+  0x47, 0x53, 0x63, 0xD9, 0xF0, 0xF1, 0xDE, 0xD0,
+  0x43, 0x3E, 0x47, 0x45, 0x4A, 0x5B, 0xDC, 0xDA,
+  0x5D, 0x59, 0x49, 0x5F, 0xD1, 0xD2, 0xD3, 0xB9,
+  0xA5, 0xA7, 0x98, 0x9B, 0x96, 0x9D, 0x89, 0x89,
+  0x8B, 0x9C, 0x9D, 0xFC, 0xFC, 0xFC, 0x26, 0x22,
+  0x23, 0x23, 0x22, 0x22, 0x21, 0x22, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x23,
+  0x22, 0x22, 0x29, 0x2D, 0x99, 0x99, 0xA2, 0xAA,
+  0xC4, 0xFB, 0xFC, 0xFC, 0xFC, 0xF6, 0xBF, 0xA2,
+  0x9C, 0x9C, 0x8E, 0xDC, 0xCD, 0x51, 0x41, 0x3E,
+  0x45, 0x49, 0x58, 0xCD, 0xE0, 0xE0, 0xD8, 0xDA,
+  0x4C, 0x4A, 0x45, 0x45, 0x48, 0x47, 0xDA, 0xDA,
+  0x5C, 0x58, 0x44, 0x69, 0xA9, 0x98, 0xA4, 0xA6,
+  0xA1, 0xA4, 0x99, 0x9E, 0x9D, 0x8B, 0x8A, 0x97,
+  0x87, 0x9A, 0x8A, 0xC2, 0xFC, 0xFC, 0xFC, 0x4D,
+  0x21, 0x21, 0x23, 0x22, 0x21, 0x22, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x23, 0x22,
+  0x21, 0x22, 0x2D, 0x34, 0xA4, 0xA2, 0xA2, 0xA9,
+  0xBF, 0xC0, 0xC3, 0xC1, 0xC0, 0xBE, 0xA6, 0x9D,
+  0x99, 0x87, 0xA2, 0xF1, 0xDC, 0x64, 0x42, 0x45,
+  0x47, 0x3E, 0x49, 0x4C, 0xDD, 0xDF, 0xD8, 0xDB,
+  0x5E, 0x4C, 0x48, 0x45, 0x45, 0x41, 0xD1, 0xD6,
+  0x5A, 0x55, 0x3F, 0xA7, 0xA1, 0x98, 0x9F, 0x99,
+  0x9F, 0x9D, 0x9A, 0x95, 0x8B, 0x97, 0x89, 0x8A,
+  0x88, 0x94, 0x9C, 0x8C, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xF4, 0x21, 0x23, 0x22, 0x21, 0x22, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x22, 0x23,
+  0x23, 0x23, 0x2C, 0x2C, 0xA8, 0xA2, 0xA4, 0xA4,
+  0xA9, 0xAA, 0xAA, 0xAA, 0xA9, 0xA6, 0x98, 0x9C,
+  0x8B, 0x88, 0x98, 0x8D, 0xD8, 0xD6, 0x4E, 0x47,
+  0x47, 0x49, 0x47, 0x3F, 0xDA, 0xDD, 0xDE, 0xDD,
+  0xCC, 0x4A, 0x4B, 0x3E, 0x45, 0x43, 0x61, 0xD4,
+  0x56, 0x51, 0x44, 0xA4, 0x9B, 0x8B, 0x9C, 0x9A,
+  0xA0, 0xA2, 0x98, 0x98, 0x8B, 0x8B, 0x98, 0x98,
+  0x84, 0x8B, 0x94, 0x8A, 0xA4, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xF2, 0x21, 0x22, 0x21, 0x22, 0x23, 0x22,
+  0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x23, 0x23,
+  0x23, 0x22, 0x2C, 0x2D, 0xC0, 0xA4, 0xA2, 0xA4,
+  0xA4, 0xA6, 0xA6, 0xA6, 0xA4, 0xA2, 0x9F, 0x89,
+  0x8B, 0x9C, 0x9C, 0x8B, 0x68, 0xDB, 0x5F, 0x4B,
+  0x3E, 0x49, 0x4B, 0x3E, 0xCC, 0xDA, 0xDC, 0xDD,
+  0xD3, 0x49, 0x52, 0x48, 0x45, 0x45, 0x53, 0xD0,
+  0x51, 0x4A, 0x44, 0xA4, 0x9B, 0x8B, 0x9C, 0xA0,
+  0x9B, 0x86, 0x89, 0x98, 0x89, 0x8A, 0x96, 0x8A,
+  0x9C, 0x89, 0x89, 0x9C, 0x8C, 0xF6, 0xFC, 0xFC,
+  0xFC, 0xFC, 0x21, 0x22, 0x21, 0x22, 0x23, 0x22,
+  0x22, 0x22, 0x22, 0x23, 0x22, 0x21, 0x22, 0x23,
+  0x22, 0x21, 0x2B, 0x34, 0xC0, 0xA8, 0xA4, 0xA2,
+  0xA2, 0x98, 0xA1, 0xA0, 0x98, 0x9F, 0x95, 0x8A,
+  0x94, 0xA1, 0x8A, 0x84, 0x9B, 0x68, 0xCC, 0x49,
+  0x4A, 0x47, 0x4C, 0x4B, 0x51, 0xD3, 0xDA, 0xDC,
+  0xD5, 0x56, 0x56, 0x4A, 0x3E, 0x45, 0x48, 0x63,
+  0x4A, 0x47, 0x3E, 0xA7, 0x98, 0x9D, 0x9E, 0x8B,
+  0x95, 0x9B, 0x89, 0x86, 0x9B, 0x8B, 0x89, 0x84,
+  0x9A, 0xA1, 0x95, 0x9A, 0x8C, 0xA4, 0xFC, 0xFC,
+  0xFC, 0xFA, 0x23, 0x22, 0x21, 0x22, 0x23, 0x22,
+  0x22, 0x22, 0x22, 0x23, 0x22, 0x21, 0x22, 0x23,
+  0x21, 0x23, 0x2C, 0xF6, 0xBF, 0xA9, 0xA2, 0x99,
+  0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9B, 0x87, 0x8B,
+  0x9C, 0x86, 0x9C, 0x8A, 0x87, 0x87, 0x89, 0x51,
+  0x54, 0x47, 0x4B, 0x50, 0x4B, 0xCF, 0xD6, 0xDC,
+  0xD5, 0x60, 0x54, 0x52, 0x48, 0x45, 0x40, 0x5A,
+  0x45, 0x43, 0x47, 0xA7, 0x98, 0x9B, 0x95, 0x95,
+  0x9A, 0x87, 0x98, 0x98, 0x8A, 0x86, 0x87, 0x9E,
+  0x9B, 0x95, 0x9D, 0x9D, 0x99, 0x85, 0xA6, 0xFA,
+  0xF2, 0x21, 0x23, 0x22, 0x21, 0x22, 0x23, 0x22,
+  0x22, 0x22, 0x22, 0x23, 0x22, 0x21, 0x22, 0x22,
+  0x21, 0x24, 0xFB, 0xF7, 0xBF, 0xA6, 0xA2, 0x99,
+  0x97, 0x89, 0x86, 0x89, 0x9C, 0x96, 0x9E, 0x94,
+  0x89, 0x99, 0x98, 0x89, 0x9E, 0x9B, 0x89, 0x8B,
+  0x58, 0x4B, 0x4A, 0x52, 0x48, 0xCC, 0xD3, 0xDA,
+  0xD3, 0x65, 0x4C, 0x58, 0x49, 0x3E, 0x2E, 0x4D,
+  0x40, 0x41, 0x45, 0xA9, 0xA1, 0x9B, 0x9E, 0x9C,
+  0x95, 0x8A, 0x94, 0x89, 0x96, 0x87, 0x9C, 0x9A,
+  0x84, 0x9D, 0x9C, 0x9E, 0x9A, 0x9C, 0x9D, 0xBB,
+  0x23, 0x23, 0x22, 0x22, 0x21, 0x22, 0x23, 0x22,
+  0x22, 0x22, 0x22, 0x23, 0x22, 0x21, 0x23, 0x23,
+  0x24, 0xFC, 0xFC, 0xF6, 0xBF, 0xA6, 0x9F, 0x99,
+  0x89, 0x95, 0x87, 0x94, 0x9D, 0x9E, 0x97, 0x9E,
+  0x95, 0x9B, 0x89, 0x95, 0x95, 0x9B, 0x89, 0x87,
+  0x5D, 0x56, 0x3E, 0x51, 0x3E, 0x60, 0xCF, 0xD3,
+  0xD2, 0xCD, 0x5C, 0x49, 0x4B, 0x3E, 0x2C, 0x48,
+  0x3E, 0x43, 0x3E, 0xA9, 0xA1, 0x9B, 0x97, 0x94,
+  0x95, 0x9A, 0x9C, 0x87, 0x87, 0x9B, 0x9C, 0x95,
+  0x9D, 0x89, 0x9A, 0x89, 0x9E, 0x9E, 0x8C, 0xA6,
+  0x20, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22,
+  0x22, 0x22, 0x22, 0x22, 0x21, 0x21, 0x20, 0x40,
+  0xFC, 0xFC, 0xFC, 0xEC, 0xBE, 0xA4, 0x9F, 0x99,
+  0x95, 0x9F, 0xA0, 0x88, 0x9D, 0x8B, 0x97, 0x95,
+  0x87, 0x95, 0x96, 0x95, 0x97, 0x94, 0x94, 0x98,
+  0xD3, 0x4C, 0x47, 0x4D, 0x42, 0x4C, 0x60, 0xCC,
+  0xCE, 0xD0, 0x65, 0x4B, 0x47, 0x44, 0x2B, 0x45,
+  0x4B, 0x47, 0x49, 0xA7, 0xA1, 0x9A, 0x97, 0x89,
+  0x95, 0x97, 0x97, 0x9E, 0x89, 0x95, 0x89, 0x9C,
+  0x87, 0x95, 0x97, 0x99, 0x95, 0x99, 0x9F, 0xA4,
+  0xC4, 0x21, 0x21, 0x23, 0x21, 0x23, 0x23, 0x23,
+  0x23, 0x23, 0x23, 0x23, 0x21, 0x20, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xEA, 0xAA, 0xA6, 0xA2, 0x99,
+  0x8B, 0x9A, 0x95, 0x9E, 0x9E, 0x9A, 0x94, 0x87,
+  0x94, 0x94, 0x89, 0x94, 0x9B, 0x9B, 0xA7, 0xDC,
+  0xDB, 0x65, 0x2E, 0x3E, 0x43, 0x44, 0x49, 0x58,
+  0x63, 0xD3, 0xD3, 0x5E, 0x42, 0x42, 0x2D, 0x40,
+  0x54, 0x4C, 0x4A, 0xA7, 0xA0, 0x99, 0x9B, 0x94,
+  0xA0, 0x8A, 0x9B, 0x9D, 0x87, 0x95, 0x94, 0x8B,
+  0x8A, 0x98, 0x9C, 0x8A, 0x9B, 0x99, 0xA2, 0xA6,
+  0xBF, 0xEC, 0x2A, 0x20, 0x21, 0x23, 0x21, 0x20,
+  0x20, 0x20, 0x20, 0x4C, 0xF9, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xEB, 0xAA, 0xA4, 0x9F, 0x9C,
+  0x8B, 0x9B, 0x88, 0x84, 0x9E, 0x9D, 0x96, 0x94,
+  0x94, 0x9A, 0x9B, 0x9B, 0xA4, 0xD5, 0xCD, 0xDE,
+  0xF1, 0xDA, 0x4C, 0x2D, 0x41, 0x2B, 0x42, 0x4C,
+  0x5E, 0xD4, 0xD7, 0xCD, 0x49, 0x2E, 0x2E, 0x41,
+  0x5E, 0x57, 0xA7, 0xA6, 0xA7, 0xA4, 0xA2, 0x98,
+  0x9D, 0x9C, 0xA1, 0x99, 0x9D, 0x88, 0x8B, 0x9C,
+  0x8A, 0x9C, 0x9C, 0x94, 0x9C, 0x89, 0xA0, 0xA6,
+  0xAA, 0xEB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFB, 0xE9, 0xAA, 0xA6, 0xA2, 0x8B,
+  0x8B, 0x8A, 0x86, 0x9B, 0x9C, 0x98, 0xA0, 0x9B,
+  0x9B, 0x84, 0xA7, 0xB4, 0x61, 0xD1, 0xD2, 0xE0,
+  0xF1, 0xDC, 0x61, 0x2D, 0x2E, 0x3F, 0x56, 0x62,
+  0x5D, 0xD4, 0xD9, 0xD3, 0x54, 0x41, 0x41, 0x44,
+  0xCB, 0x60, 0x52, 0xA9, 0xA9, 0xA9, 0xA7, 0xA6,
+  0xA6, 0xA4, 0xA4, 0xA2, 0xA2, 0x9D, 0x95, 0x89,
+  0x9C, 0x8A, 0x9E, 0x9C, 0x8A, 0x9E, 0xA0, 0xA8,
+  0xC0, 0xE9, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xE9, 0xAA, 0xA6, 0xA0, 0x99,
+  0x9C, 0x8B, 0x9A, 0x84, 0x9B, 0x9B, 0x98, 0x98,
+  0xA9, 0xB9, 0x49, 0x57, 0xCB, 0xD4, 0xD3, 0xF1,
+  0xD8, 0xDA, 0xCE, 0x3F, 0x41, 0x4B, 0x5D, 0xCB,
+  0x5E, 0xD6, 0xDB, 0xD6, 0x5D, 0x43, 0x3F, 0x49,
+  0xD1, 0xCC, 0x4F, 0xDD, 0xC3, 0xBB, 0xBF, 0xAA,
+  0xAA, 0xA9, 0xAA, 0xA8, 0xA8, 0xA6, 0xA6, 0xA2,
+  0x9C, 0x9F, 0x9B, 0x9A, 0x9D, 0xA2, 0xA8, 0xAA,
+  0xC1, 0xEA, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xEA, 0xC0, 0xAA, 0xA6, 0xA2,
+  0xA2, 0x99, 0xA0, 0xA0, 0xA4, 0xA7, 0xA9, 0xC0,
+  0x67, 0x49, 0x54, 0x60, 0xD0, 0xD4, 0xCC, 0xDF,
+  0xD9, 0xD5, 0xD2, 0x3E, 0x47, 0x56, 0x60, 0xCD,
+  0x5D, 0xD9, 0xD9, 0xD6, 0x61, 0x3F, 0x47, 0x52,
+  0xD6, 0xD3, 0x62, 0x4D, 0x40, 0x4A, 0x57, 0xCA,
+  0xC3, 0xC1, 0xC1, 0xC0, 0xBF, 0xBF, 0xAA, 0xAA,
+  0xA6, 0xA4, 0xA4, 0xA4, 0xA6, 0xA8, 0xBE, 0xC1,
+  0xC9, 0xEB, 0xFB, 0xFB, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+  0xFC, 0xFC, 0xFC, 0xEB, 0xC3, 0xC0, 0xAA, 0xA8,
+  0xA6, 0xA6, 0xA6, 0xA9, 0xAA, 0xC0, 0xE8, 0xD0,
+  0xD2, 0x4C, 0x5E, 0x64, 0xD0, 0xD1, 0x5F, 0xD9,
+  0xD5, 0xD1, 0xD0, 0x48, 0x52, 0x5C, 0x64, 0xCD,
+  0x5C, 0xDC, 0xD7, 0xD5, 0x62, 0x3F, 0x4C, 0x53,
+  0xDA, 0xD7, 0xCE, 0x56, 0x40, 0x4B, 0x52, 0x56,
+  0xCE, 0xDF, 0x6A, 0xEB, 0xE9, 0xC9, 0xC3, 0xC0,
+  0xC0, 0xBF, 0xBE, 0xAA, 0xBF, 0xC0, 0xC3, 0xC9,
+  0xEA, 0xF6, 0xEE, 0x58, 0x57, 0x5E, 0xD6, 0xD0,
+  0xD2, 0x61, 0xCB, 0xD6, 0xD6, 0xD4, 0xDF, 0xF3,
+  0xF2, 0xDD, 0xD7, 0xEB, 0xC9, 0xC1, 0xC0, 0xBF,
+  0xAA, 0xAA, 0xAA, 0xBE, 0xC3, 0xF0, 0xD2, 0xD2,
+  0xD2, 0x51, 0x62, 0xCC, 0xD0, 0xCC, 0x61, 0xD3,
+  0xCF, 0xCE, 0xD2, 0x48, 0x5A, 0x61, 0xCC, 0xCE,
+  0x5F, 0xD9, 0xD5, 0xD1, 0x63, 0x44, 0x56, 0x56,
+  0xDC, 0xD9, 0xD4, 0x5E, 0x42, 0x4A, 0x4C, 0x57,
+  0x5D, 0xD8, 0xE0, 0xD8, 0xDC, 0xCB, 0x66, 0xEC,
+  0xE8, 0xC3, 0xC3, 0xC3, 0xC3, 0xC9, 0xE8, 0xEA,
+  0xF6, 0x50, 0x3E, 0x58, 0x57, 0x5A, 0xD6, 0xD4,
+  0xCC, 0x4B, 0x53, 0x5C, 0x64, 0xD1, 0xDF, 0xF3,
+  0xF1, 0xDE, 0xD9, 0xF6, 0xEB, 0xC9, 0xC1, 0xC1,
+  0xC0, 0xC0, 0xC1, 0xC9, 0xF0, 0xD6, 0xCD, 0xD6,
+  0xD3, 0x53, 0xCB, 0xCF, 0xCD, 0x5F, 0x5F, 0xCE,
+  0xCF, 0xCD, 0xD0, 0x47, 0x5F, 0xCB, 0xCE, 0xCD,
+  0x63, 0xD6, 0xD3, 0xD1, 0x63, 0x3F, 0x58, 0x58,
+  0xDB, 0xDC, 0xDA, 0x65, 0x3E, 0x49, 0x49, 0x4D,
+  0x49, 0xDC, 0xDF, 0xE0, 0xDE, 0xD5, 0x47, 0x47,
+  0x46, 0x6B, 0xEB, 0xEA, 0xE9, 0xEA, 0xEB, 0xF6,
+  0xD0, 0x57, 0x57, 0x47, 0x47, 0x5B, 0xD4, 0xD4,
+  0xCD, 0x44, 0x3E, 0x4B, 0x50, 0x4B, 0x51, 0xD5,
+  0xDB, 0xD8, 0xDE, 0x4B, 0xF6, 0xF6, 0xEA, 0xE9,
+  0xE8, 0xEA, 0xEB, 0x67, 0x5E, 0xCC, 0xD6, 0xDC,
+  0xD5, 0x58, 0xCE, 0xCE, 0x62, 0x50, 0xCC, 0xD3,
+  0xD2, 0xCD, 0xCD, 0x4B, 0x64, 0xCE, 0xCE, 0x64,
+  0xCC, 0xD3, 0xD2, 0xD2, 0x61, 0x47, 0x5D, 0x5C,
+  0xDD, 0xDD, 0xD9, 0xD1, 0x4C, 0x47, 0x49, 0x4A,
+  0x4B, 0xD1, 0xD8, 0xE0, 0xDF, 0xDD, 0x5D, 0x4A,
+  0x48, 0x52, 0x51, 0x3F, 0xF6, 0xEC, 0xE0, 0xE0,
+  0xD3, 0x5E, 0x5F, 0x50, 0x4B, 0x50, 0xCB, 0xCE,
+  0x64, 0x45, 0x4C, 0x57, 0x57, 0x58, 0x52, 0xD6,
+  0xD3, 0xDE, 0xDF, 0xD1, 0x3E, 0x4B, 0xF6, 0xF6,
+  0xEC, 0x66, 0x53, 0x43, 0x56, 0xD1, 0xD9, 0xDE,
+  0xD4, 0x5E, 0xCE, 0xCC, 0x5B, 0x2C, 0xD4, 0xD5,
+  0xD2, 0xD0, 0x63, 0x5D, 0xCD, 0xD0, 0xCD, 0x5E,
+  0xD0, 0xCF, 0xCE, 0xD2, 0x5E, 0x50, 0x60, 0x5D,
+  0xDE, 0xDD, 0xDC, 0xD7, 0x5D, 0x45, 0x47, 0x3E,
+  0x4B, 0x5E, 0xDE, 0xDF, 0xE0, 0xD8, 0xCF, 0x3E,
+  0x45, 0x51, 0x58, 0x42, 0xCB, 0xDA, 0xDE, 0xD8,
+  0xD2, 0x61, 0xCC, 0xCF, 0xD6, 0xDA, 0xDA, 0xD5,
+  0xD0, 0x50, 0x44, 0x57, 0x57, 0x58, 0x45, 0xD1,
+  0xD1, 0xD7, 0xDF, 0xDF, 0xD7, 0xCF, 0x64, 0x60,
+  0xCE, 0xCE, 0xCE, 0x63, 0xCF, 0xDA, 0xDE, 0xD9,
+  0xCF, 0x63, 0xCD, 0x63, 0x4D, 0x4B, 0xD6, 0xD5,
+  0xCE, 0xD3, 0x60, 0xCB, 0xD0, 0xD0, 0x65, 0x47,
+  0xD0, 0xCC, 0xCC, 0xD1, 0x59, 0x5D, 0x63, 0x5E,
+  0xDD, 0xDD, 0xDE, 0xDC, 0xCB, 0x40, 0x48, 0x45,
+  0x3E, 0x3E, 0xD9, 0xDF, 0xE0, 0xDF, 0xDA, 0x51,
+  0x4C, 0x48, 0x56, 0x4C, 0x5B, 0xD2, 0xDA, 0xDB,
+  0xCB, 0x5F, 0xD0, 0xCC, 0xDC, 0xF0, 0xF3, 0xE0,
+  0xDD, 0xCC, 0x41, 0x50, 0x57, 0x57, 0x4B, 0x5D,
+  0xD3, 0xD1, 0xDE, 0xDF, 0xDE, 0xD7, 0xD0, 0xD0,
+  0xD5, 0xD6, 0xD6, 0xCE, 0xD7, 0xDC, 0xDA, 0xD5,
+  0x60, 0x63, 0x64, 0x5E, 0x47, 0x61, 0xD5, 0xD2,
+  0xCF, 0xD0, 0x59, 0xCD, 0xD1, 0xCF, 0x61, 0x4D,
+  0xCC, 0xCE, 0xCD, 0xD0, 0x52, 0x61, 0x64, 0x60,
+  0xDA, 0xDE, 0xDE, 0xDD, 0xD1, 0x4B, 0x4A, 0x45,
+  0x3E, 0x41, 0xCD, 0xDE, 0xE0, 0xF1, 0xDE, 0x63,
+  0x4A, 0x4A, 0x4A, 0x4B, 0x50, 0xCB, 0xD4, 0xD7,
+  0x5E, 0x54, 0x62, 0xD3, 0xD4, 0xF0, 0xF3, 0xF3,
+  0xF2, 0xDE, 0x61, 0x40, 0x49, 0x56, 0x4D, 0x3E,
+  0x4B, 0xCE, 0xD9, 0xD8, 0xD9, 0xD5, 0xCF, 0xD2,
+  0xD6, 0xD6, 0xD1, 0xD1, 0xD7, 0xD5, 0xCF, 0xD0,
+  0x54, 0x64, 0x63, 0x56, 0x2C, 0xCB, 0xD1, 0xCC,
+  0xD3, 0xCD, 0x54, 0xCF, 0xD1, 0xCE, 0x5E, 0x5C,
+  0xCE, 0xCE, 0xCE, 0xCB, 0x4B, 0x63, 0xCC, 0x61,
+  0xD4, 0xDC, 0xDE, 0xDE, 0xDA, 0x5D, 0x45, 0x45,
+  0x48, 0x3F, 0x52, 0xD9, 0xD8, 0xDF, 0xDF, 0xD2,
+  0x52, 0x4B, 0x3E, 0x2E, 0x47, 0x60, 0xCF, 0xD3,
+  0x59, 0x48, 0x50, 0x5E, 0xCC, 0xDE, 0xF2, 0xF2,
+  0xF3, 0xF3, 0xDD, 0x5D, 0x3E, 0x48, 0x47, 0x47,
+  0x58, 0xD1, 0xDA, 0xDA, 0xD5, 0xD1, 0xCD, 0xD2,
+  0xD3, 0xCF, 0xD3, 0xD1, 0xCD, 0xD3, 0xD2, 0x5E,
+  0x52, 0x64, 0x60, 0x4B, 0x45, 0x61, 0xCD, 0xD3,
+  0xD3, 0x64, 0x61, 0xD0, 0xD0, 0x64, 0x45, 0x63,
+  0xD0, 0xCE, 0xD0, 0x60, 0x56, 0xCB, 0xCC, 0x62,
+  0xCE, 0xDA, 0xDE, 0xD8, 0xDD, 0xCC, 0x45, 0x49,
+  0x3E, 0x47, 0x42, 0xD1, 0xDC, 0xD8, 0xD8, 0xD3,
+  0x5D, 0x4C, 0x49, 0x3F, 0x47, 0x59, 0xCD, 0xCF,
+  0x59, 0x2E, 0x48, 0x47, 0x52, 0x63, 0xF0, 0xF2,
+  0xF3, 0xF3, 0xF2, 0xDA, 0x52, 0x4B, 0x52, 0x58,
+  0x5E, 0x63, 0xD0, 0xD0, 0xD0, 0xCF, 0xCE, 0xCE,
+  0xCF, 0x65, 0x61, 0xD6, 0xD6, 0xD6, 0xCB, 0x4B,
+  0x61, 0x62, 0x5D, 0x43, 0x4B, 0x61, 0xD0, 0xD4,
+  0xD1, 0x61, 0xCE, 0xD2, 0xCD, 0x5E, 0x4A, 0xCE,
+  0xD0, 0xCC, 0xD0, 0x59, 0x61, 0xCC, 0xCC, 0x62,
+  0xD1, 0xD5, 0xDE, 0xD8, 0xDD, 0xCF, 0x4B, 0x4A,
+  0x45, 0x3E, 0x2D, 0xCB, 0xDC, 0xDE, 0xD8, 0xD5,
+  0x60, 0x54, 0x51, 0x4C, 0x4D, 0x5C, 0xCC, 0xCE,
+  0x5A, 0x2C, 0x50, 0x53, 0x3E, 0x59, 0xD8, 0xF3,
+  0xF2, 0xF3, 0xF3, 0xE0, 0x5E, 0x4A, 0x4C, 0x53,
+  0x5E, 0x63, 0xCC, 0xCC, 0xCC, 0xCD, 0xCF, 0xD3,
+  0x62, 0x53, 0xD6, 0xD6, 0xD6, 0xD6, 0x5B, 0x48,
+  0x64, 0x63, 0x59, 0x44, 0x57, 0x63, 0xD2, 0xD3,
+  0xD0, 0x5E, 0xD0, 0xD1, 0xCB, 0x58, 0x4C, 0xCF,
+  0xCF, 0xCE, 0xCE, 0x57, 0x63, 0xCC, 0xCD, 0x57,
+};
+
+unsigned char linux_logo_bw[] __initdata = {
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x3F,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F,
+  0xFE, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFE, 0x3F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xC7, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xC3,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF,
+  0xFB, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFD, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xF1,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF,
+  0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xF9, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xF9, 0xCF, 0xC3, 0xF8, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x87, 0x81, 0xF9,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xA7,
+  0x99, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xF9, 0xF3, 0xBC, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xF9, 0xE3, 0xBC, 0xF9, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xB0, 0x3C, 0xF9,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xB0,
+  0x19, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xF9, 0xC0, 0x03, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xF9, 0x80, 0x01, 0xF8, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x80, 0x01, 0xF8,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x80,
+  0x01, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xF9, 0xC0, 0x21, 0xD8, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xF9, 0xB1, 0x80, 0xEC, 0xC0, 0x1F,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x90, 0x00, 0xE4,
+  0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x8C,
+  0xC0, 0x7C, 0x04, 0x81, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xE3, 0x80, 0x00, 0x7C, 0x40, 0x11, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xE3, 0x80, 0x00, 0x7F, 0xD2, 0x29,
+  0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0x00, 0x00, 0x3F,
+  0x80, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x0E, 0x00,
+  0x00, 0x3F, 0x80, 0x19, 0xFF, 0xFF, 0xFF, 0xFF,
+  0x1E, 0x00, 0x00, 0x1F, 0x80, 0x19, 0xFF, 0xFF,
+  0xFF, 0xFE, 0x1C, 0x00, 0x00, 0x1E, 0x80, 0x19,
+  0xFF, 0xFF, 0xFF, 0xFE, 0x3C, 0x00, 0x00, 0x1E,
+  0x80, 0x11, 0xFF, 0xFF, 0xFF, 0xFC, 0x7C, 0x00,
+  0x00, 0x0F, 0x80, 0x11, 0xFF, 0xFF, 0xFF, 0xFC,
+  0xF8, 0x00, 0x00, 0x0E, 0x80, 0x11, 0xFF, 0xFF,
+  0xFF, 0xFC, 0xF8, 0x00, 0x00, 0x06, 0x00, 0x11,
+  0xFF, 0xFF, 0xFF, 0xF8, 0xF8, 0x00, 0x00, 0x06,
+  0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xF9, 0xF0, 0x00,
+  0x00, 0x02, 0x00, 0x09, 0xFF, 0xFF, 0xFF, 0xF1,
+  0xF0, 0x00, 0x00, 0x02, 0x80, 0x10, 0xFF, 0xFF,
+  0xFF, 0xF1, 0xE0, 0x00, 0x00, 0x00, 0x97, 0x10,
+  0xFF, 0xFF, 0xFF, 0xE3, 0xE0, 0x00, 0x00, 0x00,
+  0xDF, 0xF0, 0xFF, 0xFF, 0xFF, 0xE3, 0xC0, 0x00,
+  0x00, 0x00, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xC7,
+  0xC0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0xFF, 0xFF,
+  0xFF, 0xC7, 0x80, 0x00, 0x00, 0x01, 0xFF, 0xF8,
+  0xFF, 0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x00, 0x01,
+  0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0x8F, 0x80, 0x00,
+  0x00, 0x01, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0x9F,
+  0x80, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0xFF, 0xFF,
+  0xFF, 0x9F, 0x80, 0x00, 0x00, 0x01, 0x80, 0x18,
+  0xFF, 0xFF, 0xFF, 0x9E, 0x80, 0x00, 0x00, 0x03,
+  0xA8, 0x11, 0xFF, 0xFF, 0xFF, 0x9F, 0x80, 0x00,
+  0x00, 0x02, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x99,
+  0x80, 0x00, 0x00, 0x00, 0x00, 0x09, 0xFF, 0xFF,
+  0xFF, 0x00, 0x80, 0x00, 0x00, 0x01, 0xC0, 0x01,
+  0xFF, 0xFF, 0xFE, 0x20, 0x60, 0x00, 0x00, 0x00,
+  0xFF, 0xC3, 0xFF, 0xFF, 0xF8, 0x00, 0x30, 0x00,
+  0x00, 0x00, 0xFF, 0x0F, 0xFF, 0xFF, 0xC0, 0x40,
+  0x38, 0x00, 0x00, 0x00, 0xFE, 0x47, 0xFF, 0xFF,
+  0x81, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xFC, 0x23,
+  0xFF, 0xFF, 0x90, 0x00, 0x1E, 0x00, 0x00, 0x00,
+  0x78, 0x11, 0xFF, 0xFF, 0x80, 0x00, 0x0F, 0x80,
+  0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00,
+  0x07, 0xC0, 0x00, 0x00, 0x00, 0x08, 0xFF, 0xFF,
+  0xC0, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x04,
+  0x7F, 0xFF, 0x80, 0x00, 0x03, 0xC0, 0x00, 0x10,
+  0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x01, 0x80,
+  0x00, 0x30, 0x00, 0x00, 0x0F, 0xFF, 0x80, 0x00,
+  0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x4F, 0xFF,
+  0x80, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00,
+  0x0F, 0xFF, 0xC0, 0x00, 0x00, 0x80, 0x03, 0xF0,
+  0x00, 0x00, 0x8F, 0xFF, 0x80, 0x00, 0x00, 0x40,
+  0x0F, 0xF0, 0x00, 0x04, 0x1F, 0xFF, 0x80, 0x00,
+  0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x10, 0x1F, 0xFF,
+  0xC0, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x40,
+  0xFF, 0xFF, 0x98, 0x00, 0x00, 0xFF, 0xFF, 0xF0,
+  0x00, 0x83, 0xFF, 0xFF, 0x81, 0xE0, 0x01, 0xFF,
+  0xFF, 0xF8, 0x02, 0x07, 0xFF, 0xFF, 0x80, 0x3F,
+  0x07, 0xE0, 0x00, 0x1C, 0x0C, 0x1F, 0xFF, 0xFF,
+  0xF8, 0x03, 0xFF, 0x80, 0x00, 0x1F, 0x78, 0x1F,
+  0xFF, 0xFF, 0xFF, 0x80, 0x7F, 0x00, 0x07, 0x0F,
+  0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFE, 0x0C, 0x07,
+  0xFF, 0x83, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0x00, 0x1F, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+unsigned char linux_logo16_red[] __initdata = {
+    0x00, 0x90, 0xb0, 0x9c, 0xf7, 0x35, 0x83, 0xa5,
+    0x65, 0x8f, 0x98, 0xc9, 0xdb, 0xe1, 0xe7, 0xf8
+};
+
+unsigned char linux_logo16_green[] __initdata = {
+    0x00, 0x90, 0xb0, 0x9c, 0xf7, 0x2e, 0x83, 0xa5,
+    0x65, 0x6e, 0x98, 0x89, 0xbf, 0xac, 0xda, 0xf8
+};
+
+unsigned char linux_logo16_blue[] __initdata = {
+    0x00, 0x90, 0xaf, 0x9c, 0xf7, 0x2b, 0x82, 0xa5,
+    0x65, 0x41, 0x97, 0x1e, 0x60, 0x29, 0xa5, 0xf8
+};
+
+unsigned char linux_logo16[] __initdata = {
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa1, 0x11, 0x11,
+    0x61, 0x16, 0x66, 0x66, 0x11, 0x11, 0x11, 0x11,
+    0x11, 0x11, 0x1a, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0xa8, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x87, 0x77, 0x77, 0x77, 0x77,
+    0x77, 0x77, 0x73, 0x33, 0x33, 0x3a, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xa3, 0x33, 0x33, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x77, 0x77,
+    0x77, 0x27, 0x77, 0x77, 0x77, 0x33, 0x3a, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xa3, 0x33, 0x33, 0x30, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x55, 0x50, 0x08, 0x33, 0x77, 0x77,
+    0x77, 0x72, 0x72, 0x27, 0x77, 0x77, 0x33, 0x33,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xa3, 0x33, 0x33, 0x77, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x58, 0x85, 0x00, 0x11, 0x11, 0xaa,
+    0xa3, 0x37, 0x77, 0x72, 0x22, 0x22, 0x77, 0x73,
+    0x33, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa3,
+    0x33, 0x37, 0x77, 0x33, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x56, 0x85, 0x00, 0x06, 0x66, 0x11,
+    0x11, 0x1a, 0xa3, 0x37, 0x77, 0x72, 0x22, 0x77,
+    0x73, 0x33, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33,
+    0x33, 0x33, 0x33, 0x30, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x55, 0x00, 0x00, 0x06, 0x66, 0x66,
+    0x66, 0x66, 0x11, 0x1a, 0xa3, 0x77, 0x72, 0x22,
+    0x77, 0x73, 0x3a, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x33,
+    0x33, 0x33, 0x33, 0xa0, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,
+    0x66, 0x66, 0x66, 0x66, 0x11, 0xa3, 0x77, 0x22,
+    0x22, 0x77, 0x33, 0x33, 0xaa, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x33, 0x33,
+    0x33, 0x3a, 0xa1, 0x10, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x33,
+    0xaa, 0x11, 0x16, 0x66, 0x66, 0x61, 0x1a, 0x37,
+    0x22, 0x22, 0x77, 0x33, 0x3a, 0xaa, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xa3, 0x33, 0x33, 0x33,
+    0x3a, 0xa1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x22,
+    0x22, 0x77, 0x3a, 0x11, 0x66, 0x66, 0x66, 0x1a,
+    0x37, 0x22, 0x22, 0x77, 0x33, 0x3a, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x33, 0x33, 0x3a,
+    0xa1, 0x11, 0x11, 0x10, 0x00, 0x00, 0x50, 0x00,
+    0x00, 0x05, 0x80, 0x50, 0x00, 0x00, 0x07, 0x72,
+    0x22, 0x22, 0x22, 0x73, 0xa1, 0x66, 0x66, 0x61,
+    0x1a, 0x77, 0x22, 0x27, 0x73, 0x33, 0xaa, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x33, 0x3a, 0xaa,
+    0x11, 0x11, 0x1a, 0xa0, 0x08, 0x71, 0x05, 0x00,
+    0x00, 0x12, 0x22, 0x50, 0x00, 0x00, 0x07, 0x77,
+    0x77, 0x72, 0x22, 0x22, 0x27, 0x31, 0x16, 0x66,
+    0x61, 0x13, 0x77, 0x22, 0x77, 0x33, 0x3a, 0xaa,
+    0xaa, 0xaa, 0xaa, 0xa3, 0x33, 0x33, 0xaa, 0xa1,
+    0x11, 0x1a, 0x33, 0x70, 0x07, 0x2e, 0x70, 0x00,
+    0x01, 0x44, 0x42, 0x60, 0x00, 0x00, 0x02, 0x22,
+    0x22, 0x22, 0x22, 0x22, 0x22, 0x27, 0x31, 0x66,
+    0x66, 0x61, 0xa3, 0x72, 0x22, 0x77, 0x33, 0xaa,
+    0xaa, 0xaa, 0xa3, 0x33, 0x33, 0xaa, 0xaa, 0x11,
+    0x1a, 0x33, 0x77, 0x30, 0x04, 0x82, 0x40, 0x00,
+    0x54, 0x48, 0x54, 0x40, 0x00, 0x00, 0x01, 0xaa,
+    0x32, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x31,
+    0x66, 0x66, 0x11, 0x37, 0x22, 0x27, 0x73, 0x3a,
+    0xaa, 0xaa, 0xa3, 0x33, 0x3a, 0xaa, 0xaa, 0xaa,
+    0xa3, 0x77, 0xaa, 0x10, 0x50, 0x08, 0x46, 0x05,
+    0x54, 0x80, 0x50, 0x42, 0x00, 0x00, 0x08, 0x66,
+    0x66, 0x1a, 0x32, 0x22, 0x22, 0x22, 0x22, 0x27,
+    0x31, 0x66, 0x66, 0x13, 0x72, 0x22, 0x77, 0x33,
+    0xaa, 0xaa, 0xaa, 0x33, 0xaa, 0xa1, 0xaa, 0xa3,
+    0x37, 0xa1, 0x1a, 0x30, 0x50, 0x06, 0x26, 0x00,
+    0x54, 0x00, 0x00, 0x44, 0x00, 0x00, 0x08, 0xe2,
+    0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0x22, 0x22,
+    0x27, 0xa6, 0x66, 0x61, 0xa7, 0x72, 0x27, 0x73,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33,
+    0x31, 0x11, 0x37, 0x70, 0x02, 0x00, 0xab, 0xbb,
+    0xb6, 0x00, 0x00, 0xf4, 0x00, 0x00, 0xee, 0xee,
+    0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0x22,
+    0x22, 0x23, 0x16, 0x66, 0x1a, 0x37, 0x22, 0x77,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa3, 0x3a,
+    0x11, 0xa7, 0x33, 0x10, 0x04, 0x09, 0xbd, 0xdd,
+    0xbd, 0xd0, 0x04, 0x45, 0x00, 0x0e, 0xee, 0xee,
+    0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0x22,
+    0x22, 0x22, 0x71, 0x66, 0x66, 0x13, 0x72, 0x27,
+    0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x11,
+    0xa3, 0x73, 0xa1, 0x60, 0x08, 0xbd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdb, 0x90, 0x00, 0x02, 0xec, 0xee,
+    0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xce, 0x22,
+    0x22, 0x22, 0x27, 0xa6, 0x66, 0x61, 0x37, 0x27,
+    0x1a, 0xaa, 0xaa, 0xaa, 0xaa, 0xa3, 0xa1, 0x1a,
+    0x33, 0xa1, 0x16, 0x60, 0x0b, 0xbd, 0xdd, 0xdd,
+    0xcd, 0xdd, 0xdd, 0xd9, 0x00, 0x00, 0xec, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0xa2,
+    0x22, 0x22, 0x22, 0x7a, 0x66, 0x66, 0x13, 0x77,
+    0x1a, 0xaa, 0xaa, 0xaa, 0xaa, 0x3a, 0x11, 0x33,
+    0xaa, 0x11, 0x66, 0x60, 0x9b, 0xdd, 0xdd, 0xdd,
+    0xcd, 0xdd, 0xdb, 0xb9, 0x00, 0x00, 0xec, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xee, 0x61,
+    0x72, 0x22, 0x22, 0x22, 0xa1, 0x66, 0x61, 0x37,
+    0x1a, 0xaa, 0xaa, 0xaa, 0xa3, 0xa1, 0x13, 0x3a,
+    0x11, 0x11, 0x11, 0x10, 0x5b, 0xdd, 0xdd, 0xdc,
+    0xdd, 0xdd, 0xbd, 0xd9, 0x00, 0x00, 0xec, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xee, 0x86,
+    0x17, 0x22, 0x22, 0x22, 0x23, 0x16, 0x66, 0xaa,
+    0xaa, 0xa3, 0x3a, 0xaa, 0xaa, 0x1a, 0x3a, 0xa1,
+    0x11, 0x11, 0x1a, 0x70, 0x05, 0xbd, 0xdd, 0xdd,
+    0xdb, 0x5b, 0xdd, 0xb0, 0x00, 0x60, 0x2e, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xe6, 0x88,
+    0x66, 0x32, 0x22, 0x22, 0x22, 0x36, 0x66, 0x11,
+    0x33, 0x33, 0x3a, 0xaa, 0x11, 0xaa, 0xaa, 0xa1,
+    0x11, 0x1a, 0x3a, 0x60, 0x02, 0x99, 0xbb, 0xb9,
+    0x9b, 0xbb, 0xbc, 0x22, 0x00, 0x86, 0x5e, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xe1, 0x68,
+    0x86, 0x63, 0x22, 0x22, 0x22, 0x2a, 0x66, 0x66,
+    0x33, 0x33, 0xaa, 0xaa, 0x1a, 0xaa, 0xaa, 0x11,
+    0x1a, 0xa7, 0x68, 0x80, 0x02, 0x2b, 0xbd, 0xbb,
+    0xbb, 0xb9, 0x22, 0x22, 0x00, 0x06, 0x6e, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xc7, 0xa6,
+    0x88, 0x86, 0x32, 0x22, 0x22, 0x27, 0xa6, 0x66,
+    0x33, 0x3a, 0xaa, 0xa1, 0xaa, 0xaa, 0xa1, 0x11,
+    0xa3, 0xa6, 0x88, 0x80, 0x02, 0x22, 0x9b, 0xbb,
+    0xbb, 0x22, 0x24, 0xf4, 0x60, 0x00, 0x0c, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xc2, 0x21,
+    0x68, 0x88, 0x63, 0x22, 0x22, 0x22, 0x71, 0x66,
+    0x33, 0x3a, 0x11, 0x11, 0xaa, 0xaa, 0x11, 0xaa,
+    0x71, 0x88, 0x88, 0x00, 0x02, 0xe2, 0x26, 0x99,
+    0x22, 0x22, 0x4f, 0xf4, 0x40, 0x00, 0x0c, 0xcc,
+    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x22, 0x22,
+    0x16, 0x88, 0x86, 0xa2, 0x22, 0x22, 0x27, 0x11,
+    0x33, 0xa1, 0x11, 0x11, 0xaa, 0x31, 0x1a, 0xa3,
+    0x68, 0x88, 0x81, 0x00, 0x54, 0x42, 0x22, 0x22,
+    0x22, 0x44, 0xff, 0xff, 0x48, 0x00, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x22, 0x22,
+    0x21, 0x88, 0x88, 0x6a, 0x22, 0x22, 0x22, 0x31,
+    0x3a, 0xa1, 0x11, 0x1a, 0xa3, 0x11, 0x33, 0x36,
+    0x88, 0x86, 0x30, 0x00, 0x4f, 0x44, 0x22, 0x22,
+    0x24, 0xff, 0xff, 0xff, 0x44, 0x00, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x95, 0x22, 0x72,
+    0x22, 0x18, 0x88, 0x86, 0x32, 0x22, 0x22, 0x27,
+    0xaa, 0x11, 0x11, 0x1a, 0x31, 0x13, 0x33, 0x68,
+    0x88, 0x6a, 0x00, 0x02, 0x4f, 0x4f, 0x42, 0x24,
+    0x4f, 0xff, 0xff, 0xff, 0xf4, 0x50, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x22, 0x73,
+    0x72, 0x26, 0x88, 0x88, 0x63, 0x22, 0x22, 0x22,
+    0x11, 0x11, 0x11, 0xa3, 0xa1, 0x73, 0xa6, 0x88,
+    0x81, 0xa5, 0x00, 0x04, 0x4f, 0x4f, 0x44, 0x4f,
+    0xff, 0xff, 0xff, 0xff, 0xf4, 0x40, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x12, 0x27,
+    0xaa, 0x22, 0x68, 0x55, 0x86, 0x72, 0x22, 0x22,
+    0x11, 0x11, 0x1a, 0x33, 0x13, 0x3a, 0x18, 0x88,
+    0x1a, 0x10, 0x00, 0x44, 0x4f, 0x4f, 0xff, 0x4f,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x61, 0x22,
+    0x3a, 0xa2, 0x26, 0x85, 0x58, 0x67, 0x22, 0x22,
+    0x61, 0x61, 0x1a, 0x7a, 0x37, 0x31, 0x88, 0x81,
+    0x11, 0x00, 0x05, 0xe4, 0x44, 0xff, 0xff, 0xff,
+    0x4f, 0xf4, 0x44, 0xff, 0xff, 0xf5, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x88, 0x12,
+    0x2a, 0xaa, 0x72, 0x68, 0x55, 0x81, 0x22, 0x22,
+    0x66, 0x61, 0xa3, 0x33, 0x73, 0x16, 0x88, 0x11,
+    0x10, 0x00, 0x08, 0x74, 0x44, 0x4f, 0x44, 0x44,
+    0xf4, 0xf4, 0x44, 0x44, 0xe2, 0x44, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x88, 0x81,
+    0x22, 0xaa, 0xa7, 0x26, 0x85, 0x88, 0x12, 0x22,
+    0x66, 0x61, 0x37, 0xa7, 0x3a, 0x66, 0x66, 0x11,
+    0x80, 0x00, 0x0a, 0x72, 0x44, 0x4f, 0x44, 0x4f,
+    0xff, 0x44, 0x44, 0x22, 0x22, 0x24, 0x00, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x85, 0x88,
+    0x12, 0x2a, 0xaa, 0x22, 0x68, 0x58, 0x63, 0x22,
+    0x66, 0x1a, 0x73, 0x77, 0x31, 0x66, 0x61, 0x11,
+    0x00, 0x00, 0x07, 0x44, 0xff, 0x4f, 0xf4, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0x42, 0x22, 0x40, 0x9b,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb9, 0x85, 0x55,
+    0x81, 0x27, 0xaa, 0xa2, 0x78, 0x88, 0x86, 0x72,
+    0x66, 0x13, 0x77, 0x73, 0x11, 0x66, 0x61, 0x76,
+    0x00, 0x50, 0x84, 0xf4, 0xff, 0x4f, 0xf4, 0xff,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x42, 0x40, 0x9b,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb9, 0x68, 0x55,
+    0x58, 0x12, 0x3a, 0xaa, 0x23, 0x88, 0x88, 0xa7,
+    0x66, 0xa7, 0x77, 0x7a, 0x16, 0x66, 0x1a, 0x15,
+    0x05, 0x00, 0x4f, 0xf4, 0xff, 0x4f, 0xf4, 0xff,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x44, 0x24, 0x9b,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb9, 0x26, 0x55,
+    0x55, 0x81, 0x23, 0xaa, 0x32, 0x18, 0x88, 0x6a,
+    0x61, 0x37, 0x77, 0x31, 0x66, 0x66, 0x17, 0x60,
+    0x05, 0x08, 0x4f, 0xf4, 0xff, 0x4f, 0xf4, 0xff,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x4e, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0xa2, 0x65,
+    0x55, 0x58, 0xa2, 0x7a, 0xa2, 0x26, 0x88, 0x61,
+    0x61, 0x32, 0x27, 0xa1, 0x66, 0x61, 0x31, 0x60,
+    0x00, 0x04, 0x4f, 0xf4, 0xff, 0x44, 0x44, 0xff,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x44, 0xf4, 0x99,
+    0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x9b, 0xaa, 0x26,
+    0x55, 0x55, 0x87, 0x27, 0x33, 0x27, 0x68, 0x61,
+    0x1a, 0x72, 0x27, 0xa6, 0x66, 0x6a, 0x71, 0x00,
+    0x80, 0x84, 0xff, 0xf4, 0xff, 0x44, 0x44, 0xff,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x44, 0xf4, 0x99,
+    0x9b, 0x9b, 0x99, 0xb9, 0xb9, 0x99, 0xaa, 0xa2,
+    0x85, 0x55, 0x56, 0x22, 0x27, 0x22, 0x36, 0x66,
+    0x13, 0x22, 0x23, 0x16, 0x86, 0x63, 0x73, 0x00,
+    0x00, 0x44, 0xf4, 0xf4, 0xff, 0x44, 0x44, 0xff,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x4f, 0x99,
+    0x9b, 0x99, 0x99, 0x99, 0xb9, 0x99, 0xaa, 0xaa,
+    0x28, 0x55, 0x58, 0x12, 0x22, 0x22, 0x21, 0x11,
+    0xa3, 0x27, 0x7a, 0x66, 0x86, 0x17, 0x75, 0x05,
+    0x05, 0xff, 0xf4, 0xf4, 0xff, 0x44, 0x44, 0xff,
+    0xff, 0x4f, 0x44, 0x4f, 0x4f, 0x44, 0x4f, 0x99,
+    0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x3a, 0xaa,
+    0xa2, 0x85, 0x58, 0x67, 0x72, 0x22, 0x27, 0xa1,
+    0x37, 0x27, 0x7a, 0x68, 0x86, 0xa2, 0x70, 0x00,
+    0x02, 0xff, 0xf4, 0xf4, 0xff, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0xf4, 0xf4, 0xf4, 0x99,
+    0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x23, 0xaa,
+    0xa7, 0x78, 0x88, 0x81, 0x77, 0x22, 0x27, 0x3a,
+    0x72, 0x73, 0x71, 0x68, 0x66, 0x32, 0x50, 0x00,
+    0x04, 0x4f, 0xf4, 0xf4, 0xff, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0xf4, 0xf4, 0x44, 0x95,
+    0x99, 0x99, 0x99, 0x99, 0x99, 0x55, 0x12, 0x3a,
+    0xaa, 0x21, 0x88, 0x81, 0x77, 0x27, 0x73, 0x73,
+    0x72, 0x33, 0x36, 0x86, 0x61, 0x72, 0x00, 0x00,
+    0x04, 0x44, 0xf4, 0xf4, 0xf4, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x44, 0x55,
+    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x8a, 0x27,
+    0xaa, 0x77, 0x68, 0x61, 0x23, 0x71, 0x11, 0x3a,
+    0x27, 0xa3, 0x36, 0x86, 0x61, 0x20, 0x00, 0x00,
+    0x04, 0xf4, 0xf4, 0xf4, 0xf4, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x41, 0x59,
+    0x99, 0x99, 0x99, 0x99, 0x99, 0x95, 0x58, 0x77,
+    0x27, 0x32, 0x36, 0x63, 0x23, 0x71, 0x66, 0x11,
+    0x27, 0x13, 0xa6, 0x86, 0x6a, 0x20, 0x00, 0x50,
+    0x04, 0x4f, 0x4f, 0x4f, 0x4f, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x41, 0x99,
+    0x9b, 0xbb, 0xbb, 0xbb, 0xb9, 0x99, 0x68, 0x13,
+    0x32, 0x22, 0x73, 0xa7, 0x2a, 0x31, 0x88, 0x66,
+    0x7a, 0x13, 0x18, 0x66, 0x63, 0x20, 0x00, 0x06,
+    0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x49, 0x95,
+    0xa9, 0xa9, 0x99, 0x97, 0x92, 0x99, 0x65, 0x6a,
+    0x17, 0x22, 0x23, 0x72, 0x27, 0xaa, 0x88, 0x88,
+    0xa1, 0x17, 0x68, 0x66, 0x67, 0x70, 0x00, 0x05,
+    0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xff, 0xf4, 0xf4, 0x49, 0x9c,
+    0x2e, 0xee, 0xee, 0xee, 0xee, 0xa9, 0x65, 0x8a,
+    0x1a, 0xaa, 0x37, 0x72, 0x27, 0x37, 0x88, 0x88,
+    0x11, 0x17, 0x68, 0x66, 0x67, 0x10, 0x9d, 0xd0,
+    0x84, 0x44, 0xff, 0x4f, 0x4f, 0x44, 0xf4, 0x4f,
+    0xff, 0x4f, 0x44, 0xff, 0xf4, 0xf4, 0x4f, 0x69,
+    0xcc, 0xee, 0xee, 0xee, 0xec, 0x99, 0x88, 0x63,
+    0x61, 0x68, 0x61, 0x72, 0x22, 0x7a, 0x68, 0x88,
+    0x11, 0x17, 0x88, 0x66, 0x12, 0x1b, 0xdd, 0xdd,
+    0x02, 0x44, 0x4f, 0x4f, 0x4f, 0x44, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xff, 0xff, 0x4f, 0x4c, 0xc5,
+    0x0c, 0xc1, 0x11, 0x1c, 0xc0, 0x26, 0x66, 0x17,
+    0x66, 0x88, 0x88, 0x12, 0x22, 0x23, 0xa8, 0x88,
+    0x11, 0x13, 0x88, 0x66, 0x17, 0xbb, 0xdd, 0xdd,
+    0xd0, 0x8f, 0xff, 0xf4, 0xf4, 0x44, 0xf4, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0x4f, 0x44, 0xdd, 0xdd,
+    0x00, 0x00, 0x00, 0x05, 0x9d, 0x21, 0x66, 0x27,
+    0xa6, 0x65, 0x58, 0x67, 0x22, 0x27, 0x28, 0x88,
+    0x11, 0xaa, 0x86, 0x68, 0x1a, 0xbb, 0xdd, 0xdd,
+    0xdb, 0x05, 0xf4, 0xf4, 0xf4, 0xf4, 0x44, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0xf4, 0xf4, 0xdd, 0xdb,
+    0x00, 0x00, 0x00, 0x00, 0xdd, 0xda, 0x66, 0x22,
+    0x71, 0x15, 0x55, 0x81, 0x22, 0x22, 0x76, 0x88,
+    0x11, 0x31, 0x88, 0x88, 0xab, 0xbd, 0xdd, 0xdd,
+    0xdd, 0x00, 0x04, 0x44, 0xff, 0xff, 0x4f, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0xf4, 0x44, 0xdd, 0xdb,
+    0x00, 0x00, 0x00, 0x0b, 0xdd, 0xda, 0x11, 0x22,
+    0x23, 0x68, 0x55, 0x86, 0x22, 0x22, 0x7a, 0x88,
+    0x1a, 0x71, 0x88, 0x89, 0xbb, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xd0, 0x00, 0x4f, 0x44, 0xff, 0x4f, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0xff, 0xe2, 0xdd, 0xdb,
+    0x90, 0x00, 0x05, 0xbd, 0xdd, 0xb8, 0x63, 0x22,
+    0x27, 0xa6, 0x55, 0x88, 0x77, 0x22, 0x22, 0x88,
+    0x1a, 0x28, 0xbd, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdb, 0x00, 0x07, 0x44, 0x4f, 0x4f, 0x4f,
+    0xff, 0x4f, 0x44, 0x4f, 0x4f, 0x22, 0xdd, 0xdb,
+    0xbb, 0x9b, 0xbb, 0xbd, 0xdd, 0xd5, 0x86, 0x22,
+    0x22, 0x77, 0x85, 0x88, 0x17, 0x22, 0x22, 0x88,
+    0xaa, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0x00, 0x00, 0x54, 0x4f, 0x4f, 0x4f,
+    0xff, 0x4f, 0x44, 0xf4, 0x44, 0x22, 0xbd, 0xdd,
+    0xbb, 0xbb, 0xbb, 0xdd, 0xdd, 0xdd, 0x88, 0x72,
+    0x27, 0x22, 0x88, 0x88, 0x67, 0x72, 0x22, 0x18,
+    0x33, 0x2d, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xd0, 0x00, 0x05, 0x4f, 0x4f, 0x4f,
+    0xff, 0x4f, 0x44, 0x44, 0x4f, 0x22, 0xbd, 0xdd,
+    0xdb, 0xbb, 0xdd, 0xdd, 0xdd, 0xdd, 0x88, 0x17,
+    0x27, 0x72, 0x68, 0x88, 0x87, 0x32, 0x22, 0x36,
+    0x37, 0x2d, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xd5, 0x00, 0x00, 0x4f, 0x4f, 0x4f,
+    0xff, 0xf4, 0xf4, 0xf4, 0xf4, 0x22, 0xbb, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd8, 0x67,
+    0x72, 0x77, 0x38, 0x88, 0x83, 0x37, 0x22, 0x26,
+    0x72, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0x00, 0x00, 0x4f, 0x4f, 0x4f,
+    0xff, 0xf4, 0xf4, 0xf4, 0x44, 0x25, 0xbb, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd3,
+    0x32, 0x73, 0x76, 0x88, 0x81, 0x33, 0x22, 0x2a,
+    0x22, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xb0, 0x54, 0x4f, 0x4f, 0x4f,
+    0xff, 0xf4, 0xf4, 0xff, 0x44, 0x00, 0xbb, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xa7, 0x73, 0x26, 0x88, 0x86, 0x7a, 0x72, 0x27,
+    0x22, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdb, 0x44, 0xff, 0x4f, 0x4f,
+    0xff, 0xf4, 0xf4, 0x44, 0x40, 0x05, 0xbb, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0x13, 0x23, 0x21, 0x68, 0x86, 0x17, 0x72, 0x22,
+    0x22, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdb, 0x44, 0x4f, 0x4f, 0x4f,
+    0xff, 0xff, 0x44, 0x42, 0x00, 0x05, 0xbd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0x87, 0x27, 0x27, 0x16, 0x66, 0x67, 0x22, 0x22,
+    0x72, 0x7b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0x94, 0x44, 0x44, 0x44,
+    0x44, 0x44, 0x44, 0x00, 0x00, 0x05, 0xbb, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xb8,
+    0x86, 0x22, 0x22, 0x7a, 0x68, 0x81, 0x22, 0x22,
+    0x37, 0x7b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdb, 0xb5, 0x44, 0x44, 0x44,
+    0x44, 0x47, 0x00, 0x00, 0x00, 0x05, 0xbd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd8, 0x68,
+    0x58, 0x72, 0x22, 0x27, 0x18, 0x86, 0x72, 0x22,
+    0x1a, 0xbb, 0xbd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdb, 0xb5, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xbb, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xb9, 0x18, 0x85,
+    0x58, 0x12, 0x22, 0x36, 0x18, 0x88, 0x32, 0x22,
+    0x61, 0x3b, 0xbb, 0xbb, 0xbd, 0xdd, 0xdd, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdb, 0xb9, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xbb, 0xdd,
+    0xdd, 0xdd, 0xdd, 0xdd, 0xb9, 0x7a, 0x68, 0x85,
+    0x88, 0x62, 0x27, 0x16, 0x18, 0x88, 0x12, 0x27,
+    0x86, 0x18, 0x9b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbd,
+    0xdd, 0xdd, 0xdd, 0xbb, 0xb5, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xbb, 0xbd,
+    0xdd, 0xdd, 0xdb, 0xbb, 0x87, 0x31, 0x68, 0x65,
+    0x88, 0x82, 0x23, 0x16, 0x18, 0x88, 0x12, 0x23,
+    0x88, 0x67, 0x27, 0xa8, 0x9b, 0xbb, 0xbb, 0xbb,
+    0xbd, 0xdd, 0xbb, 0xbb, 0x95, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x9b, 0xbb,
+    0xbb, 0xbb, 0xbb, 0x96, 0x87, 0x16, 0x68, 0x18,
+    0x88, 0x62, 0x31, 0x66, 0x18, 0x88, 0x62, 0x73,
+    0x88, 0x63, 0x27, 0x33, 0x65, 0x55, 0x99, 0x9b,
+    0xbb, 0xbb, 0xbb, 0x99, 0x55, 0x0a, 0xa1, 0x86,
+    0x81, 0x68, 0x88, 0x55, 0x58, 0x85, 0x9b, 0xbb,
+    0xbb, 0xbb, 0x95, 0x88, 0x83, 0x66, 0x66, 0x18,
+    0x66, 0x82, 0xa1, 0x66, 0x18, 0x88, 0x62, 0x33,
+    0x88, 0x81, 0x27, 0x7a, 0x18, 0x58, 0x86, 0x85,
+    0x99, 0x99, 0x99, 0x95, 0x53, 0x2a, 0xaa, 0x88,
+    0x67, 0x31, 0x68, 0x55, 0x58, 0x85, 0x59, 0xbb,
+    0xbb, 0xb9, 0x58, 0x68, 0x83, 0x66, 0x61, 0x16,
+    0x66, 0x62, 0x16, 0x66, 0x68, 0x88, 0x62, 0xaa,
+    0x88, 0x86, 0x27, 0x77, 0x78, 0x55, 0x88, 0x22,
+    0x25, 0x55, 0x95, 0x55, 0x6a, 0xa2, 0x2a, 0x88,
+    0x62, 0x27, 0x37, 0x38, 0x88, 0x87, 0x55, 0x59,
+    0x95, 0x58, 0x16, 0x88, 0x8a, 0x66, 0x63, 0x68,
+    0x86, 0x67, 0x66, 0x66, 0x68, 0x88, 0x12, 0x11,
+    0x88, 0x88, 0x72, 0x77, 0x78, 0x85, 0x58, 0x17,
+    0x23, 0x32, 0x55, 0x55, 0x81, 0x13, 0x73, 0x66,
+    0x62, 0x7a, 0xaa, 0x38, 0x88, 0x58, 0x27, 0x55,
+    0x58, 0x32, 0x38, 0x88, 0x81, 0x66, 0xa2, 0x88,
+    0x86, 0x61, 0x66, 0x61, 0x66, 0x68, 0x13, 0x11,
+    0x88, 0x88, 0x12, 0x22, 0x71, 0x85, 0x58, 0x62,
+    0x23, 0xa2, 0x68, 0x88, 0x81, 0x66, 0x88, 0x88,
+    0x63, 0x2a, 0xaa, 0x28, 0x88, 0x55, 0x86, 0x61,
+    0x66, 0x66, 0x68, 0x88, 0x66, 0x66, 0x77, 0x88,
+    0x68, 0x16, 0x66, 0x62, 0x66, 0x68, 0xa1, 0x61,
+    0x88, 0x88, 0x62, 0x22, 0x22, 0x85, 0x55, 0x83,
+    0x72, 0x37, 0xa8, 0x88, 0x61, 0x66, 0x85, 0x55,
+    0x86, 0x23, 0xaa, 0x71, 0x88, 0x85, 0x88, 0x66,
+    0x88, 0x86, 0x88, 0x88, 0x16, 0x61, 0x21, 0x88,
+    0x66, 0xa6, 0x86, 0x17, 0x66, 0x66, 0x31, 0x61,
+    0x88, 0x88, 0x87, 0x72, 0x22, 0x68, 0x55, 0x86,
+    0x77, 0x77, 0x36, 0x88, 0x13, 0x68, 0x85, 0x55,
+    0x58, 0x12, 0x73, 0x72, 0x76, 0x88, 0x88, 0x68,
+    0x88, 0x88, 0x88, 0x66, 0x36, 0x63, 0x26, 0x86,
+    0x86, 0x36, 0x86, 0x11, 0x66, 0x66, 0x76, 0x61,
+    0x88, 0x88, 0x81, 0x22, 0x22, 0x38, 0x85, 0x58,
+    0x37, 0x22, 0x21, 0x68, 0xa2, 0x31, 0x68, 0x55,
+    0x55, 0x81, 0x22, 0x22, 0xa8, 0x88, 0x88, 0x68,
+    0x86, 0x88, 0x68, 0x81, 0x36, 0x17, 0x21, 0x68,
+    0x86, 0x16, 0x66, 0x26, 0x66, 0x61, 0x36, 0x66,
+    0x68, 0x88, 0x86, 0x27, 0x22, 0x28, 0x88, 0x88,
+    0x17, 0x72, 0x2a, 0x66, 0xa2, 0x22, 0x36, 0x55,
+    0x55, 0x58, 0x37, 0x3a, 0x16, 0x66, 0x66, 0x66,
+    0x66, 0x18, 0x88, 0x67, 0x16, 0x12, 0x71, 0x68,
+    0x81, 0x68, 0x61, 0x76, 0x66, 0x6a, 0x16, 0x66,
+    0x88, 0x88, 0x86, 0x77, 0x22, 0x26, 0x88, 0x88,
+    0x13, 0x37, 0x71, 0x66, 0xa2, 0x33, 0x2a, 0x85,
+    0x55, 0x55, 0x17, 0x73, 0x16, 0x66, 0x66, 0x68,
+    0x63, 0x88, 0x88, 0xa2, 0x66, 0xa2, 0xa6, 0x88,
+    0x61, 0x68, 0x6a, 0x76, 0x66, 0x6a, 0x66, 0x6a
+};
 
 #else
 
diff -u --recursive --new-file v2.1.119/linux/include/asm-m68k/pgtable.h linux/include/asm-m68k/pgtable.h
--- v2.1.119/linux/include/asm-m68k/pgtable.h	Tue Aug 18 22:02:07 1998
+++ linux/include/asm-m68k/pgtable.h	Wed Sep  2 09:39:18 1998
@@ -16,6 +16,9 @@
 /* For virtual address to physical address conversion */
 extern unsigned long mm_vtop(unsigned long addr) __attribute__ ((const));
 extern unsigned long mm_ptov(unsigned long addr) __attribute__ ((const));
+
+#include<asm/virtconvert.h>
+
 #define VTOP(addr)  (mm_vtop((unsigned long)(addr)))
 #define PTOV(addr)  (mm_ptov((unsigned long)(addr)))
 
@@ -126,7 +129,7 @@
 			      "cpushp %%dc,(%0)\n\t"
 			      "cinvp %%ic,(%0)\n\t"
 			      ".chip 68k"
-			      : : "a" (VTOP(address)));
+			      : : "a" (virt_to_phys((void *)address)));
     }
     else {
 	unsigned long _tmp;
@@ -151,7 +154,7 @@
 				  "cpushp %%dc,(%0)\n\t"
 				  "cinvp %%ic,(%0)\n\t"
 				  ".chip 68k"
-				  : : "a" (VTOP(address)));
+				  : : "a" (virt_to_phys((void *)address)));
 	    address += PAGE_SIZE;
 	}
     }
@@ -423,9 +426,9 @@
  * and a page entry and page directory to the page they refer to.
  */
 #define mk_pte(page, pgprot) \
-({ pte_t __pte; pte_val(__pte) = VTOP(page) + pgprot_val(pgprot); __pte; })
+({ pte_t __pte; pte_val(__pte) = virt_to_phys((void *)page) + pgprot_val(pgprot); __pte; })
 #define mk_pte_phys(physpage, pgprot) \
-({ pte_t __pte; pte_val(__pte) = VTOP(physpage) + pgprot_val(pgprot); __pte; })
+({ pte_t __pte; pte_val(__pte) = virt_to_phys((void *)physpage) + pgprot_val(pgprot); __pte; })
 
 extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 { pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot); return pte; }
@@ -434,7 +437,7 @@
 {
 	int i;
 
-	ptep = (pte_t *) VTOP(ptep);
+	ptep = (pte_t *) virt_to_phys(ptep);
 	for (i = 0; i < 16; i++, ptep += PTRS_PER_PTE/16)
 		pmdp->pmd[i] = _PAGE_TABLE | _PAGE_ACCESSED | (unsigned long)ptep;
 }
@@ -444,23 +447,23 @@
 {
 	int i;
 
-	ptep = (pte_t *) VTOP(ptep);
+	ptep = (pte_t *) virt_to_phys(ptep);
 	for (i = 0; i < 16; i++, ptep += PTRS_PER_PTE/16)
 		pmdp->pmd[i] = _PAGE_PRESENT | _PAGE_ACCESSED | (unsigned long)ptep;
 }
 
 extern inline void pgd_set(pgd_t * pgdp, pmd_t * pmdp)
-{ pgd_val(*pgdp) = _PAGE_TABLE | _PAGE_ACCESSED | VTOP(pmdp); }
+{ pgd_val(*pgdp) = _PAGE_TABLE | _PAGE_ACCESSED | virt_to_phys(pmdp); }
 
 extern inline unsigned long pte_page(pte_t pte)
-{ return PTOV(pte_val(pte) & PAGE_MASK); }
+{ return (unsigned long)phys_to_virt((unsigned long)(pte_val(pte) & PAGE_MASK)); }
 
 extern inline unsigned long pmd_page2(pmd_t *pmd)
-{ return PTOV(pmd_val(*pmd) & _TABLE_MASK); }
+{ return (unsigned long)phys_to_virt((unsigned long)(pmd_val(*pmd) & _TABLE_MASK)); }
 #define pmd_page(pmd) pmd_page2(&(pmd))
 
 extern inline unsigned long pgd_page(pgd_t pgd)
-{ return PTOV(pgd_val(pgd) & _TABLE_MASK); }
+{ return (unsigned long)phys_to_virt((unsigned long)(pgd_val(pgd) & _TABLE_MASK)); }
 
 extern inline int pte_none(pte_t pte)		{ return !pte_val(pte); }
 extern inline int pte_present(pte_t pte)	{ return pte_val(pte) & (_PAGE_PRESENT | _PAGE_FAKE_SUPER); }
@@ -517,7 +520,7 @@
 extern inline void SET_PAGE_DIR(struct task_struct * tsk, pgd_t * pgdir)
 {
 	tsk->tss.crp[0] = 0x80000000 | _PAGE_TABLE;
-	tsk->tss.crp[1] = VTOP(pgdir);
+	tsk->tss.crp[1] = virt_to_phys(pgdir);
 	if (tsk == current) {
 		if (CPU_IS_040_OR_060)
 			__asm__ __volatile__ (".chip 68040\n\t"
@@ -632,7 +635,7 @@
 	ret = pte_quicklist;
 	if (ret) {
 		pte_quicklist = (unsigned long *)*ret;
-		ret[0] = ret[1];
+		ret[0] = 0;
 		quicklists.pgtable_cache_sz -= 8;
 	}
 	return (pte_t *)ret;
@@ -658,7 +661,7 @@
 	ret = pmd_quicklist;
 	if (ret) {
 		pmd_quicklist = (unsigned long *)*ret;
-		ret[0] = ret[1];
+		ret[0] = 0;
 		quicklists.pgtable_cache_sz--;
 	}
 	return (pmd_t *)ret;
@@ -799,7 +802,18 @@
  * Check if the addr/len goes up to the end of a physical
  * memory chunk.  Used for DMA functions.
  */
+#ifdef CONFIG_SINGLE_MEMORY_CHUNK
+/*
+ * It makes no sense to consider whether we cross a memory boundary if
+ * we support just one physical chunk of memory.
+ */
+extern inline int mm_end_of_chunk (unsigned long addr, int len)
+{
+	return 0;
+}
+#else
 int mm_end_of_chunk (unsigned long addr, int len);
+#endif
 
 /*
  * Map some physical address range into the kernel address space.
diff -u --recursive --new-file v2.1.119/linux/include/asm-m68k/setup.h linux/include/asm-m68k/setup.h
--- v2.1.119/linux/include/asm-m68k/setup.h	Tue Jun 23 10:01:28 1998
+++ linux/include/asm-m68k/setup.h	Wed Sep  2 09:39:18 1998
@@ -43,7 +43,7 @@
 #ifdef __KERNEL__
 
 #ifndef __ASSEMBLY__
-extern u_long m68k_machtype;
+extern unsigned long m68k_machtype;
 #endif /* !__ASSEMBLY__ */
 
 #if !defined(CONFIG_AMIGA)
@@ -186,9 +186,9 @@
 #ifdef __KERNEL__
 
 #ifndef __ASSEMBLY__
-extern u_long m68k_cputype;
-extern u_long m68k_fputype;
-extern u_long m68k_mmutype;			/* Not really used yet */
+extern unsigned long m68k_cputype;
+extern unsigned long m68k_fputype;
+extern unsigned long m68k_mmutype;			/* Not really used yet */
 
     /*
      *  m68k_is040or060 is != 0 for a '040 or higher;
@@ -260,29 +260,23 @@
 
 #define CPU_TYPE (m68k_cputype)
 
-#endif /* __KERNEL__ */
-
-
     /*
      *  Miscellaneous
      */
 
-#define NUM_MEMINFO	(4)
-#define CL_SIZE		(256)
+#define NUM_MEMINFO	4
+#define CL_SIZE		256
 
 #ifndef __ASSEMBLY__
+extern int m68k_num_memory;		/* # of memory blocks found */
+extern struct mem_info m68k_memory[NUM_MEMINFO];/* memory description */
 
 struct mem_info {
 	unsigned long addr;		/* physical address of memory chunk */
 	unsigned long size;		/* length of memory chunk (in bytes) */
 };
+#endif
 
-#ifdef __KERNEL__
-extern int m68k_num_memory;		/* # of memory blocks found */
-extern struct mem_info m68k_memory[NUM_MEMINFO];/* memory description */
 #endif /* __KERNEL__ */
-
-#endif /* !__ASSEMBLY__ */
-
 
 #endif /* _M68K_SETUP_H */
diff -u --recursive --new-file v2.1.119/linux/include/asm-m68k/softirq.h linux/include/asm-m68k/softirq.h
--- v2.1.119/linux/include/asm-m68k/softirq.h	Tue Aug 18 22:02:07 1998
+++ linux/include/asm-m68k/softirq.h	Wed Sep  2 09:39:18 1998
@@ -61,6 +61,6 @@
 /* These are for the irq's testing the lock */
 #define softirq_trylock(cpu)  (local_bh_count[cpu] ? 0 : (local_bh_count[cpu]=1))
 #define softirq_endlock(cpu)  (local_bh_count[cpu] = 0)
-#define synchronize_bh()	do { } while (0)
+#define synchronize_bh()	barrier()
 
 #endif
diff -u --recursive --new-file v2.1.119/linux/include/asm-m68k/spinlock.h linux/include/asm-m68k/spinlock.h
--- v2.1.119/linux/include/asm-m68k/spinlock.h	Sat May 24 09:10:24 1997
+++ linux/include/asm-m68k/spinlock.h	Wed Sep  2 09:39:18 1998
@@ -5,8 +5,16 @@
  * We don't do SMP on the m68k .... at least not yet.
  */
 
-typedef struct { int dummy; } spinlock_t;
+/*
+ * Gcc-2.7.x has a nasty bug with empty initializers.
+ */
+#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)
+typedef struct { } spinlock_t;
+#define SPIN_LOCK_UNLOCKED { }
+#else
+typedef struct { int gcc_is_buggy; } spinlock_t;
 #define SPIN_LOCK_UNLOCKED { 0 }
+#endif
 
 #define spin_lock_init(lock)	do { } while(0)
 #define spin_lock(lock)		do { } while(0)
@@ -31,8 +39,8 @@
  * irq-safe write-lock, but readers can get non-irqsafe
  * read-locks.
  */
-typedef struct { int dummy; } rwlock_t;
-#define RW_LOCK_UNLOCKED { 0 }
+typedef struct { } rwlock_t;
+#define RW_LOCK_UNLOCKED { }
 
 #define read_lock(lock)		do { } while(0)
 #define read_unlock(lock)	do { } while(0)
diff -u --recursive --new-file v2.1.119/linux/include/asm-m68k/uaccess.h linux/include/asm-m68k/uaccess.h
--- v2.1.119/linux/include/asm-m68k/uaccess.h	Wed Aug 26 11:37:44 1998
+++ linux/include/asm-m68k/uaccess.h	Wed Sep  2 09:39:18 1998
@@ -157,12 +157,24 @@
 	 "6:\n"
 	 ".section .fixup,\"ax\"\n"
 	 "   .even\n"
-	 "7: lsll #2,%2\n"
+	 "7: movel %2,%%d0\n"
+	 "71:clrl (%0)+\n"
+	 "   subql #1,%%d0\n"
+	 "   jne 71b\n"
+	 "   lsll #2,%2\n"
 	 "   addl %4,%2\n"
+	 "   btst #1,%4\n"
+	 "   jne 81f\n"
+	 "   btst #0,%4\n"
+	 "   jne 91f\n"
 	 "   jra 6b\n"
 	 "8: addql #2,%2\n"
+	 "81:clrw (%0)+\n"
+	 "   btst #0,%4\n"
+	 "   jne 91f\n"
 	 "   jra 6b\n"
 	 "9: addql #1,%2\n"
+	 "91:clrb (%0)+\n"
 	 "   jra 6b\n"
          ".previous\n"
 	 ".section __ex_table,\"a\"\n"
@@ -233,7 +245,11 @@
 	 "    jne 10b\n"				\
 	 ".section .fixup,\"ax\"\n"			\
 	 "    .even\n"					\
-	 "11: lsll #2,%2\n"				\
+	 "11: movel %2,%%d0\n"				\
+	 "13: clrl (%0)+\n"				\
+	 "    subql #1,%%d0\n"				\
+	 "    jne 13b\n"				\
+	 "    lsll #2,%2\n"				\
 	 fixup "\n"					\
 	 "    jra 12f\n"				\
 	 ".previous\n"					\
@@ -261,6 +277,7 @@
 	     ".section .fixup,\"ax\"\n"
 	     "   .even\n"
 	     "3: addql #1,%2\n"
+	     "   clrb (%0)+\n"
 	     "   jra 2b\n"
 	     ".previous\n"
 	     ".section __ex_table,\"a\"\n"
@@ -279,6 +296,7 @@
 	     ".section .fixup,\"ax\"\n"
 	     "   .even\n"
 	     "3: addql #2,%2\n"
+	     "   clrw (%0)+\n"
 	     "   jra 2b\n"
 	     ".previous\n"
 	     ".section __ex_table,\"a\"\n"
@@ -299,7 +317,9 @@
 	     ".section .fixup,\"ax\"\n"
 	     "   .even\n"
 	     "4: addql #2,%2\n"
+	     "   clrw (%0)+\n"
 	     "5: addql #1,%2\n"
+	     "   clrb (%0)+\n"
 	     "   jra 3b\n"
 	     ".previous\n"
 	     ".section __ex_table,\"a\"\n"
@@ -319,6 +339,7 @@
 	     ".section .fixup,\"ax\"\n"
 	     "   .even\n"
 	     "3: addql #4,%2\n"
+	     "   clrl (%0)+\n"
 	     "   jra 2b\n"
 	     ".previous\n"
 	     ".section __ex_table,\"a\"\n"
@@ -339,7 +360,9 @@
 	     ".section .fixup,\"ax\"\n"
 	     "   .even\n"
 	     "4: addql #4,%2\n"
+	     "   clrl (%0)+\n"
 	     "5: addql #4,%2\n"
+	     "   clrl (%0)+\n"
 	     "   jra 3b\n"
 	     ".previous\n"
 	     ".section __ex_table,\"a\"\n"
@@ -363,8 +386,11 @@
 	     ".section .fixup,\"ax\"\n"
 	     "   .even\n"
 	     "5: addql #4,%2\n"
+	     "   clrl (%0)+\n"
 	     "6: addql #4,%2\n"
+	     "   clrl (%0)+\n"
 	     "7: addql #4,%2\n"
+	     "   clrl (%0)+\n"
 	     "   jra 4b\n"
 	     ".previous\n"
 	     ".section __ex_table,\"a\"\n"
@@ -391,9 +417,13 @@
 	     ".section .fixup,\"ax\"\n"
 	     "   .even\n"
 	     "6: addql #4,%2\n"
+	     "   clrl (%0)+\n"
 	     "7: addql #4,%2\n"
+	     "   clrl (%0)+\n"
 	     "8: addql #4,%2\n"
+	     "   clrl (%0)+\n"
 	     "9: addql #4,%2\n"
+	     "   clrl (%0)+\n"
 	     "   jra 5b\n"
 	     ".previous\n"
 	     ".section __ex_table,\"a\"\n"
@@ -415,7 +445,8 @@
 	case 1:
 	    __copy_from_user_big(to, from, n,
 				 /* fixup */
-				 "1: addql #1,%2",
+				 "1: addql #1,%2\n"
+				 "   clrb (%0)+",
 				 /* copy */
 				 "2: movesb (%1)+,%%d0\n"
 				 "   moveb %%d0,(%0)+\n"
@@ -426,7 +457,8 @@
 	case 2:
 	    __copy_from_user_big(to, from, n,
 				 /* fixup */
-				 "1: addql #2,%2",
+				 "1: addql #2,%2\n"
+				 "   clrw (%0)+",
 				 /* copy */
 				 "2: movesw (%1)+,%%d0\n"
 				 "   movew %%d0,(%0)+\n"
@@ -438,7 +470,9 @@
 	    __copy_from_user_big(to, from, n,
 				 /* fixup */
 				 "1: addql #2,%2\n"
-				 "2: addql #1,%2",
+				 "   clrw (%0)+\n"
+				 "2: addql #1,%2\n"
+				 "   clrb (%0)+",
 				 /* copy */
 				 "3: movesw (%1)+,%%d0\n"
 				 "   movew %%d0,(%0)+\n"
@@ -712,16 +746,9 @@
 }
 
 #define copy_from_user(to, from, n)		\
-{ void *__to = (to);				\
-  void *__from = (from);			\
-  unsigned long __n = (n);			\
-  char *__end = (char *)__to + __n;		\
-  unsigned long __res =				\
 (__builtin_constant_p(n) ?			\
  __constant_copy_from_user(to, from, n) :	\
- __generic_copy_from_user(to, from, n));	\
-  if (__res) memset(__end - __res, 0, __res);	\
-  res; }
+ __generic_copy_from_user(to, from, n))
 
 #define copy_to_user(to, from, n)		\
 (__builtin_constant_p(n) ?			\
diff -u --recursive --new-file v2.1.119/linux/include/asm-m68k/unistd.h linux/include/asm-m68k/unistd.h
--- v2.1.119/linux/include/asm-m68k/unistd.h	Tue Aug 18 22:02:07 1998
+++ linux/include/asm-m68k/unistd.h	Wed Sep  2 09:39:18 1998
@@ -27,7 +27,7 @@
 #define __NR_lseek		 19
 #define __NR_getpid		 20
 #define __NR_mount		 21
-#define __NR_oldumount		 22
+#define __NR_umount		 22
 #define __NR_setuid		 23
 #define __NR_getuid		 24
 #define __NR_stime		 25
@@ -57,7 +57,7 @@
 #define __NR_geteuid		 49
 #define __NR_getegid		 50
 #define __NR_acct		 51
-#define __NR_umount		 52
+#define __NR_umount2		 52
 #define __NR_lock		 53
 #define __NR_ioctl		 54
 #define __NR_fcntl		 55
@@ -315,6 +315,8 @@
 static inline _syscall0(int,sync)
 static inline _syscall0(pid_t,setsid)
 static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
+static inline _syscall3(int,read,int,fd,char *,buf,off_t,count)
+static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count)
 static inline _syscall1(int,dup,int,fd)
 static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp)
 static inline _syscall3(int,open,const char *,file,int,flag,int,mode)
diff -u --recursive --new-file v2.1.119/linux/include/asm-m68k/virtconvert.h linux/include/asm-m68k/virtconvert.h
--- v2.1.119/linux/include/asm-m68k/virtconvert.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-m68k/virtconvert.h	Wed Sep  2 09:39:18 1998
@@ -0,0 +1,76 @@
+#ifndef __VIRT_CONVERT__
+#define __VIRT_CONVERT__
+
+/*
+ * Macros used for converting between virtual and physical mappings.
+ */
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <asm/setup.h>
+
+#ifdef CONFIG_AMIGA
+#include <asm/amigahw.h>
+#endif
+
+/*
+ * Change virtual addresses to physical addresses and vv.
+ */
+extern unsigned long mm_vtop(unsigned long addr) __attribute__ ((const));
+extern unsigned long mm_vtop_fallback (unsigned long);
+extern unsigned long mm_ptov(unsigned long addr) __attribute__ ((const));
+
+#ifdef CONFIG_SINGLE_MEMORY_CHUNK
+extern inline unsigned long virt_to_phys(volatile void * address)
+{
+	unsigned long voff = (unsigned long) address;
+
+	if (voff < m68k_memory[0].size)
+		return m68k_memory[0].addr + voff;
+	else
+		return mm_vtop_fallback(voff);
+}
+
+extern inline void * phys_to_virt(unsigned long paddr)
+{
+	unsigned long base = m68k_memory[0].addr;
+
+	if ((paddr >=  base) && (paddr < (base + m68k_memory[0].size)))
+		return (void *)(paddr - base);
+#ifdef CONFIG_AMIGA
+	/*
+	 * if on an amiga and address is in first 16M, move it 
+	 * to the ZTWO_VADDR range
+	 */
+	if (MACH_IS_AMIGA && paddr < 16*1024*1024)
+		return (void *)ZTWO_VADDR(paddr);
+#endif
+	return (void *)paddr;
+}
+#else
+extern inline unsigned long virt_to_phys(volatile void * address)
+{
+	return mm_vtop((unsigned long)address);
+}
+
+extern inline void * phys_to_virt(unsigned long address)
+{
+	return (void *) mm_ptov(address);
+}
+#endif
+
+/*
+ * IO bus memory addresses are 1:1 with the physical address,
+ * except on the PCI bus of the Hades.
+ */
+#ifdef CONFIG_HADES
+#define virt_to_bus(a) (virt_to_phys(a) + (MACH_IS_HADES ? 0x80000000 : 0))
+#define bus_to_virt(a) (phys_to_virt((a) - (MACH_IS_HADES ? 0x80000000 : 0)))
+#else
+#define virt_to_bus virt_to_phys
+#define bus_to_virt phys_to_virt
+#endif
+
+#endif
+#endif
diff -u --recursive --new-file v2.1.119/linux/include/linux/byteorder/generic.h linux/include/linux/byteorder/generic.h
--- v2.1.119/linux/include/linux/byteorder/generic.h	Wed Jun 24 22:54:11 1998
+++ linux/include/linux/byteorder/generic.h	Tue Sep  1 10:50:11 1998
@@ -5,18 +5,18 @@
  * linux/byteorder_generic.h
  * Generic Byte-reordering support
  *
- * Francois-Rene Rideau <rideau@ens.fr> 19970707
+ * Francois-Rene Rideau <fare@tunes.org> 19970707
  *    gathered all the good ideas from all asm-foo/byteorder.h into one file,
  *    cleaned them up.
  *    I hope it is compliant with non-GCC compilers.
  *    I decided to put __BYTEORDER_HAS_U64__ in byteorder.h,
  *    because I wasn't sure it would be ok to put it in types.h
  *    Upgraded it to 2.1.43
- * Francois-Rene Rideau <rideau@ens.fr> 19971012
+ * Francois-Rene Rideau <fare@tunes.org> 19971012
  *    Upgraded it to 2.1.57
  *    to please Linus T., replaced huge #ifdef's between little/big endian
  *    by nestedly #include'd files.
- * Francois-Rene Rideau <rideau@ens.fr> 19971205
+ * Francois-Rene Rideau <fare@tunes.org> 19971205
  *    Made it to 2.1.71; now a facelift:
  *    Put files under include/linux/byteorder/
  *    Split swab from generic support.
@@ -31,6 +31,11 @@
  *    nybble swapping support...
  *   = every architecture could add their byteswap macro in asm/byteorder.h
  *    see how some architectures already do (i386, alpha, ppc, etc)
+ *   = cpu_to_beXX and beXX_to_cpu might some day need to be well
+ *    distinguished throughout the kernel. This is not the case currently,
+ *    since little endian, big endian, and pdp endian machines needn't it.
+ *    But this might be the case for, say, a port of Linux to 20/21 bit
+ *    architectures (and F21 Linux addict around?).
  */
 
 /*
diff -u --recursive --new-file v2.1.119/linux/include/linux/byteorder/swab.h linux/include/linux/byteorder/swab.h
--- v2.1.119/linux/include/linux/byteorder/swab.h	Wed Jun 24 22:54:11 1998
+++ linux/include/linux/byteorder/swab.h	Tue Sep  1 10:50:11 1998
@@ -6,7 +6,7 @@
  * Byte-swapping, independently from CPU endianness
  *	swabXX[ps]?(foo)
  *
- * Francois-Rene Rideau <rideau@ens.fr> 19971205
+ * Francois-Rene Rideau <fare@tunes.org> 19971205
  *    separated swab functions from cpu_to_XX,
  *    to clean up support for bizarre-endian architectures.
  *
@@ -15,10 +15,13 @@
  *
  */
 
+/* casts are necessary for constants, because we never know how for sure
+ * how U/UL/ULL map to __u16, __u32, __u64. At least not in a portable way.
+ */
 #define ___swab16(x) \
 	((__u16)( \
-		(((__u16)(x) & 0x00ffU) << 8) | \
-		(((__u16)(x) & 0xff00U) >> 8) ))
+		(((__u16)(x) & (__u16)0x00ffU) << 8) | \
+		(((__u16)(x) & (__u16)0xff00U) >> 8) ))
 #define ___swab32(x) \
 	((__u32)( \
 		(((__u32)(x) & (__u32)0x000000ffUL) << 24) | \
diff -u --recursive --new-file v2.1.119/linux/include/linux/byteorder/swabb.h linux/include/linux/byteorder/swabb.h
--- v2.1.119/linux/include/linux/byteorder/swabb.h	Mon Jan 12 19:47:22 1998
+++ linux/include/linux/byteorder/swabb.h	Tue Sep  1 10:50:11 1998
@@ -8,8 +8,8 @@
  *
  * Support for obNUXIous pdp-endian and other bizarre architectures.
  * Will Linux ever run on such ancient beasts? if not, this file
- * will be but a programming pearl. Still, it's a reminder that
- * cpu_to_beXX and beXX_to_cpu should be well distinguished.
+ * will be but a programming pearl. Still, it's a reminder that we
+ * shouldn't be making too many assumptions when trying to be portable.
  *
  */
 
diff -u --recursive --new-file v2.1.119/linux/include/linux/coda.h linux/include/linux/coda.h
--- v2.1.119/linux/include/linux/coda.h	Thu Jul 16 18:09:29 1998
+++ linux/include/linux/coda.h	Mon Aug 31 15:46:10 1998
@@ -72,6 +72,7 @@
 #define	C_O_WRITE       0x002
 #define C_O_TRUNC       0x010
 #define C_O_EXCL	0x100
+#define C_O_CREAT	0x200
 
 /* these are to find mode bits in Venus */ 
 #define C_M_READ  00400
@@ -80,6 +81,7 @@
 /* for access Venus will use */
 #define C_A_R_OK    4               /* Test for read permission.  */
 #define C_A_W_OK    2               /* Test for write permission.  */
+#define C_A_C_OK    8               /* Test for writing upon create.  */
 #define C_A_X_OK    1               /* Test for execute permission.  */
 #define C_A_F_OK    0               /* Test for existence.  */
 
@@ -148,8 +150,8 @@
 
 #ifndef _VUID_T_
 #define _VUID_T_
-typedef unsigned int vuid_t;
-typedef unsigned int vgid_t;
+typedef u_int32_t vuid_t;
+typedef u_int32_t vgid_t;
 #endif /*_VUID_T_ */
 
 #ifndef _CODACRED_T_
diff -u --recursive --new-file v2.1.119/linux/include/linux/coda_fs_i.h linux/include/linux/coda_fs_i.h
--- v2.1.119/linux/include/linux/coda_fs_i.h	Fri Jul 31 17:05:52 1998
+++ linux/include/linux/coda_fs_i.h	Mon Aug 31 15:46:10 1998
@@ -43,7 +43,7 @@
 int coda_cnode_make(struct inode **, struct ViceFid *, struct super_block *);
 int coda_cnode_makectl(struct inode **inode, struct super_block *sb);
 struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb);
-
+void coda_replace_fid(struct inode *, ViceFid *, ViceFid *);
 
 #endif
 #endif
diff -u --recursive --new-file v2.1.119/linux/include/linux/coda_linux.h linux/include/linux/coda_linux.h
--- v2.1.119/linux/include/linux/coda_linux.h	Sun Jun  7 11:16:39 1998
+++ linux/include/linux/coda_linux.h	Mon Aug 31 15:46:10 1998
@@ -51,7 +51,6 @@
 int coda_fid_is_weird(struct ViceFid *fid);
 int coda_iscontrol(const char *name, size_t length);
 
-
 void coda_load_creds(struct coda_cred *cred);
 int coda_mycred(struct coda_cred *);
 void coda_vattr_to_iattr(struct inode *, struct coda_vattr *);
@@ -70,6 +69,11 @@
 int coda_inode_grab(dev_t dev, ino_t ino, struct inode **ind);
 
 #define NB_SFS_SIZ 0x895440
+
+/* cache.c */
+void coda_purge_children(struct inode *);
+void coda_purge_dentries(struct inode *);
+
 
 /* debugging masks */
 #define D_SUPER     1   /* print results returned by Venus */ 
diff -u --recursive --new-file v2.1.119/linux/include/linux/cyclades.h linux/include/linux/cyclades.h
--- v2.1.119/linux/include/linux/cyclades.h	Tue Aug 18 22:02:07 1998
+++ linux/include/linux/cyclades.h	Thu Sep  3 16:02:52 1998
@@ -754,6 +754,13 @@
 #define CyTBPR		(0x72*2)
 #define CyTCOR		(0x76*2)
 
+/* Custom Registers */
+
+#define	CyPLX_VER	(0x3400)
+#define	PLX_9050	0x11
+#define	PLX_9060	0x12
+#define	PLX_9080	0x13
+
 /***************************************************************************/
 
 #endif /* __KERNEL__ */
diff -u --recursive --new-file v2.1.119/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.1.119/linux/include/linux/fs.h	Wed Aug 26 11:37:44 1998
+++ linux/include/linux/fs.h	Fri Sep  4 19:24:22 1998
@@ -272,6 +272,7 @@
 #include <linux/smb_fs_i.h>
 #include <linux/hfs_fs_i.h>
 #include <linux/adfs_fs_i.h>
+#include <linux/qnx4_fs_i.h>
 
 /*
  * Attribute flags.  These should be or-ed together to figure out what
@@ -381,6 +382,7 @@
 		struct smb_inode_info		smbfs_i;
 		struct hfs_inode_info		hfs_i;
 		struct adfs_inode_info		adfs_i;
+		struct qnx4_inode_info		qnx4_i;	   
 		struct socket			socket_i;
 		void				*generic_ip;
 	} u;
@@ -534,6 +536,7 @@
 #include <linux/smb_fs_sb.h>
 #include <linux/hfs_fs_sb.h>
 #include <linux/adfs_fs_sb.h>
+#include <linux/qnx4_fs_sb.h>
 
 extern struct list_head super_blocks;
 
@@ -575,6 +578,7 @@
 		struct smb_sb_info	smbfs_sb;
 		struct hfs_sb_info	hfs_sb;
 		struct adfs_sb_info	adfs_sb;
+		struct qnx4_sb_info	qnx4_sb;	   
 		void			*generic_sbp;
 	} u;
 };
diff -u --recursive --new-file v2.1.119/linux/include/linux/genhd.h linux/include/linux/genhd.h
--- v2.1.119/linux/include/linux/genhd.h	Tue Aug 18 22:02:07 1998
+++ linux/include/linux/genhd.h	Mon Aug 31 12:13:41 1998
@@ -49,7 +49,7 @@
 	unsigned char end_cyl;		/* end cylinder */
 	unsigned int start_sect;	/* starting sector counting from 0 */
 	unsigned int nr_sects;		/* nr of sectors in partition */
-};
+} __attribute__((packed));
 
 struct hd_struct {
 	long start_sect;
diff -u --recursive --new-file v2.1.119/linux/include/linux/if.h linux/include/linux/if.h
--- v2.1.119/linux/include/linux/if.h	Fri Jul 31 17:07:27 1998
+++ linux/include/linux/if.h	Thu Aug 27 19:33:08 1998
@@ -114,6 +114,7 @@
 		int	ifru_mtu;
 		struct  ifmap ifru_map;
 		char	ifru_slave[IFNAMSIZ];	/* Just fits the size */
+		char	ifru_newname[IFNAMSIZ];
 		__kernel_caddr_t	ifru_data;
 	} ifr_ifru;
 };
@@ -133,6 +134,7 @@
 #define ifr_ifindex	ifr_ifru.ifru_ivalue	/* interface index	*/
 #define ifr_bandwidth	ifr_ifru.ifru_ivalue    /* link bandwidth	*/
 #define ifr_qlen	ifr_ifru.ifru_ivalue	/* Queue length 	*/
+#define ifr_newname	ifr_ifru.ifru_newname	/* New name		*/
 
 /*
  * Structure used in SIOCGIFCONF request.
diff -u --recursive --new-file v2.1.119/linux/include/linux/if_packet.h linux/include/linux/if_packet.h
--- v2.1.119/linux/include/linux/if_packet.h	Mon Jan 12 15:28:18 1998
+++ linux/include/linux/if_packet.h	Thu Aug 27 19:33:08 1998
@@ -25,9 +25,10 @@
 #define PACKET_BROADCAST	1		/* To all		*/
 #define PACKET_MULTICAST	2		/* To group		*/
 #define PACKET_OTHERHOST	3		/* To someone else 	*/
-#define PACKET_OUTGOING		4		/* Originated by us	*/
-#define PACKET_LOOPBACK		5
-#define PACKET_FASTROUTE	6
+#define PACKET_OUTGOING		4		/* Outgoing of any type */
+/* These ones are invisible by user level */
+#define PACKET_LOOPBACK		5		/* MC/BRD frame looped back */
+#define PACKET_FASTROUTE	6		/* Fastrouted frame	*/
 
 /* Packet socket options */
 
diff -u --recursive --new-file v2.1.119/linux/include/linux/in6.h linux/include/linux/in6.h
--- v2.1.119/linux/include/linux/in6.h	Fri Jul 31 17:07:27 1998
+++ linux/include/linux/in6.h	Thu Aug 27 19:33:08 1998
@@ -35,8 +35,15 @@
 		__u16		u6_addr16[8];
 		__u32		u6_addr32[4];
 #if (~0UL) > 0xffffffff
+#ifndef __RELAX_IN6_ADDR_ALIGNMENT
+		/* Alas, protocols do not respect 64bit alignmnet.
+		   rsvp/pim/... are broken. However, it is good
+		   idea to force correct alignment always, when
+		   it is possible.
+		 */
 		__u64		u6_addr64[2];
 #endif
+#endif
 	} in6_u;
 #define s6_addr			in6_u.u6_addr8
 #define s6_addr16		in6_u.u6_addr16
@@ -101,19 +108,34 @@
 #define IPPROTO_DSTOPTS		60	/* IPv6 destination options	*/
 
 /*
+ *	IPv6 TLV options.
+ */
+#define IPV6_TLV_PAD0		0
+#define IPV6_TLV_PADN		1
+#define IPV6_TLV_ROUTERALERT	20
+#define IPV6_TLV_JUMBO		194
+
+/*
  *	IPV6 socket options
  */
 
 #define IPV6_ADDRFORM		1
 #define IPV6_PKTINFO		2
-#define IPV6_RXHOPOPTS		3 /* obsolete name */
-#define IPV6_RXDSTOPTS		4 /* obsolete name */
-#define IPV6_HOPOPTS		IPV6_RXHOPOPTS  /* new name */
-#define IPV6_DSTOPTS		IPV6_RXDSTOPTS  /* new name */
-#define IPV6_RXSRCRT		5
+#define IPV6_HOPOPTS		3
+#define IPV6_DSTOPTS		4
+#define IPV6_RTHDR		5
 #define IPV6_PKTOPTIONS		6
 #define IPV6_CHECKSUM		7
 #define IPV6_HOPLIMIT		8
+#define IPV6_NEXTHOP		9
+#define IPV6_AUTHHDR		10
+
+#if 0
+/* Aliases for obsolete names */
+#define IPV6_RXHOPOPTS		IPV6_HOPOPTS
+#define IPV6_RXDSTOPTS		IPV6_DSTOPTS
+#define IPV6_RXSRCRT		IPV6_RTHDR
+#endif
 
 /*
  *	Alternative names
diff -u --recursive --new-file v2.1.119/linux/include/linux/ipv6.h linux/include/linux/ipv6.h
--- v2.1.119/linux/include/linux/ipv6.h	Fri Jul 31 17:07:27 1998
+++ linux/include/linux/ipv6.h	Fri Sep  4 19:20:50 1998
@@ -4,6 +4,9 @@
 #include <linux/in6.h>
 #include <asm/byteorder.h>
 
+/* The latest drafts declared increase in minimal mtu up to 1280. */
+
+#define IPV6_MIN_MTU	1280
 
 /*
  *	Advanced API
@@ -58,8 +61,6 @@
 #define ipv6_optlen(p)  (((p)->hdrlen+1) << 3)
 #endif
 
-
-
 /*
  *	routing header type 0 (used in cmsghdr struct)
  */
@@ -72,10 +73,11 @@
 #define rt0_type		rt_hdr.type;
 };
 
-#ifdef __KERNEL__
-
 /*
  *	IPv6 fixed header
+ *
+ *	BEWARE, it is incorrect. The first 4 bits of flow_lbl
+ *	are glued to priority now, forming "class".
  */
 
 struct ipv6hdr {
@@ -87,7 +89,7 @@
 				priority:4;
 #else
 #error	"Please fix <asm/byteorder.h>"
-#endif						
+#endif
 	__u8			flow_lbl[3];
 
 	__u16			payload_len;
@@ -98,27 +100,23 @@
 	struct	in6_addr	daddr;
 };
 
-/*
- *	The length of this struct cannot be greater than the length of
- *	the proto_priv field in a sk_buff which is currently
- *	defined to be 16 bytes.
- *	Pointers take upto 8 bytes (sizeof(void *) is 8 on the alpha).
- */
-struct ipv6_options 
-{
-	/* length of extension headers   */
-
-	__u16			opt_flen;	/* after fragment hdr */
-	__u16			opt_nflen;	/* before fragment hdr */
+#ifdef __KERNEL__
 
-	/* 
-	 * protocol options 
-	 * usually carried in IPv6 extension headers
-	 */
+/* 
+   This structure contains results of exthdrs parsing
+   as offsets from skb->nh.
+ */
 
-	struct ipv6_rt_hdr		*srcrt;	/* Routing Header */
+struct inet6_skb_parm
+{
+	int			iif;
+	__u16			ra;
+	__u16			hop;
+	__u16			auth;
+	__u16			dst0;
+	__u16			srcrt;
+	__u16			dst1;
 };
-
 
 #endif
 
diff -u --recursive --new-file v2.1.119/linux/include/linux/ipv6_route.h linux/include/linux/ipv6_route.h
--- v2.1.119/linux/include/linux/ipv6_route.h	Thu Mar 26 15:57:05 1998
+++ linux/include/linux/ipv6_route.h	Thu Aug 27 19:33:08 1998
@@ -26,7 +26,6 @@
 #define RTF_ALLONLINK	0x00020000	/* fallback, no routers on link	*/
 #define RTF_ADDRCONF	0x00040000	/* addrconf route - RA		*/
 
-#define RTF_LINKRT	0x00100000	/* link specific - device match	*/
 #define RTF_NONEXTHOP	0x00200000	/* route with no nexthop	*/
 #define RTF_EXPIRES	0x00400000
 
diff -u --recursive --new-file v2.1.119/linux/include/linux/nbd.h linux/include/linux/nbd.h
--- v2.1.119/linux/include/linux/nbd.h	Tue Aug 18 22:02:07 1998
+++ linux/include/linux/nbd.h	Fri Aug 28 10:38:32 1998
@@ -12,6 +12,7 @@
 #ifdef MAJOR_NR
 
 #include <linux/locks.h>
+#include <asm/semaphore.h>
 
 #define LOCAL_END_REQUEST
 
@@ -42,11 +43,13 @@
 	int harderror;		/* Code of hard error			*/
 #define NBD_READ_ONLY 0x0001
 #define NBD_WRITE_NOCHK 0x0002
+#define NBD_INITIALISED 0x0004
 	struct socket * sock;
 	struct file * file; 		/* If == NULL, device is not ready, yet	*/
 	int magic;			/* FIXME: not if debugging is off	*/
 	struct request *head;	/* Requests are added here...			*/
 	struct request *tail;
+	struct semaphore queue_lock;
 };
 
 /* This now IS in some kind of include file...	*/
diff -u --recursive --new-file v2.1.119/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.1.119/linux/include/linux/netdevice.h	Fri Jul 31 17:07:27 1998
+++ linux/include/linux/netdevice.h	Fri Sep  4 19:24:35 1998
@@ -232,8 +232,7 @@
 	
 	unsigned short		flags;	/* interface flags (a la BSD)	*/
 	unsigned short		gflags;
-	unsigned short		metric;	/* routing metric (not used)	*/
-	unsigned short		mtu;	/* interface MTU value		*/
+	unsigned		mtu;	/* interface MTU value		*/
 	unsigned short		type;	/* interface hardware type	*/
 	unsigned short		hard_header_len;	/* hardware hdr length	*/
 	void			*priv;	/* pointer to private data	*/
diff -u --recursive --new-file v2.1.119/linux/include/linux/netlink.h linux/include/linux/netlink.h
--- v2.1.119/linux/include/linux/netlink.h	Sat May  2 14:19:54 1998
+++ linux/include/linux/netlink.h	Thu Aug 27 19:33:08 1998
@@ -16,8 +16,8 @@
 {
 	sa_family_t	nl_family;	/* AF_NETLINK	*/
 	unsigned short	nl_pad;		/* zero		*/
-	__kernel_pid_t	nl_pid;		/* process pid	*/
-	unsigned	nl_groups;	/* multicast groups mask */
+	__u32		nl_pid;		/* process pid	*/
+       	__u32		nl_groups;	/* multicast groups mask */
 };
 
 struct nlmsghdr
@@ -26,7 +26,7 @@
 	__u16		nlmsg_type;	/* Message content */
 	__u16		nlmsg_flags;	/* Additional flags */
 	__u32		nlmsg_seq;	/* Sequence number */
-	__kernel_pid_t	nlmsg_pid;	/* Sending process PID */
+	__u32		nlmsg_pid;	/* Sending process PID */
 };
 
 /* Flags values */
@@ -64,7 +64,7 @@
 #define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
 #define NLMSG_NEXT(nlh,len)	 ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
 				  (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
-#define NLMSG_OK(nlh,len) ((nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
+#define NLMSG_OK(nlh,len) ((len) > 0 && (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
 			   (nlh)->nlmsg_len <= (len))
 #define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
 
@@ -86,10 +86,11 @@
 struct netlink_skb_parms
 {
 	struct ucred		creds;		/* Skb credentials	*/
-	pid_t			pid;
-	unsigned		groups;
-	pid_t			dst_pid;
-	unsigned		dst_groups;
+	__u32			pid;
+	__u32			groups;
+	__u32			dst_pid;
+	__u32			dst_groups;
+	kernel_cap_t		eff_cap;
 };
 
 #define NETLINK_CB(skb)		(*(struct netlink_skb_parms*)&((skb)->cb))
@@ -102,10 +103,10 @@
 extern int init_netlink(void);
 extern struct sock *netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len));
 extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err);
-extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, pid_t pid, int nonblock);
-extern void netlink_broadcast(struct sock *ssk, struct sk_buff *skb, pid_t pid,
-			      unsigned group, int allocation);
-extern void netlink_set_err(struct sock *ssk, pid_t pid, unsigned group, int code);
+extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock);
+extern void netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid,
+			      __u32 group, int allocation);
+extern void netlink_set_err(struct sock *ssk, __u32 pid, __u32 group, int code);
 
 /*
  *	skb should fit one page. This choice is good for headerless malloc.
@@ -125,28 +126,8 @@
 	long		args[4];
 };
 
-#if 0
-
-void* nlmsg_broadcast(struct sock*, unsigned long type, int len, unsigned groups);
-struct skb_buff *nlmsg_alloc(unsigned long type, int len,
-			     unsigned long seq, unsigned long pid, int allocation);
-void __nlmsg_transmit(struct sock*, int allocation);
-
-extern __inline__ void nlmsg_release(struct sk_buff *skb)
-{
-	atomic_dec(skb->users);
-}
-
-extern __inline__ void nlmsg_transmit(struct sk_buff *sk, int allocation)
-{
-	if (sk->write_queue.qlen)
-		__nlmsg_transmit(sk, allocation);
-}
-
-#endif
-
 extern __inline__ struct nlmsghdr *
-__nlmsg_put(struct sk_buff *skb, pid_t pid, u32 seq, int type, int len)
+__nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len)
 {
 	struct nlmsghdr *nlh;
 	int size = NLMSG_LENGTH(len);
diff -u --recursive --new-file v2.1.119/linux/include/linux/nfsd/nfsfh.h linux/include/linux/nfsd/nfsfh.h
--- v2.1.119/linux/include/linux/nfsd/nfsfh.h	Fri Jul 31 17:10:04 1998
+++ linux/include/linux/nfsd/nfsfh.h	Fri Sep  4 19:21:54 1998
@@ -110,9 +110,9 @@
 static __inline__ struct svc_fh *
 fh_copy(struct svc_fh *dst, struct svc_fh *src)
 {
-	if (src->fh_dverified) {
+	if (src->fh_dverified || src->fh_locked) {
 		struct dentry *dentry = src->fh_dentry;
-		printk("fh_copy: copying %s/%s, already verified!\n",
+		printk(KERN_ERR "fh_copy: copying %s/%s, already verified!\n",
 			dentry->d_parent->d_name.name, dentry->d_name.name);
 	}
 			
@@ -133,18 +133,24 @@
 static inline void
 fh_lock(struct svc_fh *fhp)
 {
-	struct inode	*inode = fhp->fh_dentry->d_inode;
+	struct dentry	*dentry = fhp->fh_dentry;
+	struct inode	*inode;
 
 	/*
 	dfprintk(FILEOP, "nfsd: fh_lock(%x/%ld) locked = %d\n",
 			SVCFH_DEV(fhp), SVCFH_INO(fhp), fhp->fh_locked);
 	 */
+	if (!fhp->fh_dverified) {
+		printk(KERN_ERR "fh_lock: fh not verified!\n");
+		return;
+	}
 	if (fhp->fh_locked) {
 		printk(KERN_WARNING "fh_lock: %s/%s already locked!\n",
-			fhp->fh_dentry->d_parent->d_name.name,
-			fhp->fh_dentry->d_name.name);
+			dentry->d_parent->d_name.name, dentry->d_name.name);
 		return;
 	}
+
+	inode = dentry->d_inode;
 	down(&inode->i_sem);
 	if (!fhp->fh_pre_mtime)
 		fhp->fh_pre_mtime = inode->i_mtime;
@@ -157,8 +163,13 @@
 static inline void
 fh_unlock(struct svc_fh *fhp)
 {
+	if (!fhp->fh_dverified)
+		printk(KERN_ERR "fh_unlock: fh not verified!\n");
+
 	if (fhp->fh_locked) {
-		struct inode *inode = fhp->fh_dentry->d_inode;
+		struct dentry *dentry = fhp->fh_dentry;
+		struct inode *inode = dentry->d_inode;
+
 		if (!fhp->fh_post_version)
 			fhp->fh_post_version = inode->i_version;
 		fhp->fh_locked = 0;
diff -u --recursive --new-file v2.1.119/linux/include/linux/notifier.h linux/include/linux/notifier.h
--- v2.1.119/linux/include/linux/notifier.h	Mon Jan 12 15:28:19 1998
+++ linux/include/linux/notifier.h	Thu Aug 27 19:33:08 1998
@@ -98,6 +98,7 @@
 #define NETDEV_CHANGEMTU	0x0007
 #define NETDEV_CHANGEADDR	0x0008
 #define NETDEV_GOING_DOWN	0x0009
+#define NETDEV_CHANGENAME	0x000A
 
 #define SYS_DOWN	0x0001	/* Notify of system down */
 #define SYS_RESTART	SYS_DOWN
diff -u --recursive --new-file v2.1.119/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.1.119/linux/include/linux/proc_fs.h	Tue Aug 18 22:02:07 1998
+++ linux/include/linux/proc_fs.h	Fri Sep  4 19:24:26 1998
@@ -119,7 +119,7 @@
 	PROC_NET_AX25_BPQETHER,
 	PROC_NET_IP_MASQ_APP,
 	PROC_NET_RT6,
-	PROC_NET_RT6_TREE,
+	PROC_NET_SNMP6,
 	PROC_NET_RT6_STATS,
 	PROC_NET_NDISC,
 	PROC_NET_STRIP_STATUS,
diff -u --recursive --new-file v2.1.119/linux/include/linux/qnx4_fs.h linux/include/linux/qnx4_fs.h
--- v2.1.119/linux/include/linux/qnx4_fs.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/qnx4_fs.h	Mon Aug 31 13:01:35 1998
@@ -0,0 +1,123 @@
+/*
+ *  Name                         : qnx4_fs.h
+ *  Author                       : Richard Frowijn
+ *  Function                     : qnx4 global filesystem definitions
+ *  Version                      : 1.0
+ *  Last modified                : 23-03-1998
+ *
+ *  History                      : 23-03-1998 created
+ */
+#ifndef _LINUX_QNX4_FS_H
+#define _LINUX_QNX4_FS_H
+
+#include <linux/qnxtypes.h>
+
+#define QNX4_ROOT_INO 1
+
+#define _MAX_XTNTS_PER_XBLK	60
+/* for di_status */
+#define QNX4_FILE_USED          0x01
+#define QNX4_FILE_MODIFIED      0x02
+#define QNX4_FILE_BUSY          0x04
+#define QNX4_FILE_LINK          0x08
+#define QNX4_FILE_INODE         0x10
+#define QNX4_FILE_FSYSCLEAN     0x20
+
+#define QNX4_I_MAP_SLOTS	8
+#define QNX4_Z_MAP_SLOTS	64
+#define QNX4_SUPER_MAGIC	0x002f	/* qnx4 fs detection */
+#define QNX4_VALID_FS		0x0001	/* Clean fs. */
+#define QNX4_ERROR_FS		0x0002	/* fs has errors. */
+#define QNX4_BLOCK_SIZE         0x200	/* blocksize of 512 bytes */
+#define QNX4_DIR_ENTRY_SIZE     0x040	/* dir entry size */
+#define QNX4_XBLK_ENTRY_SIZE    0x200	/* xblk entry size */
+#define QNX4_INODES_PER_BLOCK   0x08	/* 512 / 64 */
+
+/* for filenames */
+#define _SHORT_NAME_MAX        16
+#define QNX4_NAME_MAX          48
+
+/*
+ * This is the original qnx4 inode layout on disk.
+ */
+struct qnx4_inode_entry {
+	char di_fname[16];
+	off_t di_size;
+	_xtnt_t di_first_xtnt;
+	long di_xblk;
+	time_t di_ftime;
+	time_t di_mtime;
+	time_t di_atime;
+	time_t di_ctime;
+	_nxtnt_t di_num_xtnts;
+	mode_t di_mode;
+	muid_t di_uid;
+	mgid_t di_gid;
+	nlink_t di_nlink;
+	char di_zero[4];
+	_ftype_t di_type;
+	unsigned char di_status;
+};
+
+struct qnx4_link_info {
+	char dl_fname[QNX4_NAME_MAX];
+	long dl_inode_blk;
+	unsigned char dl_inode_ndx;
+	unsigned char dl_spare[10];
+	unsigned char dl_status;
+};
+
+struct qnx4_xblk {
+	long xblk_next_xblk;
+	long xblk_prev_xblk;
+	unsigned char xblk_num_xtnts;
+	char xblk_spare[3];
+	long xblk_num_blocks;
+	_xtnt_t xblk_xnts[_MAX_XTNTS_PER_XBLK];
+	char xblk_signature[8];
+	_xtnt_t xblk_first_xtnt;
+};
+
+struct qnx4_super_block {
+	struct qnx4_inode_entry RootDir;
+	struct qnx4_inode_entry Inode;
+	struct qnx4_inode_entry Boot;
+	struct qnx4_inode_entry AltBoot;
+};
+
+#ifdef __KERNEL__
+
+#define QNX4_DEBUG 0
+
+#if QNX4_DEBUG
+#define QNX4DEBUG(X) printk X
+#else
+#define QNX4DEBUG(X) (void) 0
+#endif
+
+extern int qnx4_lookup(struct inode *dir, struct dentry *dentry);
+extern unsigned long qnx4_count_free_inodes(struct super_block *sb);
+extern unsigned long qnx4_count_free_blocks(struct super_block *sb);
+
+extern struct buffer_head *qnx4_getblk(struct inode *, int, int);
+extern struct buffer_head *qnx4_bread(struct inode *, int, int);
+
+extern int init_qnx4_fs(void);
+extern int qnx4_create(struct inode *dir, struct dentry *dentry, int mode);
+extern struct inode_operations qnx4_file_inode_operations;
+extern struct inode_operations qnx4_dir_inode_operations;
+extern struct inode_operations qnx4_symlink_inode_operations;
+extern int qnx4_is_free(struct super_block *sb, int block);
+extern int qnx4_set_bitmap(struct super_block *sb, int block, int busy);
+extern int qnx4_create(struct inode *inode, struct dentry *dentry, int mode);
+extern void qnx4_truncate(struct inode *inode);
+extern void qnx4_free_inode(struct inode *inode);
+extern int qnx4_unlink(struct inode *dir, struct dentry *dentry);
+extern int qnx4_rmdir(struct inode *dir, struct dentry *dentry);
+extern int qnx4_sync_file(struct file *file, struct dentry *dentry);
+extern int qnx4_sync_inode(struct inode *inode);
+extern int qnx4_bmap(struct inode *inode, int block);
+
+#endif				/* __KERNEL__ */
+
+#endif
diff -u --recursive --new-file v2.1.119/linux/include/linux/qnx4_fs_i.h linux/include/linux/qnx4_fs_i.h
--- v2.1.119/linux/include/linux/qnx4_fs_i.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/qnx4_fs_i.h	Mon Aug 31 13:01:35 1998
@@ -0,0 +1,38 @@
+/*
+ *  Name                         : qnx4_fs_i.h
+ *  Author                       : Richard Frowijn
+ *  Function                     : qnx4 inode definitions
+ *  Version                      : 1.0
+ *  Last modified                : 25-05-1998
+ * 
+ *  History                      : 23-03-1998 created
+ * 
+ */
+#ifndef _QNX4_FS_I
+#define _QNX4_FS_I
+
+#include <linux/qnxtypes.h>
+
+/*
+ * qnx4 fs inode entry 
+ */
+struct qnx4_inode_info {
+	char i_reserved[16];	/* 16 */
+	off_t i_size;		/*  4 */
+	_xtnt_t i_first_xtnt;	/*  8 */
+	long i_xblk;		/*  4 */
+	time_t i_ftime;		/*  4 */
+	time_t i_mtime;		/*  4 */
+	time_t i_atime;		/*  4 */
+	time_t i_ctime;		/*  4 */
+	_nxtnt_t i_num_xtnts;	/*  2 */
+	mode_t i_mode;		/*  2 */
+	muid_t i_uid;		/*  2 */
+	mgid_t i_gid;		/*  2 */
+	nlink_t i_nlink;	/*  2 */
+	char i_zero[4];		/*  4 */
+	_ftype_t i_type;	/*  1 */
+	unsigned char i_status;	/*  1 */
+};
+
+#endif
diff -u --recursive --new-file v2.1.119/linux/include/linux/qnx4_fs_sb.h linux/include/linux/qnx4_fs_sb.h
--- v2.1.119/linux/include/linux/qnx4_fs_sb.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/qnx4_fs_sb.h	Mon Aug 31 13:01:35 1998
@@ -0,0 +1,27 @@
+/*
+ *  Name                         : qnx4_fs_sb.h
+ *  Author                       : Richard Frowijn
+ *  Function                     : qnx4 superblock definitions
+ *  Version                      : 1.0
+ *  Last modified                : 20-05-1998
+ * 
+ *  History                      : 23-03-1998 created
+ * 
+ */
+#ifndef _QNX4_FS_SB
+#define _QNX4_FS_SB
+
+#include <linux/qnxtypes.h>
+
+/*
+ * qnx4 super-block data in memory
+ */
+
+struct qnx4_sb_info {
+	struct buffer_head *sb_buf;	/* superblock buffer */
+	struct qnx4_super_block *sb;	/* our superblock */
+	unsigned int Version;	/* may be useful */
+	struct qnx4_inode_entry *BitMap;	/* useful */
+};
+
+#endif
diff -u --recursive --new-file v2.1.119/linux/include/linux/qnxtypes.h linux/include/linux/qnxtypes.h
--- v2.1.119/linux/include/linux/qnxtypes.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/qnxtypes.h	Mon Aug 31 13:01:35 1998
@@ -0,0 +1,28 @@
+/*
+ *  Name                         : qnxtypes.h
+ *  Author                       : Richard Frowijn
+ *  Function                     : standard qnx types
+ *  Version                      : 1.0
+ *  Last modified                : 22-03-1998
+ * 
+ *  History                      : 22-03-1998 created
+ * 
+ */
+
+#ifndef _QNX4TYPES_H
+#define _QNX4TYPES_H
+
+typedef unsigned short _nxtnt_t;
+typedef unsigned char _ftype_t;
+
+typedef struct {
+	long xtnt_blk;
+	long xtnt_size;
+} _xtnt_t;
+
+typedef unsigned short muid_t;
+typedef unsigned short mgid_t;
+typedef unsigned long qnx_off_t;
+typedef unsigned short qnx_nlink_t;
+
+#endif
diff -u --recursive --new-file v2.1.119/linux/include/linux/rtnetlink.h linux/include/linux/rtnetlink.h
--- v2.1.119/linux/include/linux/rtnetlink.h	Tue Jun 23 10:01:29 1998
+++ linux/include/linux/rtnetlink.h	Thu Aug 27 19:33:08 1998
@@ -5,7 +5,6 @@
 #include <linux/netlink.h>
 
 #define RTNL_DEBUG 1
-/* #define CONFIG_RTNL_OLD_IFINFO 1 */
 
 
 /****
@@ -66,14 +65,14 @@
 
 #define RTA_ALIGNTO	4
 #define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) )
-#define RTA_OK(rta,len) ((rta)->rta_len >= sizeof(struct rtattr) && \
+#define RTA_OK(rta,len) ((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \
 			 (rta)->rta_len <= (len))
 #define RTA_NEXT(rta,attrlen)	((attrlen) -= RTA_ALIGN((rta)->rta_len), \
 				 (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len)))
 #define RTA_LENGTH(len)	(RTA_ALIGN(sizeof(struct rtattr)) + (len))
 #define RTA_SPACE(len)	RTA_ALIGN(RTA_LENGTH(len))
 #define RTA_DATA(rta)   ((void*)(((char*)(rta)) + RTA_LENGTH(0)))
-#define RTA_PAYLOAD(rta) ((rta)->rta_len - RTA_LENGTH(0))
+#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
 
 
 
@@ -91,18 +90,9 @@
 
 	unsigned char		rtm_table;	/* Routing table id */
 	unsigned char		rtm_protocol;	/* Routing protocol; see below	*/
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	unsigned char		rtm_nhs;	/* Number of nexthops */
-#else
 	unsigned char		rtm_scope;	/* See below */	
-#endif
 	unsigned char		rtm_type;	/* See below	*/
 
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	unsigned short		rtm_optlen;	/* Byte length of rtm_opt */
-	unsigned char		rtm_scope;	/* See below */	
-	unsigned char		rtm_whatsit;	/* Unused byte */
-#endif
 	unsigned		rtm_flags;
 };
 
@@ -176,9 +166,6 @@
 #define RTM_F_NOTIFY		0x100	/* Notify user of route change	*/
 #define RTM_F_CLONED		0x200	/* This route is cloned		*/
 #define RTM_F_EQUALIZE		0x400	/* Multipath equalizer: NI	*/
-#ifdef CONFIG_RTNL_OLD_IFINFO
-#define RTM_F_NOPMTUDISC	0x800	/* Do not make PMTU discovery	*/
-#endif
 
 /* Reserved table identifiers */
 
@@ -206,17 +193,10 @@
 	RTA_GATEWAY,
 	RTA_PRIORITY,
 	RTA_PREFSRC,
-#ifndef CONFIG_RTNL_OLD_IFINFO
 	RTA_METRICS,
 	RTA_MULTIPATH,
 	RTA_PROTOINFO,
 	RTA_FLOW,
-#else
-	RTA_WINDOW,
-	RTA_RTT,
-	RTA_MTU,
-	RTA_IFNAME,
-#endif
 	RTA_CACHEINFO
 };
 
@@ -253,18 +233,12 @@
 #define RTNH_ALIGNTO	4
 #define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) )
 #define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \
-			   (rtnh)->rtnh_len <= (len))
+			   ((int)(rtnh)->rtnh_len) <= (len))
 #define RTNH_NEXT(rtnh)	((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len)))
 #define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len))
 #define RTNH_SPACE(len)	RTNH_ALIGN(RTNH_LENGTH(len))
 #define RTNH_DATA(rtnh)   ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0)))
 
-#ifdef CONFIG_RTNL_OLD_IFINFO
-#define RTM_RTNH(r) ((struct rtnexthop*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)) \
-					   + NLMSG_ALIGN((r)->rtm_optlen)))
-#define RTM_NHLEN(nlh,r) ((nlh)->nlmsg_len - NLMSG_SPACE(sizeof(struct rtmsg)) - NLMSG_ALIGN((r)->rtm_optlen))
-#endif
-
 /* RTM_CACHEINFO */
 
 struct rta_cacheinfo
@@ -424,35 +398,6 @@
  * on network protocol.
  */
 
-#ifdef CONFIG_RTNL_OLD_IFINFO
-struct ifinfomsg
-{
-	unsigned char	ifi_family;		/* Dummy	*/
-	unsigned char	ifi_addrlen;		/* Length of HW address */
-	unsigned short	ifi_pad__;
-	int		ifi_index;		/* Link index	*/
-	int		ifi_link;		/* Physical device */
-	char		ifi_name[IFNAMSIZ];
-	struct sockaddr	ifi_address;		/* HW address	*/
-	struct sockaddr	ifi_broadcast;		/* HW broadcast	*/
-	unsigned	ifi_flags;		/* IFF_* flags	*/
-	int		ifi_mtu;		/* Link mtu	*/
-	char		ifi_qdiscname[IFNAMSIZ];/* Id of packet scheduler */
-	int		ifi_qdisc;		/* Packet scheduler handle */
-};
-
-enum
-{
-	IFLA_UNSPEC,
-	IFLA_ADDRESS,
-	IFLA_BROADCAST,
-	IFLA_IFNAME,
-	IFLA_QDISC,
-	IFLA_STATS
-};
-
-#else
-
 struct ifinfomsg
 {
 	unsigned char	ifi_family;
@@ -475,8 +420,6 @@
 	IFLA_STATS
 };
 
-#endif
-
 
 #define IFLA_MAX IFLA_STATS
 
@@ -588,7 +531,7 @@
 
 extern struct rtnetlink_link * rtnetlink_links[NPROTO];
 extern int rtnetlink_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb);
-extern int rtnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo);
+extern int rtnetlink_send(struct sk_buff *skb, u32 pid, u32 group, int echo);
 
 extern void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data);
 
diff -u --recursive --new-file v2.1.119/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.1.119/linux/include/linux/sched.h	Tue Aug 18 22:02:07 1998
+++ linux/include/linux/sched.h	Fri Sep  4 19:24:24 1998
@@ -1,6 +1,8 @@
 #ifndef _LINUX_SCHED_H
 #define _LINUX_SCHED_H
 
+extern void checksignals(void);
+
 #include <asm/param.h>	/* for HZ */
 
 extern unsigned long event;
diff -u --recursive --new-file v2.1.119/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v2.1.119/linux/include/linux/skbuff.h	Fri Jul 31 17:05:53 1998
+++ linux/include/linux/skbuff.h	Fri Sep  4 19:24:35 1998
@@ -537,6 +537,19 @@
 	return skb;
 }
 
+extern __inline__ struct sk_buff *
+skb_cow(struct sk_buff *skb, unsigned int headroom)
+{
+	headroom = (headroom+15)&~15;
+
+	if ((unsigned)skb_headroom(skb) < headroom || skb_cloned(skb)) {
+		struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom);
+		kfree_skb(skb);
+		skb = skb2;
+	}
+	return skb;
+}
+
 extern struct sk_buff *		skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
 extern unsigned int		datagram_poll(struct file *file, struct socket *sock, struct poll_table_struct *wait);
 extern int			skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size);
diff -u --recursive --new-file v2.1.119/linux/include/linux/socket.h linux/include/linux/socket.h
--- v2.1.119/linux/include/linux/socket.h	Mon Aug  3 12:45:47 1998
+++ linux/include/linux/socket.h	Thu Aug 27 19:33:08 1998
@@ -88,20 +88,27 @@
 
 /*
  *	Get the next cmsg header
+ *
+ *	PLEASE, do not touch this function. If you think, that it is
+ *	incorrect, grep kernel sources and think about consequences
+ *	before trying to improve it.
+ *
+ *	Now it always returns valid, not truncated ancillary object
+ *	HEADER. But caller still MUST check, that cmsg->cmsg_len is
+ *	inside range, given by msg->msg_controllen before using
+ *	ansillary object DATA.				--ANK (980731)
  */
  
 __KINLINE struct cmsghdr * __cmsg_nxthdr(void *__ctl, __kernel_size_t __size,
 					       struct cmsghdr *__cmsg)
 {
-	unsigned char * __ptr;
+	struct cmsghdr * __ptr;
 
-	if (__cmsg->cmsg_len < sizeof(struct cmsghdr))
-		return NULL;
-	__ptr = ((unsigned char *) __cmsg) +  CMSG_ALIGN(__cmsg->cmsg_len);
-	if (__ptr >= (unsigned char *) __ctl + __size)
+	__ptr = (struct cmsghdr*)(((unsigned char *) __cmsg) +  CMSG_ALIGN(__cmsg->cmsg_len));
+	if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size)
 		return NULL;
 
-	return (struct cmsghdr *) __ptr;
+	return __ptr;
 }
 
 __KINLINE struct cmsghdr * cmsg_nxthdr (struct msghdr *__msg, struct cmsghdr *__cmsg)
diff -u --recursive --new-file v2.1.119/linux/include/linux/sockios.h linux/include/linux/sockios.h
--- v2.1.119/linux/include/linux/sockios.h	Mon Dec 29 10:22:46 1997
+++ linux/include/linux/sockios.h	Thu Aug 27 19:33:08 1998
@@ -45,6 +45,7 @@
 #define SIOCSIFMEM	0x8920		/* set memory address (BSD)	*/
 #define SIOCGIFMTU	0x8921		/* get MTU size			*/
 #define SIOCSIFMTU	0x8922		/* set MTU size			*/
+#define SIOCSIFNAME	0x8923		/* set interface name */
 #define	SIOCSIFHWADDR	0x8924		/* set hardware address 	*/
 #define SIOCGIFENCAP	0x8925		/* get/set encapsulations       */
 #define SIOCSIFENCAP	0x8926		
diff -u --recursive --new-file v2.1.119/linux/include/linux/swap.h linux/include/linux/swap.h
--- v2.1.119/linux/include/linux/swap.h	Thu Aug 20 17:05:19 1998
+++ linux/include/linux/swap.h	Mon Aug 31 13:04:26 1998
@@ -75,6 +75,7 @@
 /* linux/mm/page_io.c */
 extern void rw_swap_page(int, unsigned long, char *, int);
 extern void rw_swap_page_nocache(int, unsigned long, char *);
+extern void swap_after_unlock_page (unsigned long entry);
 
 /* linux/mm/page_alloc.c */
 extern void swap_in(struct task_struct *, struct vm_area_struct *,
@@ -84,10 +85,15 @@
 /* linux/mm/swap_state.c */
 extern void show_swap_cache_info(void);
 extern int add_to_swap_cache(struct page *, unsigned long);
-extern void swap_duplicate(unsigned long);
-extern void swap_after_unlock_page (unsigned long entry);
+extern int swap_duplicate(unsigned long);
+extern int swap_check_entry(unsigned long);
 extern struct page * read_swap_cache_async(unsigned long, int);
 #define read_swap_cache(entry) read_swap_cache_async(entry, 1);
+/*
+ * Make these inline later once they are working properly.
+ */
+extern void delete_from_swap_cache(struct page *page);
+extern void free_page_and_swap_cache(unsigned long addr);
 
 /* linux/mm/swapfile.c */
 extern unsigned int nr_swapfiles;
@@ -100,6 +106,8 @@
 	int next;	/* swapfile to be used next */
 };
 extern struct swap_list_t swap_list;
+int sys_swapoff(const char *);
+int sys_swapon(const char *, int);
 
 /*
  * vm_ops not present page codes for shared memory.
@@ -147,14 +155,6 @@
 		count--;
 	return (count > 1);
 }
-
-/*
- * Make these inline later once they are working properly.
- */
-extern long find_in_swap_cache(struct page *page);
-extern int delete_from_swap_cache(struct page *page);
-extern void remove_from_swap_cache(struct page *page);
-extern void free_page_and_swap_cache(unsigned long addr);
 
 #endif /* __KERNEL__*/
 
diff -u --recursive --new-file v2.1.119/linux/include/linux/ufs_fs.h linux/include/linux/ufs_fs.h
--- v2.1.119/linux/include/linux/ufs_fs.h	Fri Jul 31 17:05:52 1998
+++ linux/include/linux/ufs_fs.h	Tue Sep  1 10:50:11 1998
@@ -6,7 +6,7 @@
  * Laboratory for Computer Science Research Computing Facility
  * Rutgers, The State University of New Jersey
  *
- * Clean swab support by Fare <rideau@ens.fr>
+ * Clean swab support by Fare <fare@tunes.org>
  * just hope no one is using NNUUXXI on __?64 structure elements
  * 64-bit clean thanks to Maciej W. Rozycki <macro@ds2.pg.gda.pl>
  *
@@ -31,8 +31,8 @@
 #define UFS_SBLOCK 8192
 #define UFS_SBSIZE 8192
 
-#define SECTOR_SIZE 512
-#define SECTOR_BITS 9
+#define UFS_SECTOR_SIZE 512
+#define UFS_SECTOR_BITS 9
 #define UFS_MAGIC 0x00011954
 #define UFS_CIGAM 0x54190100 /* byteswapped MAGIC */
 
@@ -68,8 +68,15 @@
 /* From here to next blank line, s_flags for ufs_sb_info */
 /* endianness */
 #define UFS_BYTESEX		0x00000001	/* mask; leave room to 0xF */
+#if defined(__LITTLE_ENDIAN) || defined(__BIG_ENDIAN)
+/* these are for sane architectures */
+#define UFS_NATIVE_ENDIAN	0x00000000
+#define UFS_SWABBED_ENDIAN	0x00000001
+#else
+/* these are for pervert architectures */
 #define UFS_LITTLE_ENDIAN	0x00000000
 #define UFS_BIG_ENDIAN		0x00000001
+#endif
 /* directory entry encoding */
 #define UFS_DE_MASK		0x00000010	/* mask for the following */
 #define UFS_DE_OLD		0x00000000
diff -u --recursive --new-file v2.1.119/linux/include/linux/umsdos_fs.h linux/include/linux/umsdos_fs.h
--- v2.1.119/linux/include/linux/umsdos_fs.h	Fri Jul 31 17:10:57 1998
+++ linux/include/linux/umsdos_fs.h	Fri Sep  4 19:24:02 1998
@@ -1,6 +1,8 @@
 #ifndef LINUX_UMSDOS_FS_H
 #define LINUX_UMSDOS_FS_H
 
+#define UMS_DEBUG 1
+
 #define UMSDOS_VERSION	0
 #define UMSDOS_RELEASE	4
 
@@ -23,6 +25,27 @@
 #endif
 
 
+#ifdef __KERNEL__
+/* #Specification: convention / PRINTK Printk and printk
+ * Here is the convention for the use of printk inside fs/umsdos
+ * 
+ * printk carry important message (error or status).
+ * Printk is for debugging (it is a macro defined at the beginning of
+ * most source.
+ * PRINTK is a nulled Printk macro.
+ * 
+ * This convention makes the source easier to read, and Printk easier
+ * to shut off.
+ */
+#	define PRINTK(x)
+#	ifdef UMS_DEBUG
+#		define Printk(x) printk x
+#	else
+#		define Printk(x)
+#	endif
+#endif
+
+
 struct umsdos_fake_info {
 	char fname[13];
 	int len;
@@ -93,7 +116,7 @@
 #define UMSDOS_READDIR_EMD _IO(0x04,216)	/* read the EMD file only. */
 #define UMSDOS_GETVERSION  _IO(0x04,217)	/* Get the release number of UMSDOS */
 #define UMSDOS_INIT_EMD    _IO(0x04,218)	/* Create the EMD file if not there */
-#define UMSDOS_DOS_SETUP   _IO(0x04,219)	/* Set the defaults of the MsDOS driver */
+#define UMSDOS_DOS_SETUP   _IO(0x04,219)	/* Set the defaults of the MS-DOS driver. */
 
 #define UMSDOS_RENAME_DOS  _IO(0x04,220)	/* rename a file/directory in the DOS
 						 * directory only */
diff -u --recursive --new-file v2.1.119/linux/include/linux/umsdos_fs.p linux/include/linux/umsdos_fs.p
--- v2.1.119/linux/include/linux/umsdos_fs.p	Wed May 20 19:10:41 1998
+++ linux/include/linux/umsdos_fs.p	Wed Sep  2 16:12:37 1998
@@ -6,7 +6,7 @@
 			int len,
 			int mode,
 			struct inode **inode);
-int  UMSDOS_dir_read ( struct file *filp,
+int  dummy_dir_read ( struct file *filp,
 	 char *buf,
 	 size_t size,
 	 loff_t *count);
@@ -24,37 +24,29 @@
 			    struct dentry *dentry,
 			    int nopseudo);
 int UMSDOS_lookup(struct inode *dir,struct dentry *dentry);
-	 
-int umsdos_hlink2inode (struct inode *hlink, struct inode **result);
+
+struct dentry *umsdos_solve_hlink (struct dentry *hlink);
 /* emd.c 22/06/95 00.22.04 */
 ssize_t umsdos_file_write_kmem_real (struct file *filp,
 				const char *buf,
-				size_t  count,
-				loff_t *offs);
+				size_t  count);
 
-ssize_t umsdos_file_read_kmem (struct inode *emd_dir,
-	 struct file *filp,
+int fix_emd_filp (struct file *filp);
+
+ssize_t umsdos_file_read_kmem (struct file *filp,
 	 char *buf,
-	 size_t count,
-	 loff_t *offs);
-ssize_t umsdos_file_write_kmem (struct inode *emd_dir,
-	 struct file *filp,
+	 size_t count);
+ssize_t umsdos_file_write_kmem (struct file *filp,
 	 const char *buf,
-	 size_t count,
-	 loff_t *offs);
-ssize_t umsdos_emd_dir_write (struct inode *emd_dir,
-	 struct file *filp,
+	 size_t count);
+ssize_t umsdos_emd_dir_write (struct file *filp,
 	 char *buf,
-	 size_t count,
-	 loff_t *offs);
-ssize_t umsdos_emd_dir_read (struct inode *emd_dir,
-	 struct file *filp,
+	 size_t count);
+ssize_t umsdos_emd_dir_read (struct file *filp,
 	 char *buf,
-	 size_t count,
-	 loff_t *loffs);
+	 size_t count);
 struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat);
-int umsdos_emd_dir_readentry (struct inode *emd_dir,
-	 struct file *filp,
+int umsdos_emd_dir_readentry (struct file *filp,
 	 struct umsdos_dirent *entry);
 int umsdos_writeentry (struct inode *dir,
 	 struct inode *emd_dir,
@@ -72,11 +64,14 @@
 /* file.c 25/01/95 02.25.38 */
 /* inode.c 12/06/95 09.49.40 */
 inline struct dentry *geti_dentry (struct inode *inode);
+void checkd_inode (struct inode *inode);
 inline void inc_count (struct inode *inode);
 void check_inode (struct inode *inode);
 void check_dentry (struct dentry *dentry);
+void check_dentry_path (struct dentry *dentry, const char *desc);
 void fill_new_filp (struct file *filp, struct dentry *dentry);
 void kill_dentry (struct dentry *dentry);
+void fin_dentry (struct dentry *dentry);
 struct dentry *creat_dentry (const char *name,
 			     const int len,
 			     struct inode *inode,
@@ -86,10 +81,9 @@
 int UMSDOS_statfs (struct super_block *sb,
 	 struct statfs *buf,
 	 int bufsiz);
-int compat_umsdos_real_lookup (struct inode *dir,
+struct dentry *compat_umsdos_real_lookup (struct dentry *d_dir,
 	 const char *name,
-	 int len,
-	 struct inode **result);
+	 int len);
 int umsdos_real_lookup(struct inode *inode,struct dentry *dentry);	 
 void umsdos_setup_dir_inode (struct inode *inode);
 void umsdos_set_dirinfo (struct inode *inode,
@@ -121,6 +115,9 @@
 void umsdos_unlockcreate (struct inode *dir);
 void umsdos_endlookup (struct inode *dir);
 
+int umsdos_readlink_x (	     struct dentry *dentry,
+			     char *buffer,
+			     int bufsiz);
 int UMSDOS_symlink (struct inode *dir,
 		    struct dentry *dentry,
 		    const char *symname);
diff -u --recursive --new-file v2.1.119/linux/include/net/dst.h linux/include/net/dst.h
--- v2.1.119/linux/include/net/dst.h	Fri Jul 31 17:11:09 1998
+++ linux/include/net/dst.h	Fri Sep  4 19:24:35 1998
@@ -35,7 +35,6 @@
 	atomic_t		use;		/* client references	*/
 	struct device	        *dev;
 	int			obsolete;
-	__u32			priority;
 	unsigned long		lastuse;
 	unsigned		mxlock;
 	unsigned		window;
diff -u --recursive --new-file v2.1.119/linux/include/net/flow.h linux/include/net/flow.h
--- v2.1.119/linux/include/net/flow.h	Thu Mar 26 15:57:06 1998
+++ linux/include/net/flow.h	Thu Aug 27 19:33:08 1998
@@ -21,6 +21,10 @@
 			struct in6_addr *	saddr;
 		} ip6_u;
 	} nl_u;
+#define fl6_dst		nl_u.ip6_u.daddr
+#define fl6_src		nl_u.ip6_u.saddr
+#define fl4_dst		nl_u.ip4_u.daddr
+#define fl4_src		nl_u.ip4_u.saddr
 
 	int	oif;
 
diff -u --recursive --new-file v2.1.119/linux/include/net/ip6_fib.h linux/include/net/ip6_fib.h
--- v2.1.119/linux/include/net/ip6_fib.h	Thu May 14 19:47:44 1998
+++ linux/include/net/ip6_fib.h	Thu Aug 27 19:33:08 1998
@@ -23,7 +23,8 @@
 
 struct rt6_info;
 
-struct fib6_node {
+struct fib6_node
+{
 	struct fib6_node	*parent;
 	struct fib6_node	*left;
 	struct fib6_node	*right;
@@ -43,12 +44,14 @@
  *
  */
 
-struct rt6key {
+struct rt6key
+{
 	struct in6_addr	addr;
 	int		plen;
 };
 
-struct rt6_info {
+struct rt6_info
+{
 	union {
 		struct dst_entry	dst;
 		struct rt6_info		*next;
@@ -56,21 +59,16 @@
 
 #define rt6i_dev			u.dst.dev
 #define rt6i_nexthop			u.dst.neighbour
-#define rt6i_use			u.dst.use
-#define rt6i_ref			u.dst.refcnt
-
-#define rt6i_tstamp			u.dst.lastuse
 
 	struct fib6_node		*rt6i_node;
 
 	struct in6_addr			rt6i_gateway;
 	
-	int				rt6i_keylen;
-
 	u32				rt6i_flags;
 	u32				rt6i_metric;
 	u8				rt6i_hoplimit;
 	unsigned long			rt6i_expires;
+	atomic_t			rt6i_ref;
 
 	union {
 		struct flow_rule	*rt6iu_flowr;
@@ -84,6 +82,33 @@
 	struct rt6key			rt6i_src;
 };
 
+struct fib6_walker_t
+{
+	struct fib6_walker_t *prev, *next;
+	struct fib6_node *root, *node;
+	struct rt6_info *leaf;
+	unsigned char state;
+	unsigned char prune;
+	int (*func)(struct fib6_walker_t *);
+	void *args;
+};
+
+extern struct fib6_walker_t fib6_walker_list;
+
+extern __inline__ void fib6_walker_link(struct fib6_walker_t *w)
+{
+	w->next = fib6_walker_list.next;
+	w->prev = &fib6_walker_list;
+	w->next->prev = w;
+	w->prev->next = w;
+}
+
+extern __inline__ void fib6_walker_unlink(struct fib6_walker_t *w)
+{
+	w->next->prev = w->prev;
+	w->prev->next = w->next;
+	w->prev = w->next = w;
+}
 
 struct rt6_statistics {
 	__u32		fib_nodes;
@@ -97,8 +122,6 @@
 #define RTN_ROOT	0x0002		/* tree root node		*/
 #define RTN_RTINFO	0x0004		/* node with valid routing info	*/
 
-#define RTN_TAG		0x0100
-
 /*
  *	priority levels (or metrics)
  *
@@ -128,11 +151,16 @@
 					     struct in6_addr *daddr,
 					     struct in6_addr *saddr);
 
-#define RT6_FILTER_RTNODES	1
+struct fib6_node		*fib6_locate(struct fib6_node *root,
+					     struct in6_addr *daddr, int dst_len,
+					     struct in6_addr *saddr, int src_len);
+
+extern void			fib6_clean_tree(struct fib6_node *root,
+						int (*func)(struct rt6_info *, void *arg),
+						int prune, void *arg);
 
-extern void			fib6_walk_tree(struct fib6_node *root,
-					       f_pnode func, void *arg,
-					       int filter);
+extern int			fib6_walk(struct fib6_walker_t *w);
+extern int			fib6_walk_continue(struct fib6_walker_t *w);
 
 extern int			fib6_add(struct fib6_node *root,
 					 struct rt6_info *rt);
diff -u --recursive --new-file v2.1.119/linux/include/net/ip6_route.h linux/include/net/ip6_route.h
--- v2.1.119/linux/include/net/ip6_route.h	Tue Jul 28 14:21:10 1998
+++ linux/include/net/ip6_route.h	Thu Aug 27 19:33:08 1998
@@ -12,23 +12,6 @@
 #include <net/flow.h>
 #include <net/ip6_fib.h>
 
-/*
- *	Structure for assync processing of operations on the routing
- *	table
- */
-
-struct rt6_req {
-	int			operation;
-	struct rt6_info		*ptr;
-
-	struct rt6_req		*next;
-	struct rt6_req		*prev;
-
-#define RT_OPER_ADD		1
-#define RT_OPER_DEL		2
-};
-
-
 struct pol_chain {
 	int			type;
 	int			priority;
@@ -53,8 +36,7 @@
 
 extern int			ipv6_route_ioctl(unsigned int cmd, void *arg);
 
-extern struct rt6_info *	ip6_route_add(struct in6_rtmsg *rtmsg,
-					      int *err);
+extern int			ip6_route_add(struct in6_rtmsg *rtmsg);
 extern int			ip6_del_rt(struct rt6_info *);
 
 extern int			ip6_rt_addr_add(struct in6_addr *addr,
@@ -85,15 +67,15 @@
 
 extern void			rt6_purge_dflt_routers(int lst_resort);
 
-extern struct rt6_info *	rt6_redirect(struct in6_addr *dest,
+extern void			rt6_redirect(struct in6_addr *dest,
 					     struct in6_addr *saddr,
-					     struct in6_addr *target,
-					     struct device *dev,
+					     struct neighbour *neigh,
 					     int on_link);
 
-extern void			rt6_pmtu_discovery(struct in6_addr *addr,
+extern void			rt6_pmtu_discovery(struct in6_addr *daddr,
+						   struct in6_addr *saddr,
 						   struct device *dev,
-						   int pmtu);
+						   u32 pmtu);
 
 struct nlmsghdr;
 struct netlink_callback;
@@ -103,22 +85,25 @@
 extern int inet6_rtm_getroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
 
 extern void rt6_ifdown(struct device *dev);
+extern void rt6_mtu_change(struct device *dev, unsigned mtu);
 
 /*
  *	Store a destination cache entry in a socket
  *	For UDP/RAW sockets this is done on udp_connect.
  */
 
-extern __inline__ void ip6_dst_store(struct sock *sk, struct dst_entry *dst)
+extern __inline__ void ip6_dst_store(struct sock *sk, struct dst_entry *dst,
+				     struct in6_addr *daddr)
 {
 	struct ipv6_pinfo *np;
 	struct rt6_info *rt;
-		
+
 	np = &sk->net_pinfo.af_inet6;
 	dst_release(xchg(&sk->dst_cache,dst));
-	
+
 	rt = (struct rt6_info *) dst;
-	
+
+	np->daddr_cache = daddr;
 	np->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0;
 }
 
diff -u --recursive --new-file v2.1.119/linux/include/net/ip_fib.h linux/include/net/ip_fib.h
--- v2.1.119/linux/include/net/ip_fib.h	Sat May  2 14:19:54 1998
+++ linux/include/net/ip_fib.h	Thu Aug 27 19:33:08 1998
@@ -212,7 +212,7 @@
 extern struct fib_info	*fib_create_info(const struct rtmsg *r, struct kern_rta *rta,
 					 const struct nlmsghdr *, int *err);
 extern int fib_nh_match(struct rtmsg *r, struct nlmsghdr *, struct kern_rta *rta, struct fib_info *fi);
-extern int fib_dump_info(struct sk_buff *skb, pid_t pid, u32 seq, int event,
+extern int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
 			 u8 tb_id, u8 type, u8 scope, void *dst, int dst_len, u8 tos,
 			 struct fib_info *fi);
 extern int fib_sync_down(u32 local, struct device *dev, int force);
diff -u --recursive --new-file v2.1.119/linux/include/net/ipv6.h linux/include/net/ipv6.h
--- v2.1.119/linux/include/net/ipv6.h	Fri Jul 31 17:11:31 1998
+++ linux/include/net/ipv6.h	Fri Sep  4 19:23:32 1998
@@ -4,7 +4,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: ipv6.h,v 1.12 1998/07/15 05:05:02 davem Exp $
+ *	$Id: ipv6.h,v 1.13 1998/08/26 12:02:11 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -86,53 +86,44 @@
 
 #include <net/sock.h>
 
-extern struct ipv6_mib	ipv6_statistics;
+extern struct ipv6_mib		ipv6_statistics;
+extern struct icmpv6_mib	icmpv6_statistics;
+extern struct udp_mib		udp_stats_in6;
 
-struct ipv6_frag {
-	__u16			offset;
-	__u16			len;
-	struct sk_buff		*skb;
-
-	struct frag_hdr		*fhdr;
-
-	struct ipv6_frag	*next;
+struct ip6_ra_chain
+{
+	struct ip6_ra_chain	*next;
+	struct sock		*sk;
+	int			sel;
+	void			(*destructor)(struct sock *);
 };
 
+extern struct ip6_ra_chain	*ip6_ra_chain;
+
 /*
- *	Equivalent of ipv4 struct ipq
+   This structure is prepared by protocol, when parsing
+   ancillary data and passed to IPv6.
  */
 
-struct frag_queue {
+struct ipv6_txoptions
+{
+	/* Length of this structure */
+	int			tot_len;
 
-	struct frag_queue	*next;
-	struct frag_queue	*prev;
+	/* length of extension headers   */
 
-	__u32			id;		/* fragment id		*/
-	struct in6_addr		saddr;
-	struct in6_addr		daddr;
-	struct timer_list	timer;		/* expire timer		*/
-	struct ipv6_frag	*fragments;
-	struct device		*dev;
-	__u8			last_in;	/* has last segment arrived? */
-	__u8			nexthdr;
-	__u8			*nhptr;
-};
+	__u16			opt_flen;	/* after fragment hdr */
+	__u16			opt_nflen;	/* before fragment hdr */
 
-struct ipv6_tlvtype
-{
-	u8 type;
-	u8 len;
-};
+	struct ipv6_opt_hdr	*hopopt;
+	struct ipv6_opt_hdr	*dst0opt;
+	struct ipv6_rt_hdr	*srcrt;	/* Routing Header */
+	struct ipv6_opt_hdr	*auth;
+	struct ipv6_opt_hdr	*dst1opt;
 
-struct ip6_ra_chain
-{
-	struct ip6_ra_chain	*next;
-	struct sock		*sk;
-	int			sel;
-	void			(*destructor)(struct sock *);
+	/* Option buffer, as read by IPV6_PKTOPTIONS, starts here. */
 };
 
-extern struct ip6_ra_chain	*ip6_ra_chain;
 
 extern int 			ip6_ra_control(struct sock *sk, int sel,
 					       void (*destructor)(struct sock *));
@@ -140,18 +131,13 @@
 
 extern int			ip6_call_ra_chain(struct sk_buff *skb, int sel);
 
-extern int 			ip6_dstopt_unknown(struct sk_buff *skb,
-						   struct ipv6_tlvtype *hdr);
+extern u8 *			ipv6_reassembly(struct sk_buff **skb, u8 *nhptr);
 
-extern int			ipv6_routing_header(struct sk_buff **skb, 
-						    struct device *dev,
-						    __u8 *nhptr, 
-						    struct ipv6_options *opt);
-
-extern int			ipv6_reassembly(struct sk_buff **skb, 
-						struct device *dev, 
-						__u8 *nhptr,
-						struct ipv6_options *opt);
+extern u8 *			ipv6_parse_hopopts(struct sk_buff *skb, u8 *nhptr);
+
+extern u8 *			ipv6_parse_exthdrs(struct sk_buff **skb, u8 *nhptr);
+
+extern struct ipv6_txoptions *  ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt);
 
 #define IPV6_FRAG_TIMEOUT	(60*HZ)		/* 60 seconds */
 
@@ -226,7 +212,7 @@
 extern int			ip6_xmit(struct sock *sk,
 					 struct sk_buff *skb,
 					 struct flowi *fl,
-					 struct ipv6_options *opt);
+					 struct ipv6_txoptions *opt);
 
 extern int			ip6_nd_hdr(struct sock *sk,
 					   struct sk_buff *skb,
@@ -240,7 +226,7 @@
 					       const void *data,
 					       struct flowi *fl,
 					       unsigned length,
-					       struct ipv6_options *opt,
+					       struct ipv6_txoptions *opt,
 					       int hlimit, int flags);
 
 /*
@@ -256,28 +242,27 @@
  *	Extension header (options) processing
  */
 
-extern int			ipv6opt_bld_rthdr(struct sk_buff *skb,
-						  struct ipv6_options *opt,
-						  struct in6_addr *addr,
-						  int proto);
-
-extern int			ipv6opt_srcrt_co(struct sockaddr_in6 *sin6, 
-						 int len, 
-						 struct ipv6_options *opt);
-
-extern int			ipv6opt_srcrt_cl(struct sockaddr_in6 *sin6, 
-						 int num_addrs, 
-						 struct ipv6_options *opt);
-
-extern int			ipv6opt_srt_tosin(struct ipv6_options *opt,
-						  struct sockaddr_in6 *sin6,
-						  int len);
-
-extern void			ipv6opt_free(struct ipv6_options *opt);
+extern u8 *			ipv6_build_nfrag_opts(struct sk_buff *skb,
+						      u8 *prev_hdr,
+						      struct ipv6_txoptions *opt,
+						      struct in6_addr *daddr,
+						      u32 jumbolen);
+extern u8 *			ipv6_build_frag_opts(struct sk_buff *skb,
+						     u8 *prev_hdr,
+						     struct ipv6_txoptions *opt);
+extern void 			ipv6_push_nfrag_opts(struct sk_buff *skb,
+						     struct ipv6_txoptions *opt,
+						     u8 *proto,
+						     struct in6_addr **daddr_p);
+extern void			ipv6_push_frag_opts(struct sk_buff *skb,
+						    struct ipv6_txoptions *opt,
+						    u8 *proto);
 
-extern struct ipv6_opt_hdr *	ipv6_skip_exthdr(struct ipv6_opt_hdr *hdr, 
+extern u8 *			ipv6_skip_exthdr(struct ipv6_opt_hdr *hdr, 
 					         u8 *nexthdrp, int len);
 
+extern struct ipv6_txoptions *	ipv6_invert_rthdr(struct sock *sk,
+						  struct ipv6_rt_hdr *hdr);
 
 
 /*
diff -u --recursive --new-file v2.1.119/linux/include/net/ndisc.h linux/include/net/ndisc.h
--- v2.1.119/linux/include/net/ndisc.h	Fri Jul 31 17:11:31 1998
+++ linux/include/net/ndisc.h	Fri Sep  4 19:24:36 1998
@@ -60,12 +60,7 @@
 
 extern void			ndisc_cleanup(void);
 
-extern int			ndisc_rcv(struct sk_buff *skb,
-					  struct device *dev,
-					  struct in6_addr *saddr,
-					  struct in6_addr *daddr,
-					  struct ipv6_options *opt,
-					  unsigned short len);
+extern int			ndisc_rcv(struct sk_buff *skb, unsigned long len);
 
 extern void			ndisc_send_ns(struct device *dev,
 					      struct neighbour *neigh,
diff -u --recursive --new-file v2.1.119/linux/include/net/pkt_sched.h linux/include/net/pkt_sched.h
--- v2.1.119/linux/include/net/pkt_sched.h	Sat May  2 14:19:54 1998
+++ linux/include/net/pkt_sched.h	Thu Aug 27 19:33:08 1998
@@ -147,12 +147,12 @@
 
 #if PSCHED_CLOCK_SOURCE == PSCHED_JIFFIES
 
-#define PSCHED_WATCHER
+#define PSCHED_WATCHER unsigned long
 
-extern unsigned long psched_time_mark;
+extern PSCHED_WATCHER psched_time_mark;
 
 #if HZ == 100
-#define PSCHED_JSCALE 7
+#define PSCHED_JSCALE 13
 #elif HZ == 1024
 #define PSCHED_JSCALE 10
 #else
@@ -179,9 +179,9 @@
 
 #elif defined (__alpha__)
 
-#define PSCHED_WATCHER
+#define PSCHED_WATCHER u32
 
-extern u32 psched_time_mark;
+extern PSCHED_WATCHER psched_time_mark;
 
 #define PSCHED_GET_TIME(stamp) \
 ({ u32 __res; \
diff -u --recursive --new-file v2.1.119/linux/include/net/protocol.h linux/include/net/protocol.h
--- v2.1.119/linux/include/net/protocol.h	Fri Jul 31 17:07:27 1998
+++ linux/include/net/protocol.h	Fri Sep  4 19:21:03 1998
@@ -48,17 +48,13 @@
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
 struct inet6_protocol 
 {
-	int	(*handler)(struct sk_buff *skb, struct device *dev,
-			struct in6_addr *saddr,
-			struct in6_addr *daddr,
-			struct ipv6_options *opt, 
-			unsigned short len,
-			int redo, struct inet6_protocol *protocol);
+	int	(*handler)(struct sk_buff *skb,
+			unsigned long len);
 
-	void	(*err_handler)(struct sk_buff *skb, int type, int code, unsigned char *buff,
-			__u32 info, struct in6_addr *saddr,
-			struct in6_addr *daddr,
-			struct inet6_protocol *protocol);
+	void	(*err_handler)(struct sk_buff *skb, struct ipv6hdr *hdr,
+			       struct inet6_skb_parm *opt,
+			       int type, int code, unsigned char *buff,
+			       __u32 info);
 	struct inet6_protocol *next;
 	unsigned char	protocol;
 	unsigned char	copy:1;
diff -u --recursive --new-file v2.1.119/linux/include/net/rawv6.h linux/include/net/rawv6.h
--- v2.1.119/linux/include/net/rawv6.h	Tue Mar  4 10:25:26 1997
+++ linux/include/net/rawv6.h	Thu Aug 27 19:33:08 1998
@@ -10,19 +10,17 @@
 extern struct sock *raw_v6_lookup(struct sock *sk, unsigned short num,
 				  struct in6_addr *loc_addr, struct in6_addr *rmt_addr);
 
-extern int			rawv6_rcv(struct sk_buff *skb, 
-					  struct device *dev,
-					  struct in6_addr *saddr, 
-					  struct in6_addr *daddr,
-					  struct ipv6_options *opt, 
-					  unsigned short len);
+extern int			rawv6_rcv(struct sock *sk,
+					  struct sk_buff *skb, 
+					  unsigned long len);
 
 
 extern void			rawv6_err(struct sock *sk,
+					  struct sk_buff *skb,
+					  struct ipv6hdr *hdr,
+					  struct inet6_skb_parm *opt,
 					  int type, int code, 
-					  unsigned char *buff,
-					  struct in6_addr *saddr,
-					  struct in6_addr *daddr);
+					  unsigned char *buff, u32 info);
 
 #endif
 
diff -u --recursive --new-file v2.1.119/linux/include/net/route.h linux/include/net/route.h
--- v2.1.119/linux/include/net/route.h	Fri Jul 31 17:11:09 1998
+++ linux/include/net/route.h	Fri Sep  4 19:24:36 1998
@@ -27,6 +27,11 @@
 #include <net/dst.h>
 #include <linux/in_route.h>
 #include <linux/rtnetlink.h>
+#include <linux/route.h>
+
+#ifndef __KERNEL__
+#warning This file is not supposed to be used outside of kernel.
+#endif
 
 #define RT_HASH_DIVISOR	    	256
 
@@ -36,7 +41,6 @@
  */
 #define RT_CACHE_BUBBLE_THRESHOLD	(5*HZ)
 
-#include <linux/route.h>
 
 #define RTO_ONLINK	0x01
 #define RTO_TPROXY	0x80000000
@@ -87,7 +91,8 @@
 #endif
 };
 
-#ifdef __KERNEL__
+extern struct rtable 	*rt_hash_table[RT_HASH_DIVISOR];
+
 extern void		ip_rt_init(void);
 extern void		ip_rt_redirect(u32 old_gw, u32 dst, u32 new_gw,
 				       u32 src, u8 tos, struct device *dev);
@@ -130,8 +135,5 @@
 	*rp = NULL;
 	return ip_route_output(rp, dst, src, tos, oif);
 }
-
-#endif
-
 
 #endif	/* _ROUTE_H */
diff -u --recursive --new-file v2.1.119/linux/include/net/snmp.h linux/include/net/snmp.h
--- v2.1.119/linux/include/net/snmp.h	Sat May  2 14:19:54 1998
+++ linux/include/net/snmp.h	Thu Aug 27 19:33:08 1998
@@ -52,11 +52,14 @@
 {
 	unsigned long	Ip6InReceives;
  	unsigned long	Ip6InHdrErrors;
+ 	unsigned long	Ip6InTooBigErrors;
+ 	unsigned long	Ip6InNoRoutes;
  	unsigned long	Ip6InAddrErrors;
- 	unsigned long	Ip6ForwDatagrams;
  	unsigned long	Ip6InUnknownProtos;
+ 	unsigned long	Ip6InTruncatedPkts;
  	unsigned long	Ip6InDiscards;
  	unsigned long	Ip6InDelivers;
+ 	unsigned long	Ip6OutForwDatagrams;
  	unsigned long	Ip6OutRequests;
  	unsigned long	Ip6OutDiscards;
  	unsigned long	Ip6OutNoRoutes;
@@ -67,6 +70,8 @@
  	unsigned long	Ip6FragOKs;
  	unsigned long	Ip6FragFails;
  	unsigned long	Ip6FragCreates;
+ 	unsigned long	Ip6InMcastPkts;
+ 	unsigned long	Ip6OutMcastPkts;
 };
  
 struct icmp_mib
@@ -98,6 +103,43 @@
  	unsigned long	IcmpOutAddrMasks;
  	unsigned long	IcmpOutAddrMaskReps;
 };
+
+struct icmpv6_mib
+{
+	unsigned long	Icmp6InMsgs;
+	unsigned long	Icmp6InErrors;
+
+	unsigned long	Icmp6InDestUnreachs;
+	unsigned long	Icmp6InPktTooBigs;
+	unsigned long	Icmp6InTimeExcds;
+	unsigned long	Icmp6InParmProblems;
+
+	unsigned long	Icmp6InEchos;
+	unsigned long	Icmp6InEchoReplies;
+	unsigned long	Icmp6InGroupMembQueries;
+	unsigned long	Icmp6InGroupMembResponses;
+	unsigned long	Icmp6InGroupMembReductions;
+	unsigned long	Icmp6InRouterSolicits;
+	unsigned long	Icmp6InRouterAdvertisements;
+	unsigned long	Icmp6InNeighborSolicits;
+	unsigned long	Icmp6InNeighborAdvertisements;
+	unsigned long	Icmp6InRedirects;
+
+	unsigned long	Icmp6OutMsgs;
+
+	unsigned long	Icmp6OutDestUnreachs;
+	unsigned long	Icmp6OutPktTooBigs;
+	unsigned long	Icmp6OutTimeExcds;
+	unsigned long	Icmp6OutParmProblems;
+
+	unsigned long	Icmp6OutEchoReplies;
+	unsigned long	Icmp6OutRouterSolicits;
+	unsigned long	Icmp6OutNeighborSolicits;
+	unsigned long	Icmp6OutNeighborAdvertisements;
+	unsigned long	Icmp6OutRedirects;
+	unsigned long	Icmp6OutGroupMembResponses;
+	unsigned long	Icmp6OutGroupMembReductions;
+};
  
 struct tcp_mib
 {
@@ -131,6 +173,9 @@
 	unsigned long	SyncookiesRecv;
 	unsigned long	SyncookiesFailed;
 	unsigned long	EmbryonicRsts;
+	unsigned long	PruneCalled; 
+	unsigned long	RcvPruned;
+	unsigned long	OfoPruned;
 };
  	
 #endif
diff -u --recursive --new-file v2.1.119/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.119/linux/include/net/sock.h	Thu Aug 27 19:56:30 1998
+++ linux/include/net/sock.h	Fri Sep  4 19:24:36 1998
@@ -87,6 +87,8 @@
 
 #include <asm/atomic.h>
 
+#define MIN_WRITE_SPACE	2048
+
 /* The AF_UNIX specific socket options */
 struct unix_opt {
 	int 			family;
@@ -134,6 +136,7 @@
 	struct in6_addr 	saddr;
 	struct in6_addr 	rcv_saddr;
 	struct in6_addr		daddr;
+	struct in6_addr		*daddr_cache;
 
 	__u32			flow_lbl;
 	int			hop_limit;
@@ -141,21 +144,28 @@
 	int			mcast_oif;
 	__u8			priority;
 
-
-	/* sockopt flags */
-
-	__u8			recvsrcrt:1,
-	                        rxinfo:1,
+	/* pktoption flags */
+	union {
+		struct {
+			__u8	srcrt:2,
+			        rxinfo:1,
 				rxhlim:1,
 				hopopts:1,
 				dstopts:1,
-				mc_loop:1,
-                                unused:2;
+                                authhdr:1,
+                                unused:1;
+		} bits;
+		__u8		all;
+	} rxopt;
+
+	/* sockopt flags */
+	__u8			mc_loop:1;
 
 	struct ipv6_mc_socklist	*ipv6_mc_list;
 	__u32			dst_cookie;
 
-	struct ipv6_options	*opt;
+	struct ipv6_txoptions	*opt;
+	struct sk_buff		*pktoptions;
 };
 
 struct raw6_opt {
@@ -207,6 +217,10 @@
 	__u32	snd_wl2;	/* Ack sequence for update		*/
 	__u32	snd_wnd;	/* The window we expect to receive	*/
 	__u32	max_window;
+	__u32	pmtu_cookie;	/* Last pmtu seen by socket		*/
+	__u16	mss_cache;	/* Cached effective mss, not including SACKS */
+	__u16	mss_clamp;	/* Maximal mss, negotiated at connection setup */
+	__u16	ext_header_len;	/* Dave, do you allow mw to use this hole? 8) --ANK */
 	__u8	pending;	/* pending events			*/
 	__u8	retransmits;
 	__u32	last_ack_sent;	/* last ack we sent			*/
@@ -226,6 +240,7 @@
  	__u32	snd_ssthresh;	/* Slow start size threshold		*/
 	__u8	dup_acks;	/* Consequetive duplicate acks seen from other end */
 	__u8	delayed_acks;
+	__u16	user_mss;  	/* mss requested by user in ioctl */
 
 	/* Two commonly used timers in both sender and receiver paths. */
  	struct timer_list	retransmit_timer;	/* Resend (no ack)	*/
@@ -252,7 +267,6 @@
 		wscale_ok,	/* Wscale seen on SYN packet		*/
 		sack_ok;	/* SACK seen on SYN packet		*/
 	char	saw_tstamp;	/* Saw TIMESTAMP on last packet		*/
-        __u16	in_mss;		/* MSS option received from sender	*/
         __u8	snd_wscale;	/* Window scaling received from sender	*/
         __u8	rcv_wscale;	/* Window scaling to send to receiver	*/
         __u32	rcv_tsval;	/* Time stamp value             	*/
@@ -270,6 +284,9 @@
 	__u32	urg_seq;
 	__u32	urg_data;
 
+	__u32	last_seg_size;	/* Size of last incoming segment */
+	__u32	rcv_mss;	/* MSS used for delayed ACK decisions */ 
+
 	struct open_request	*syn_wait_queue;
 	struct open_request	**syn_wait_last;
 
@@ -390,12 +407,6 @@
 
 	struct proto		*prot;
 
-	/* mss is min(mtu, max_window)
-	 * XXX Fix this, mtu only used in one TCP place and that is it -DaveM
-	 */
-	unsigned short		mtu;       /* mss negotiated in the syn's */
-	unsigned short		mss;       /* current eff. mss - can change */
-	unsigned short		user_mss;  /* mss requested by user in ioctl */
 	unsigned short		shutdown;
 
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
@@ -868,6 +879,26 @@
 	return -err;
 }
 
+extern __inline__ unsigned long sock_wspace(struct sock *sk)
+{
+	int amt = 0;
+
+	if (!(sk->shutdown & SEND_SHUTDOWN)) {
+		amt = sk->sndbuf - atomic_read(&sk->wmem_alloc);
+		if (amt < 0) 
+			amt = 0;
+	}
+	return amt;
+}
+
+/*
+ *	Default write policy as shown to user space via poll/select/SIGIO
+ *	Kernel internally doesn't use the MIN_WRITE_SPACE threshold.
+ */
+extern __inline__ int sock_writeable(struct sock *sk) 
+{
+	return sock_wspace(sk) >= MIN_WRITE_SPACE;
+}
 
 /* 
  *	Declarations from timer.c 
diff -u --recursive --new-file v2.1.119/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.1.119/linux/include/net/tcp.h	Fri Jul 31 17:11:11 1998
+++ linux/include/net/tcp.h	Fri Sep  4 19:24:36 1998
@@ -78,6 +78,7 @@
 	unsigned short		flags;
 #define TCPB_FLAG_LOCKED	0x0001
 #define TCPB_FLAG_FASTREUSE	0x0002
+#define TCPB_FLAG_GOODSOCKNUM	0x0004
 
 	struct tcp_bind_bucket	*next;
 	struct sock		*owners;
@@ -230,11 +231,8 @@
 	return tcp_lhashfn(sk->num);
 }
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-#define NETHDR_SIZE	sizeof(struct ipv6hdr)
-#else
-#define NETHDR_SIZE	sizeof(struct iphdr) + 40
-#endif
+/* Note, that it is > than ipv6 header */
+#define NETHDR_SIZE	(sizeof(struct iphdr) + 40)
 
 /*
  * 40 is maximal IP options size
@@ -257,7 +255,6 @@
 #define MIN_WINDOW	2048
 #define MAX_ACK_BACKLOG	2
 #define MAX_DELAY_ACK	2
-#define MIN_WRITE_SPACE	2048
 #define TCP_WINDOW_DIFF	2048
 
 /* urg_data states */
@@ -354,7 +351,7 @@
 struct tcp_v6_open_req {
 	struct in6_addr		loc_addr;
 	struct in6_addr		rmt_addr;
-	struct ipv6_options	*opt;
+	struct sk_buff		*pktopts;
 	int			iif;
 };
 #endif
@@ -400,6 +397,13 @@
 /*
  *	Pointers to address related TCP functions
  *	(i.e. things that depend on the address family)
+ *
+ * 	BUGGG_FUTURE: all the idea behind this struct is wrong.
+ *	It mixes socket frontend with transport function.
+ *	With port sharing between IPv6/v4 it gives the only advantage,
+ *	only poor IPv6 needs to permanently recheck, that it
+ *	is still IPv6 8)8) It must be cleaned up as soon as possible.
+ *						--ANK (980802)
  */
 
 struct tcp_func {
@@ -414,7 +418,7 @@
 
 	int			(*conn_request)		(struct sock *sk,
 							 struct sk_buff *skb,
-							 void *opt, __u32 isn);
+							 __u32 isn);
 
 	struct sock *		(*syn_recv_sock)	(struct sock *sk,
 							 struct sk_buff *skb,
@@ -424,6 +428,10 @@
 	struct sock *		(*get_sock)		(struct sk_buff *skb,
 							 struct tcphdr *th);
 
+	__u16			net_header_len;
+
+
+
 	int			(*setsockopt)		(struct sock *sk, 
 							 int level, 
 							 int optname, 
@@ -490,22 +498,24 @@
 extern int			tcp_rcv_state_process(struct sock *sk, 
 						      struct sk_buff *skb,
 						      struct tcphdr *th,
-						      void *opt, __u16 len);
+						      unsigned len);
 
 extern int			tcp_rcv_established(struct sock *sk, 
 						    struct sk_buff *skb,
 						    struct tcphdr *th, 
-						    __u16 len);
+						    unsigned len);
 
 extern int			tcp_timewait_state_process(struct tcp_tw_bucket *tw,
 							   struct sk_buff *skb,
 							   struct tcphdr *th,
-							   void *opt, __u16 len);
+							   unsigned len);
 
 extern void			tcp_close(struct sock *sk, 
 					  unsigned long timeout);
 extern struct sock *		tcp_accept(struct sock *sk, int flags);
 extern unsigned int		tcp_poll(struct file * file, struct socket *sock, struct poll_table_struct *wait);
+extern void			tcp_write_space(struct sock *sk); 
+
 extern int			tcp_getsockopt(struct sock *sk, int level, 
 					       int optname, char *optval, 
 					       int *optlen);
@@ -536,12 +546,11 @@
 
 extern int			tcp_v4_conn_request(struct sock *sk,
 						    struct sk_buff *skb,
-						    void *ptr, __u32 isn);
+						    __u32 isn);
 
 extern struct sock *		tcp_create_openreq_child(struct sock *sk,
 							 struct open_request *req,
-							 struct sk_buff *skb,
-							 int mss);
+							 struct sk_buff *skb);
 
 extern struct sock *		tcp_v4_syn_recv_sock(struct sock *sk,
 						     struct sk_buff *skb,
@@ -628,30 +637,25 @@
 
 extern struct tcp_sl_timer tcp_slt_array[TCP_SLT_MAX];
  
+extern int tcp_sync_mss(struct sock *sk, u32 pmtu);
+
 /* Compute the current effective MSS, taking SACKs and IP options,
  * and even PMTU discovery events into account.
  */
+
 static __inline__ unsigned int tcp_current_mss(struct sock *sk)
 {
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 	struct dst_entry *dst = sk->dst_cache;
-	unsigned int mss_now = sk->mss; 
+	int mss_now = tp->mss_cache; 
 
-	if(dst && (sk->mtu < dst->pmtu)) {
-		unsigned int mss_distance = (sk->mtu - sk->mss);
-
-		/* PMTU discovery event has occurred. */
-		sk->mtu = dst->pmtu;
-		mss_now = sk->mss = sk->mtu - mss_distance;
-	}
+	if (dst && dst->pmtu != tp->pmtu_cookie)
+		mss_now = tcp_sync_mss(sk, dst->pmtu);
 
 	if(tp->sack_ok && tp->num_sacks)
 		mss_now -= (TCPOLEN_SACK_BASE_ALIGNED +
 			    (tp->num_sacks * TCPOLEN_SACK_PERBLOCK));
-	if(sk->opt)
-		mss_now -= sk->opt->optlen;
-
-	return mss_now; 
+	return mss_now > 8 ? mss_now : 8;
 }
 
 /* Compute the actual receive window we are currently advertising.
@@ -715,7 +719,12 @@
  * skbuff.h:skbuff->cb[xxx] size appropriately.
  */
 struct tcp_skb_cb {
-	struct inet_skb_parm	header;	/* For incoming frames		*/
+	union {
+		struct inet_skb_parm	h4;
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+		struct inet6_skb_parm	h6;
+#endif
+	} header;	/* For incoming frames		*/
 	__u32		seq;		/* Starting sequence number	*/
 	__u32		end_seq;	/* SEQ + FIN + SYN + datalen	*/
 	unsigned long	when;		/* used to compute rtt's	*/
@@ -787,7 +796,7 @@
 	 *
 	 * 	Don't use the nagle rule for urgent data.
 	 */
-	if (!sk->nonagle && skb->len < (sk->mss >> 1) && tp->packets_out &&
+	if (!sk->nonagle && skb->len < (tp->mss_cache >> 1) && tp->packets_out &&
 	    !(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_URG))
 		nagle_check = 0;
 
@@ -913,8 +922,6 @@
 	 * SACKs don't matter, we never delay an ACK when we
 	 * have any of those going out.
 	 */
-	if(ts)
-		mss += TCPOLEN_TSTAMP_ALIGNED;
 	*ptr++ = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | mss);
 	if (ts) {
 		if(sack)
diff -u --recursive --new-file v2.1.119/linux/include/net/transp_v6.h linux/include/net/transp_v6.h
--- v2.1.119/linux/include/net/transp_v6.h	Fri Jul 31 17:14:06 1998
+++ linux/include/net/transp_v6.h	Fri Sep  4 19:24:36 1998
@@ -28,7 +28,7 @@
 extern int			datagram_send_ctl(struct msghdr *msg,
 						  int *oif,
 						  struct in6_addr **src_addr,
-						  struct ipv6_options *opt,
+						  struct ipv6_txoptions *opt,
 						  int *hlimit);
 
 #define		LOOPBACK4_IPV6		__constant_htonl(0x7f000006)
@@ -37,6 +37,8 @@
  *	address family specific functions
  */
 extern struct tcp_func	ipv4_specific;
+
+extern int inet6_destroy_sock(struct sock *sk);
 
 #endif
 
diff -u --recursive --new-file v2.1.119/linux/init/main.c linux/init/main.c
--- v2.1.119/linux/init/main.c	Wed Aug 26 11:37:44 1998
+++ linux/init/main.c	Wed Sep  2 09:40:06 1998
@@ -1005,6 +1005,8 @@
 		idle();
 }
 
+#define smp_init()	do { } while (0)
+
 #else
 
 /*
@@ -1018,20 +1020,10 @@
 {
 	/* Get other processors into their bootup holding patterns. */
 	smp_boot_cpus();
-}		
-
-/*
- *	The autoprobe routines assume CPU#0 on the i386
- *	so we don't actually set the game in motion until
- *	they are finished.
- */
- 
-static void __init smp_begin(void)
-{
 	smp_threads_ready=1;
 	smp_commence();
-}
-	
+}		
+
 #endif
 
 extern void initialize_secondary(void);
@@ -1112,13 +1104,10 @@
 #if defined(CONFIG_QUOTA)
 	dquot_init_hash();
 #endif
+	check_bugs();
 	printk("POSIX conformance testing by UNIFIX\n");
 
-#ifdef __SMP__
 	smp_init();
-#endif
-
-	check_bugs();
 
 #if defined(CONFIG_MTRR)	/* Do this after SMP initialization */
 /*
@@ -1126,10 +1115,9 @@
  * everything is up" style function where this would belong better
  * than in init/main.c..
  */
-	mtrr_init ();
+	mtrr_init();
 #endif
 
-	sock_init();
 #ifdef CONFIG_SYSCTL
 	sysctl_init();
 #endif
@@ -1200,6 +1188,9 @@
 	int real_root_mountflags;
 #endif
 
+	/* Networking initialization needs a process context */ 
+	sock_init();
+
 	/* Launch bdflush from here, instead of the old syscall way. */
 	kernel_thread(bdflush, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
 	/* Start the background pageout daemon. */
@@ -1221,15 +1212,6 @@
 	else mount_initrd =0;
 #endif
 	setup(0);
-
-#ifdef __SMP__
-	/*
-	 *	With the devices probed and setup we can
-	 *	now enter SMP mode.
-	 */
-	
-	smp_begin();
-#endif	
 
 #ifdef CONFIG_UMSDOS_FS
 	{
diff -u --recursive --new-file v2.1.119/linux/ipc/sem.c linux/ipc/sem.c
--- v2.1.119/linux/ipc/sem.c	Wed Aug 26 11:37:44 1998
+++ linux/ipc/sem.c	Wed Sep  2 09:30:11 1998
@@ -466,10 +466,10 @@
 		if (ipcperms (ipcp, S_IRUGO))
 			goto out;
 		switch (cmd) {
-		case GETVAL : return curr->semval;
-		case GETPID : return curr->sempid;
-		case GETNCNT: return count_semncnt(sma,semnum);
-		case GETZCNT: return count_semzcnt(sma,semnum);
+		case GETVAL : err = curr->semval; goto out;
+		case GETPID : err = curr->sempid; goto out;
+		case GETNCNT: err = count_semncnt(sma,semnum); goto out;
+		case GETZCNT: err = count_semzcnt(sma,semnum); goto out;
 		case GETALL:
 			array = arg.array;
 			break;
diff -u --recursive --new-file v2.1.119/linux/ipc/util.c linux/ipc/util.c
--- v2.1.119/linux/ipc/util.c	Wed Aug 26 11:37:45 1998
+++ linux/ipc/util.c	Mon Aug 31 11:12:22 1998
@@ -125,4 +125,8 @@
 	return -ENOSYS;
 }
 
+void shm_unuse(unsigned long entry, unsigned long page)
+{
+}
+
 #endif /* CONFIG_SYSVIPC */
diff -u --recursive --new-file v2.1.119/linux/kernel/fork.c linux/kernel/fork.c
--- v2.1.119/linux/kernel/fork.c	Wed Aug 26 11:37:45 1998
+++ linux/kernel/fork.c	Fri Sep  4 19:15:09 1998
@@ -29,8 +29,10 @@
 #include <asm/mmu_context.h>
 #include <asm/uaccess.h>
 
-int nr_tasks=1;
-int nr_running=1;
+/* The idle tasks do not count.. */
+int nr_tasks=0;
+int nr_running=0;
+
 unsigned long int total_forks=0;	/* Handle normal Linux uptimes. */
 int last_pid=0;
 
@@ -556,19 +558,6 @@
 	p->lock_depth = -1;		/* -1 = no lock */
 	p->start_time = jiffies;
 
-	{
-		/* This makes it visible to the rest of the system */
-		unsigned long flags;
-		write_lock_irqsave(&tasklist_lock, flags);
-		SET_LINKS(p);
-		hash_pid(p);
-		write_unlock_irqrestore(&tasklist_lock, flags);
-	}
-
-	nr_tasks++;
-	if (p->user)
-		atomic_inc(&p->user->count);
-
 	retval = -ENOMEM;
 	/* copy all the process information */
 	if (copy_files(clone_flags, p))
@@ -598,9 +587,23 @@
 	current->counter >>= 1;
 	p->counter = current->counter;
 
-	/* Ok, add it to the run-queues, let it rip! */
+	/*
+	 * Ok, add it to the run-queues and make it
+	 * visible to the rest of the system.
+	 *
+	 * Let it rip!
+	 */
 	retval = p->pid;
 	if (retval) {
+		write_lock_irq(&tasklist_lock);
+		SET_LINKS(p);
+		hash_pid(p);
+		write_unlock_irq(&tasklist_lock);
+
+		nr_tasks++;
+		if (p->user)
+			atomic_inc(&p->user->count);
+
 		p->next_run = NULL;
 		p->prev_run = NULL;
 		wake_up_process(p);		/* do this last */
@@ -624,17 +627,6 @@
 	if (p->binfmt && p->binfmt->module)
 		__MOD_DEC_USE_COUNT(p->binfmt->module);
 
-	{
-		unsigned long flags;
-		write_lock_irqsave(&tasklist_lock, flags); 
-		unhash_pid(p);
-		REMOVE_LINKS(p);
-		write_unlock_irqrestore(&tasklist_lock, flags);
-	}
-
-	if (p->user)
-		atomic_dec(&p->user->count);
-	nr_tasks--;
 	add_free_taskslot(p->tarray_ptr);
 bad_fork_free:
 	free_task_struct(p);
diff -u --recursive --new-file v2.1.119/linux/kernel/sched.c linux/kernel/sched.c
--- v2.1.119/linux/kernel/sched.c	Thu Aug 27 19:56:30 1998
+++ linux/kernel/sched.c	Fri Sep  4 09:13:29 1998
@@ -844,8 +844,7 @@
 
 	read_lock(&tasklist_lock);
 	for_each_task(p) {
-		if (p->pid &&
-		    (p->state == TASK_RUNNING ||
+		if ((p->state == TASK_RUNNING ||
 		     p->state == TASK_UNINTERRUPTIBLE ||
 		     p->state == TASK_SWAPPING))
 			nr += FIXED_1;
diff -u --recursive --new-file v2.1.119/linux/mm/swap_state.c linux/mm/swap_state.c
--- v2.1.119/linux/mm/swap_state.c	Thu Aug 20 17:05:19 1998
+++ linux/mm/swap_state.c	Mon Aug 31 13:04:26 1998
@@ -24,14 +24,6 @@
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
 
-#ifdef SWAP_CACHE_INFO
-unsigned long swap_cache_add_total = 0;
-unsigned long swap_cache_add_success = 0;
-unsigned long swap_cache_del_total = 0;
-unsigned long swap_cache_del_success = 0;
-unsigned long swap_cache_find_total = 0;
-unsigned long swap_cache_find_success = 0;
-
 /* 
  * Keep a reserved false inode which we will use to mark pages in the
  * page cache are acting as swap cache instead of file cache. 
@@ -43,6 +35,13 @@
  */
 struct inode swapper_inode;
 
+#ifdef SWAP_CACHE_INFO
+unsigned long swap_cache_add_total = 0;
+unsigned long swap_cache_add_success = 0;
+unsigned long swap_cache_del_total = 0;
+unsigned long swap_cache_del_success = 0;
+unsigned long swap_cache_find_total = 0;
+unsigned long swap_cache_find_success = 0;
 
 void show_swap_cache_info(void)
 {
@@ -63,13 +62,13 @@
 	       page_address(page), atomic_read(&page->count), entry);
 #endif
 	if (PageTestandSetSwapCache(page)) {
-		printk("swap_cache: replacing non-empty entry %08lx "
+		printk(KERN_ERR "swap_cache: replacing non-empty entry %08lx "
 		       "on page %08lx\n",
 		       page->offset, page_address(page));
 		return 0;
 	}
 	if (page->inode) {
-		printk("swap_cache: replacing page-cached entry "
+		printk(KERN_ERR "swap_cache: replacing page-cached entry "
 		       "on page %08lx\n", page_address(page));
 		return 0;
 	}
@@ -85,12 +84,16 @@
 }
 
 /*
- * If swap_map[] reaches SWAP_MAP_MAX the entries are treated as "permanent".
+ * Verify that a swap entry is valid and increment its swap map count.
+ *
+ * Note: if swap_map[] reaches SWAP_MAP_MAX the entries are treated as
+ * "permanent", but will be reclaimed by the next swapoff.
  */
-void swap_duplicate(unsigned long entry)
+int swap_duplicate(unsigned long entry)
 {
 	struct swap_info_struct * p;
 	unsigned long offset, type;
+	int result = 0;
 
 	if (!entry)
 		goto out;
@@ -105,36 +108,44 @@
 		goto bad_offset;
 	if (!p->swap_map[offset])
 		goto bad_unused;
+	/*
+	 * Entry is valid, so increment the map count.
+	 */
 	if (p->swap_map[offset] < SWAP_MAP_MAX)
 		p->swap_map[offset]++;
 	else {
 		static int overflow = 0;
 		if (overflow++ < 5)
-			printk("swap_duplicate: entry %08lx map count=%d\n",
+			printk(KERN_WARNING
+				"swap_duplicate: entry %08lx map count=%d\n",
 				entry, p->swap_map[offset]);
 		p->swap_map[offset] = SWAP_MAP_MAX;
 	}
+	result = 1;
 #ifdef DEBUG_SWAP
 	printk("DebugVM: swap_duplicate(entry %08lx, count now %d)\n",
 	       entry, p->swap_map[offset]);
 #endif
 out:
-	return;
+	return result;
 
 bad_file:
-	printk("swap_duplicate: Trying to duplicate nonexistent swap-page\n");
+	printk(KERN_ERR
+		"swap_duplicate: entry %08lx, nonexistent swap file\n", entry);
 	goto out;
 bad_offset:
-	printk("swap_duplicate: offset exceeds max\n");
+	printk(KERN_ERR
+		"swap_duplicate: entry %08lx, offset exceeds max\n", entry);
 	goto out;
 bad_unused:
-	printk("swap_duplicate at %8p: unused page\n", 
-	       __builtin_return_address(0));
+	printk(KERN_ERR
+		"swap_duplicate at %8p: entry %08lx, unused page\n", 
+	       __builtin_return_address(0), entry);
 	goto out;
 }
 
 
-void remove_from_swap_cache(struct page *page)
+static inline void remove_from_swap_cache(struct page *page)
 {
 	if (!page->inode) {
 		printk ("VM: Removing swap cache page with zero inode hash "
@@ -163,7 +174,7 @@
 }
 
 
-int delete_from_swap_cache(struct page *page)
+void delete_from_swap_cache(struct page *page)
 {
 #ifdef SWAP_CACHE_INFO
 	swap_cache_del_total++;
@@ -180,9 +191,7 @@
 #endif
 		remove_from_swap_cache (page);
 		swap_free (entry);
-		return 1;
 	}
-	return 0;
 }
 
 /* 
@@ -218,57 +227,67 @@
 		found = find_page(&swapper_inode, entry);
 		if (!found)
 			return 0;
-		if (found->inode != &swapper_inode 
-		    || !PageSwapCache(found)) {
-			__free_page(found);
-			printk ("VM: Found a non-swapper swap page!\n");
-			return 0;
-		}
+		if (found->inode != &swapper_inode || !PageSwapCache(found))
+			goto out_bad;
 		if (!PageLocked(found))
 			return found;
 		__free_page(found);
 		__wait_on_page(found);
 	}
+
+out_bad:
+	printk (KERN_ERR "VM: Found a non-swapper swap page!\n");
+	__free_page(found);
+	return 0;
 }
 
 /* 
  * Locate a page of swap in physical memory, reserving swap cache space
  * and reading the disk if it is not already cached.  If wait==0, we are
  * only doing readahead, so don't worry if the page is already locked.
+ *
+ * A failure return means that either the page allocation failed or that
+ * the swap entry is no longer in use.
  */
 
 struct page * read_swap_cache_async(unsigned long entry, int wait)
 {
-	struct page *found_page, *new_page = 0;
-	unsigned long new_page_addr = 0;
+	struct page *found_page, *new_page;
+	unsigned long new_page_addr;
 	
 #ifdef DEBUG_SWAP
 	printk("DebugVM: read_swap_cache_async entry %08lx%s\n",
 	       entry, wait ? ", wait" : "");
 #endif
-repeat:
+	/*
+	 * Look for the page in the swap cache.
+	 */
 	found_page = lookup_swap_cache(entry);
-	if (found_page) {
-		if (new_page)
-			__free_page(new_page);
-		return found_page;
-	}
+	if (found_page)
+		goto out;
+
+	new_page_addr = __get_free_page(GFP_KERNEL);
+	if (!new_page_addr)
+		goto out;	/* Out of memory */
+	new_page = mem_map + MAP_NR(new_page_addr);
+
+	/*
+	 * Check the swap cache again, in case we stalled above.
+	 */
+	found_page = lookup_swap_cache(entry);
+	if (found_page)
+		goto out_free_page;
+	/*
+	 * Make sure the swap entry is still in use.
+	 */
+	if (!swap_duplicate(entry))	/* Account for the swap cache */
+		goto out_free_page;
+	/* 
+	 * Add it to the swap cache and read its contents.
+	 */
+	if (!add_to_swap_cache(new_page, entry))
+		goto out_free_page;
 
-	/* The entry is not present.  Lock down a new page, add it to
-	 * the swap cache and read its contents. */
-	if (!new_page) {
-		new_page_addr = __get_free_page(GFP_KERNEL);
-		if (!new_page_addr)
-			return 0;	/* Out of memory */
-		new_page = mem_map + MAP_NR(new_page_addr);
-		goto repeat;		/* We might have stalled */
-	}
-	
-	if (!add_to_swap_cache(new_page, entry)) {
-		free_page(new_page_addr);
-		return 0;
-	}
-	swap_duplicate(entry);		/* Account for the swap cache */
 	set_bit(PG_locked, &new_page->flags);
 	rw_swap_page(READ, entry, (char *) new_page_addr, wait);
 #ifdef DEBUG_SWAP
@@ -277,5 +296,9 @@
 	       entry, (char *) page_address(new_page));
 #endif
 	return new_page;
-}
 
+out_free_page:
+	__free_page(new_page);
+out:
+	return found_page;
+}
diff -u --recursive --new-file v2.1.119/linux/mm/swapfile.c linux/mm/swapfile.c
--- v2.1.119/linux/mm/swapfile.c	Thu Aug 27 19:56:30 1998
+++ linux/mm/swapfile.c	Mon Aug 31 13:04:26 1998
@@ -297,30 +297,36 @@
 {
 	struct swap_info_struct * si = &swap_info[type];
 	struct task_struct *p;
-	unsigned long page = 0;
 	struct page *page_map;
-	unsigned long entry;
+	unsigned long entry, page;
 	int i;
 
 	while (1) {
 		/*
 		 * Find a swap page in use and read it in.
 		 */
-		for (i = 1 , entry = 0; i < si->max ; i++) {
+		for (i = 1; i < si->max ; i++) {
 			if (si->swap_map[i] > 0 && si->swap_map[i] != SWAP_MAP_BAD) {
-				entry = SWP_ENTRY(type, i);
-				break;
+				goto found_entry;
 			}
 		}
-		if (!entry)
-			break;
+		break;
+
+	found_entry:
+		entry = SWP_ENTRY(type, i);
 
 		/* Get a page for the entry, using the existing swap
                    cache page if there is one.  Otherwise, get a clean
                    page and read the swap into it. */
 		page_map = read_swap_cache(entry);
-		if (!page_map)
-			return -ENOMEM;
+		if (!page_map) {
+			/*
+			 * Continue searching if the entry became unused.
+			 */
+			if (si->swap_map[i] == 0)
+				continue;
+  			return -ENOMEM;
+		}
 		page = page_address(page_map);
 		read_lock(&tasklist_lock);
 		for_each_task(p)
@@ -331,11 +337,15 @@
                    page we've been using. */
 		if (PageSwapCache(page_map))
 			delete_from_swap_cache(page_map);
-		free_page(page);
+		__free_page(page_map);
+		/*
+		 * Check for and clear any overflowed swap map counts.
+		 */
 		if (si->swap_map[i] != 0) {
 			if (si->swap_map[i] != SWAP_MAP_MAX)
-				printk("try_to_unuse: entry %08lx "
-				       "not in use\n", entry);
+				printk(KERN_ERR
+					"try_to_unuse: entry %08lx count=%d\n",
+					entry, si->swap_map[i]);
 			si->swap_map[i] = 0;
 			nr_swap_pages++;
 		}
@@ -376,10 +386,9 @@
 		prev = type;
 	}
 	err = -EINVAL;
-	if (type < 0){
-		dput(dentry);
-		goto out;
-	}
+	if (type < 0)
+		goto out_dput;
+
 	if (prev < 0) {
 		swap_list.head = p->next;
 	} else {
@@ -392,7 +401,6 @@
 	p->flags = SWP_USED;
 	err = try_to_unuse(type);
 	if (err) {
-		dput(dentry);
 		/* re-insert swap space back into swap_list */
 		for (prev = -1, i = swap_list.head; i >= 0; prev = i, i = swap_info[i].next)
 			if (p->prio >= swap_info[i].prio)
@@ -403,7 +411,7 @@
 		else
 			swap_info[prev].next = p - swap_info;
 		p->flags = SWP_WRITEOK;
-		goto out;
+		goto out_dput;
 	}
 	if(p->swap_device){
 		memset(&filp, 0, sizeof(filp));		
@@ -418,9 +426,9 @@
 	}
 	dput(dentry);
 
-	nr_swap_pages -= p->pages;
-	dput(p->swap_file);
+	dentry = p->swap_file;
 	p->swap_file = NULL;
+	nr_swap_pages -= p->pages;
 	p->swap_device = 0;
 	vfree(p->swap_map);
 	p->swap_map = NULL;
@@ -428,6 +436,9 @@
 	p->swap_lockmap = NULL;
 	p->flags = 0;
 	err = 0;
+
+out_dput:
+	dput(dentry);
 out:
 	unlock_kernel();
 	return err;
@@ -719,4 +730,3 @@
 	val->totalswap <<= PAGE_SHIFT;
 	return;
 }
-
diff -u --recursive --new-file v2.1.119/linux/net/Config.in linux/net/Config.in
--- v2.1.119/linux/net/Config.in	Tue Jun 23 10:01:30 1998
+++ linux/net/Config.in	Thu Aug 27 19:33:08 1998
@@ -17,7 +17,9 @@
 if [ "$CONFIG_INET" = "y" ]; then
   source net/ipv4/Config.in
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    tristate 'The IPv6 protocol (EXPERIMENTAL)' CONFIG_IPV6
+#   Sorry, but IPv6 as module is still invalid.
+#   tristate 'The IPv6 protocol (EXPERIMENTAL)' CONFIG_IPV6
+    bool 'The IPv6 protocol (EXPERIMENTAL)' CONFIG_IPV6
     if [ "$CONFIG_IPV6" != "n" ]; then
 	    source net/ipv6/Config.in
     fi
diff -u --recursive --new-file v2.1.119/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v2.1.119/linux/net/appletalk/ddp.c	Tue Jul 21 00:15:33 1998
+++ linux/net/appletalk/ddp.c	Thu Aug 27 19:33:08 1998
@@ -1017,7 +1017,6 @@
 
 	sk->destruct = NULL;
 	/* Checksums on by default */
-	sk->mtu = DDP_MAXSZ;
 	sk->zapped = 1;
 
 	return (0);
diff -u --recursive --new-file v2.1.119/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.1.119/linux/net/ax25/af_ax25.c	Tue Aug 18 22:02:08 1998
+++ linux/net/ax25/af_ax25.c	Thu Aug 27 19:33:08 1998
@@ -849,7 +849,6 @@
 	sk->destruct = ax25_free_sock;
 	sock->ops    = &ax25_proto_ops;
 	sk->protocol = protocol;
-	sk->mtu      = AX25_MTU;	/* 256 */
 
 	ax25->sk          = sk;
 	sk->protinfo.ax25 = ax25;
@@ -892,7 +891,6 @@
 	sk->sndbuf   = osk->sndbuf;
 	sk->debug    = osk->debug;
 	sk->state    = TCP_ESTABLISHED;
-	sk->mtu      = osk->mtu;
 	sk->sleep    = osk->sleep;
 	sk->zapped   = osk->zapped;
 
diff -u --recursive --new-file v2.1.119/linux/net/ax25/ax25_ip.c linux/net/ax25/ax25_ip.c
--- v2.1.119/linux/net/ax25/ax25_ip.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ax25/ax25_ip.c	Fri Sep  4 15:33:26 1998
@@ -106,7 +106,7 @@
 {
 	struct sk_buff *ourskb;
 	unsigned char *bp  = skb->data;
-	struct device *dev = skb->dev;
+	struct device *dev;
 	ax25_address *src, *dst;
 	ax25_route *route;
 	ax25_dev *ax25_dev;
@@ -117,10 +117,14 @@
   	if (arp_find(bp + 1, skb))
   		return 1;
 
-	if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
-		return 1;
+	route    = ax25_rt_find_route(dst, NULL);
+	dev      = route->dev;
 
-	route = ax25_rt_find_route(dst, dev);
+	if (dev == NULL)
+		dev = skb->dev;
+
+        if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
+                return 1;
 
 	if (bp[16] == AX25_P_IP) {
 		if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) {
@@ -139,6 +143,10 @@
 			 *      instead of using skb_clone() unless this
 			 *	gets fixed.
 			 */
+
+			ax25_address src_c;
+			ax25_address dst_c;
+
 			if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) {
 				kfree_skb(skb);
 				return 1;
@@ -149,9 +157,13 @@
 
 			kfree_skb(skb);
 
+			src_c = *src;
+			dst_c = *dst;
+
 			skb_pull(ourskb, AX25_HEADER_LEN - 1);	/* Keep PID */
 
-			ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], src, dst, route->digipeat, dev);
+			ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], &src_c, 
+&dst_c, route->digipeat, dev);
 
 			return 1;
 		}
diff -u --recursive --new-file v2.1.119/linux/net/core/datagram.c linux/net/core/datagram.c
--- v2.1.119/linux/net/core/datagram.c	Thu Mar 26 15:57:08 1998
+++ linux/net/core/datagram.c	Thu Aug 27 19:33:08 1998
@@ -54,15 +54,16 @@
 
 static inline void wait_for_packet(struct sock * sk)
 {
-	unsigned long flags;
+	struct wait_queue wait = { current, NULL };
+
+	add_wait_queue(sk->sleep, &wait);
+	current->state = TASK_INTERRUPTIBLE;
 
-	release_sock(sk);
-	save_flags(flags);
-	cli();
 	if (skb_peek(&sk->receive_queue) == NULL)
-		interruptible_sleep_on(sk->sleep);
-	restore_flags(flags);
-	lock_sock(sk);
+		schedule();
+
+	current->state = TASK_RUNNING;
+	remove_wait_queue(sk->sleep, &wait);
 }
 
 /*
@@ -84,6 +85,14 @@
  *	This function will lock the socket if a skb is returned, so the caller
  *	needs to unlock the socket in that case (usually by calling skb_free_datagram)
  *
+ *	* It does not lock socket since today. This function is
+ *	* free of race conditions. This measure should/can improve
+ *	* significantly datagram socket latencies at high loads,
+ *	* when data copying to user space takes lots of time.
+ *	* (BTW I've just killed the last cli() in IP/IPv6/core/netlink/packet
+ *	*  8) Great win.)
+ *	*			                    --ANK (980729)
+ *
  *	The order of the tests when we find no data waiting are specified
  *	quite explicitly by POSIX 1003.1g, don't change them without having
  *	the standard around please.
@@ -94,7 +103,6 @@
 	int error;
 	struct sk_buff *skb;
 
-	lock_sock(sk);
 restart:
 	while(skb_queue_empty(&sk->receive_queue))	/* No data */
 	{
@@ -129,13 +137,24 @@
 	   will suddenly eat the receive_queue */
 	if (flags & MSG_PEEK)
 	{
-		unsigned long flags;
-		save_flags(flags);
-		cli();
+		unsigned long cpu_flags;
+
+		/* It is the only POTENTIAL race condition
+		   in this function. skb may be stolen by
+		   another receiver after peek, but before
+		   incrementing use count, provided kernel
+		   is reentearble (it is not) or this function
+		   is called by interrupts.
+
+		   Protect it with global skb spinlock,
+		   though for now even this is overkill.
+		                                --ANK (980728)
+		 */
+		spin_lock_irqsave(&skb_queue_lock, cpu_flags);
 		skb = skb_peek(&sk->receive_queue);
 		if(skb!=NULL)
 			atomic_inc(&skb->users);
-		restore_flags(flags);
+		spin_unlock_irqrestore(&skb_queue_lock, cpu_flags);
 	} else
 		skb = skb_dequeue(&sk->receive_queue);
 
@@ -144,7 +163,6 @@
 	return skb;
 
 no_packet:
-	release_sock(sk);
 	*err = error;
 	return NULL;
 }
@@ -152,7 +170,6 @@
 void skb_free_datagram(struct sock * sk, struct sk_buff *skb)
 {
 	kfree_skb(skb);
-	release_sock(sk);
 }
 
 /*
@@ -184,6 +201,10 @@
  *	Datagram poll: Again totally generic. This also handles
  *	sequenced packet sockets providing the socket receive queue
  *	is only ever holding data ready to receive.
+ *
+ *	Note: when you _don't_ use this routine for this protocol,
+ *	and you use a different write policy from sock_writeable()
+ *	then please supply your own write_space callback.
  */
 
 unsigned int datagram_poll(struct file * file, struct socket *sock, poll_table *wait)
@@ -199,7 +220,7 @@
 		mask |= POLLERR;
 	if (sk->shutdown & RCV_SHUTDOWN)
 		mask |= POLLHUP;
-
+	
 	/* readable? */
 	if (!skb_queue_empty(&sk->receive_queue))
 		mask |= POLLIN | POLLRDNORM;
@@ -214,15 +235,8 @@
 	}
 
 	/* writable? */
-	if (!(sk->shutdown & SEND_SHUTDOWN)) {
-		if (sk->prot) {
-			if (sock_wspace(sk) >= MIN_WRITE_SPACE)
-				mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
-		} else {
-			if (sk->sndbuf - atomic_read(&sk->wmem_alloc) >= MIN_WRITE_SPACE)
-				mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
-		}
-	}
+	if (sock_writeable(sk))
+		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
 
 	return mask;
 }
diff -u --recursive --new-file v2.1.119/linux/net/core/dev.c linux/net/core/dev.c
--- v2.1.119/linux/net/core/dev.c	Thu Aug 20 17:05:19 1998
+++ linux/net/core/dev.c	Thu Aug 27 19:33:08 1998
@@ -16,6 +16,7 @@
  *		Alan Cox <gw4pts@gw4pts.ampr.org>
  *		David Hinds <dhinds@allegro.stanford.edu>
  *		Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ *		Adam Sulmicki <adam@cfar.umd.edu>
  *
  *	Changes:
  *		Alan Cox	:	device private ioctl copies fields back.
@@ -51,7 +52,10 @@
  *		Andi Kleen	:	Fix error reporting for SIOCGIFCONF
  *	    Michael Chastain	:	Fix signed/unsigned for SIOCGIFCONF
  *		Cyrus Durgin	:	Cleaned for KMOD
- *
+ *		Adam Sulmicki   :	Bug Fix : Network Device Unload
+ *					A network device unload needs to purge
+ *					the backlog queue.
+ *	Paul Rusty Russel	:	SIOCSIFNAME
  */
 
 #include <asm/uaccess.h>
@@ -154,6 +158,8 @@
 struct net_fastroute_stats dev_fastroute_stat;
 #endif
 
+static void dev_clear_backlog(struct device *dev);
+
 
 /******************************************************************************************
 
@@ -171,6 +177,16 @@
  *	Add a protocol ID to the list. Now that the input handler is
  *	smarter we can dispense with all the messy stuff that used to be
  *	here.
+ *
+ *	BEWARE!!! Protocol handlers, mangling input packets,
+ *	MUST BE last in hash buckets and checking protocol handlers
+ *	MUST start from promiscous ptype_all chain in net_bh.
+ *	It is true now, do not change it.
+ *	Explantion follows: if protocol handler, mangling packet, will
+ *	be the first on list, it is not able to sense, that packet
+ *	is cloned and should be copied-on-write, so that it will
+ *	change it and subsequent readers will get broken packet.
+ *							--ANK (980803)
  */
  
 void dev_add_pack(struct packet_type *pt)
@@ -448,7 +464,8 @@
 	/*
 	 *	Device is now down.
 	 */
-	 
+	dev_clear_backlog(dev);
+
 	dev->flags&=~(IFF_UP|IFF_RUNNING);
 #ifdef CONFIG_NET_FASTROUTE
 	dev_clear_fastroute(dev);
@@ -457,7 +474,6 @@
 	/*
 	 *	Tell people we are going down
 	 */
-	 
 	notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
 
 	return(0);
@@ -685,6 +701,45 @@
 }
 #endif
 
+static void dev_clear_backlog(struct device *dev)
+{
+	struct sk_buff *prev, *curr;
+
+	/*
+	 *
+	 *  Let now clear backlog queue. -AS
+	 *
+	 *  We are competing here both with netif_rx() and net_bh().
+	 *  We don't want either of those to mess with skb ptrs
+	 *  while we work on them, thus cli()/sti().
+	 *
+	 *  It looks better to use net_bh trick, at least
+	 *  to be sure, that we keep interrupt latency really low. --ANK (980727)
+	 */ 
+
+	if (backlog.qlen) {
+		start_bh_atomic();
+		curr = backlog.next;
+		while ( curr != (struct sk_buff *)(&backlog) ) {
+			unsigned long flags;
+			curr=curr->next;
+			if ( curr->prev->dev == dev ) {
+				prev = curr->prev;
+				spin_lock_irqsave(&skb_queue_lock, flags);
+				__skb_unlink(prev, &backlog);
+				spin_unlock_irqrestore(&skb_queue_lock, flags);
+				kfree_skb(prev);
+			}
+		}
+		end_bh_atomic();
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+		if (netdev_dropping)
+			netdev_wakeup();
+#else
+		netdev_dropping = 0;
+#endif
+	}
+}
 
 /*
  *	Receive a packet from a device driver and queue it for the upper
@@ -1320,7 +1375,7 @@
 	 */
 
 	dev->flags = (flags & (IFF_DEBUG|IFF_NOTRAILERS|IFF_RUNNING|IFF_NOARP|
-			       IFF_SLAVE|IFF_MASTER|
+			       IFF_NODYNARP|IFF_SLAVE|IFF_MASTER|
 			       IFF_MULTICAST|IFF_PORTSEL|IFF_AUTOMEDIA)) |
 				       (dev->flags & (IFF_UP|IFF_VOLATILE|IFF_PROMISC|IFF_ALLMULTI));
 
@@ -1391,12 +1446,11 @@
 			return dev_change_flags(dev, ifr->ifr_flags);
 		
 		case SIOCGIFMETRIC:	/* Get the metric on the interface (currently unused) */
-			ifr->ifr_metric = dev->metric;
+			ifr->ifr_metric = 0;
 			return 0;
 			
 		case SIOCSIFMETRIC:	/* Set the metric on the interface (currently unused) */
-			dev->metric = ifr->ifr_metric;
-			return 0;
+			return -EOPNOTSUPP;
 	
 		case SIOCGIFMTU:	/* Get the MTU of a device */
 			ifr->ifr_mtu = dev->mtu;
@@ -1419,10 +1473,8 @@
 				dev->mtu = ifr->ifr_mtu;
 				err = 0;
 			}
-			if (!err && dev->flags&IFF_UP) {
-				printk(KERN_DEBUG "SIFMTU %s(%s)\n", dev->name, current->comm);
+			if (!err && dev->flags&IFF_UP)
 				notifier_call_chain(&netdev_chain, NETDEV_CHANGEMTU, dev);
-			}
 			return err;
 
 		case SIOCGIFHWADDR:
@@ -1484,11 +1536,22 @@
 			return 0;
 
 		case SIOCSIFTXQLEN:
-			if(ifr->ifr_qlen<2 || ifr->ifr_qlen>1024)
+			/* Why <2? 0 and 1 are valid values. --ANK (980807) */
+			if(/*ifr->ifr_qlen<2 ||*/ ifr->ifr_qlen>1024)
 				return -EINVAL;
 			dev->tx_queue_len = ifr->ifr_qlen;
 			return 0;
 
+		case SIOCSIFNAME:
+			if (dev->flags&IFF_UP)
+				return -EBUSY;
+			if (dev_get(ifr->ifr_newname))
+				return -EEXIST;
+			memcpy(dev->name, ifr->ifr_newname, IFNAMSIZ);
+			dev->name[IFNAMSIZ-1] = 0;
+			notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev);
+			return 0;
+
 		/*
 		 *	Unknown or private ioctl
 		 */
@@ -1597,6 +1660,7 @@
 		case SIOCDELMULTI:
 		case SIOCSIFHWBROADCAST:
 		case SIOCSIFTXQLEN:
+		case SIOCSIFNAME:
 			if (!capable(CAP_NET_ADMIN))
 				return -EPERM;
 			dev_load(ifr.ifr_name);
@@ -1668,6 +1732,17 @@
 	struct device *d, **dp;
 
 	if (dev_boot_phase) {
+		/* This is NOT bug, but I am not sure, that all the
+		   devices, initialized before netdev module is started
+		   are sane. 
+
+		   Now they are chained to device boot list
+		   and probed later. If a module is initialized
+		   before netdev, but assumes that dev->init
+		   is really called by register_netdev(), it will fail.
+
+		   So that this message should be printed for a while.
+		 */
 		printk(KERN_INFO "early initialization of device %s is deferred\n", dev->name);
 
 		/* Check for existence, and append to tail of chain */
diff -u --recursive --new-file v2.1.119/linux/net/core/iovec.c linux/net/core/iovec.c
--- v2.1.119/linux/net/core/iovec.c	Tue Apr 14 14:29:26 1998
+++ linux/net/core/iovec.c	Thu Aug 27 19:33:08 1998
@@ -215,7 +215,7 @@
 			partial_cnt = 0;
 		}
 
-		if (len - copy > 0)
+		if (len > copy)
 		{
 			partial_cnt = copy % 4;
 			if (partial_cnt)
diff -u --recursive --new-file v2.1.119/linux/net/core/neighbour.c linux/net/core/neighbour.c
--- v2.1.119/linux/net/core/neighbour.c	Tue Mar 17 22:18:16 1998
+++ linux/net/core/neighbour.c	Thu Aug 27 19:33:08 1998
@@ -9,6 +9,9 @@
  *      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.
+ *
+ *	Fixes:
+ *	Vitaly E. Lavrov	releasing NULL neighbor in neigh_add.
  */
 
 #include <linux/config.h>
@@ -1033,7 +1036,8 @@
 					   ndm->ndm_state,
 					   nlh->nlmsg_flags&NLM_F_REPLACE, 0);
 		}
-		neigh_release(n);
+		if (n)
+			neigh_release(n);
 		end_bh_atomic();
 		return err;
 	}
@@ -1043,7 +1047,7 @@
 
 
 static int neigh_fill_info(struct sk_buff *skb, struct neighbour *n,
-			    pid_t pid, u32 seq, int event)
+			   u32 pid, u32 seq, int event)
 {
 	unsigned long now = jiffies;
 	struct ndmsg *ndm;
diff -u --recursive --new-file v2.1.119/linux/net/core/rtnetlink.c linux/net/core/rtnetlink.c
--- v2.1.119/linux/net/core/rtnetlink.c	Thu May 14 19:47:44 1998
+++ linux/net/core/rtnetlink.c	Thu Aug 27 19:33:08 1998
@@ -12,6 +12,8 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  *
+ *	Fixes:
+ *	Vitaly E. Lavrov		RTA_OK arithmetics was wrong.
  */
 
 #include <linux/config.h>
@@ -29,6 +31,7 @@
 #include <linux/mm.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
+#include <linux/capability.h>
 #include <linux/skbuff.h>
 #include <linux/init.h>
 
@@ -135,47 +138,8 @@
 	return err;
 }
 
-#ifdef CONFIG_RTNL_OLD_IFINFO
 static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct device *dev,
-				 int type, pid_t pid, u32 seq)
-{
-	struct ifinfomsg *r;
-	struct nlmsghdr  *nlh;
-	unsigned char	 *b = skb->tail;
-
-	nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*r));
-	if (pid) nlh->nlmsg_flags |= NLM_F_MULTI;
-	r = NLMSG_DATA(nlh);
-	r->ifi_addrlen = dev->addr_len;
-	r->ifi_address.sa_family = dev->type;
-	memcpy(&r->ifi_address.sa_data, dev->dev_addr, dev->addr_len);
-	r->ifi_broadcast.sa_family = dev->type;
-	memcpy(&r->ifi_broadcast.sa_data, dev->broadcast, dev->addr_len);
-	r->ifi_flags = dev->flags;
-	r->ifi_mtu = dev->mtu;
-	r->ifi_index = dev->ifindex;
-	r->ifi_link = dev->iflink;
-	strncpy(r->ifi_name, dev->name, IFNAMSIZ-1);
-	r->ifi_qdiscname[0] = 0;
-	r->ifi_qdisc = dev->qdisc_sleeping->handle;
-	if (dev->qdisc_sleeping)
-		strcpy(r->ifi_qdiscname, dev->qdisc_sleeping->ops->id);
-	if (dev->get_stats) {
-		struct net_device_stats *stats = dev->get_stats(dev);
-		if (stats)
-			RTA_PUT(skb, IFLA_STATS, sizeof(*stats), stats);
-	}
-	nlh->nlmsg_len = skb->tail - b;
-	return skb->len;
-
-nlmsg_failure:
-rtattr_failure:
-	skb_trim(skb, b - skb->data);
-	return -1;
-}
-#else
-static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct device *dev,
-				 int type, pid_t pid, u32 seq)
+				 int type, u32 pid, u32 seq)
 {
 	struct ifinfomsg *r;
 	struct nlmsghdr  *nlh;
@@ -218,7 +182,6 @@
 	skb_trim(skb, b - skb->data);
 	return -1;
 }
-#endif
 
 int rtnetlink_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
 {
@@ -266,12 +229,7 @@
 void rtmsg_ifinfo(int type, struct device *dev)
 {
 	struct sk_buff *skb;
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	int size = NLMSG_SPACE(sizeof(struct ifinfomsg)+
-			       RTA_LENGTH(sizeof(struct net_device_stats)));
-#else
 	int size = NLMSG_GOODSIZE;
-#endif
 
 	skb = alloc_skb(size, GFP_KERNEL);
 	if (!skb)
@@ -287,7 +245,7 @@
 
 static int rtnetlink_done(struct netlink_callback *cb)
 {
-	if (NETLINK_CREDS(cb->skb)->uid == 0 && cb->nlh->nlmsg_flags&NLM_F_ATOMIC)
+	if (cap_raised(NETLINK_CB(cb->skb).eff_cap, CAP_NET_ADMIN) && cb->nlh->nlmsg_flags&NLM_F_ATOMIC)
 		rtnl_shunlock();
 	return 0;
 }
@@ -342,13 +300,13 @@
 	sz_idx = type>>2;
 	kind = type&3;
 
-	if (kind != 2 && NETLINK_CREDS(skb)->uid) {
+	if (kind != 2 && !cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) {
 		*errp = -EPERM;
 		return -1;
 	}
 
 	if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
-		int rlen;
+		u32 rlen;
 
 		if (link->dumpit == NULL)
 			link = &(rtnetlink_links[PF_UNSPEC][type]);
@@ -357,12 +315,13 @@
 			goto err_inval;
 
 		/* Super-user locks all the tables to get atomic snapshot */
-		if (NETLINK_CREDS(skb)->uid == 0 && nlh->nlmsg_flags&NLM_F_ATOMIC)
+		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 (NETLINK_CREDS(skb)->uid == 0 && nlh->nlmsg_flags&NLM_F_ATOMIC)
+			if (cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN) && nlh->nlmsg_flags&NLM_F_ATOMIC)
 				atomic_dec(&rtnl_rlockct);
 			return -1;
 		}
@@ -431,7 +390,7 @@
 	struct nlmsghdr * nlh;
 
 	while (skb->len >= NLMSG_SPACE(0)) {
-		int rlen;
+		u32 rlen;
 
 		nlh = (struct nlmsghdr *)skb->data;
 		if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
diff -u --recursive --new-file v2.1.119/linux/net/core/scm.c linux/net/core/scm.c
--- v2.1.119/linux/net/core/scm.c	Thu May 14 19:47:44 1998
+++ linux/net/core/scm.c	Thu Aug 27 19:33:08 1998
@@ -138,10 +138,14 @@
 
 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))
 	{
+		err = -EINVAL;
+
+		if ((unsigned long)(((char*)cmsg - (char*)msg->msg_control)
+				    + cmsg->cmsg_len) > msg->msg_controllen)
+			goto error;
+
 		if (cmsg->cmsg_level != SOL_SOCKET)
 			continue;
-
-		err = -EINVAL;
 
 		switch (cmsg->cmsg_type)
 		{
diff -u --recursive --new-file v2.1.119/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v2.1.119/linux/net/core/skbuff.c	Mon Aug  3 12:45:48 1998
+++ linux/net/core/skbuff.c	Thu Aug 27 19:33:08 1998
@@ -4,6 +4,8 @@
  *	Authors:	Alan Cox <iiitac@pyr.swan.ac.uk>
  *			Florian La Roche <rzsfl@rz.uni-sb.de>
  *
+ *	Version:	$Id: skbuff.c,v 1.53 1998/08/19 13:32:44 freitag Exp $
+ *
  *	Fixes:	
  *		Alan Cox	:	Fixed the worst of the load balancer bugs.
  *		Dave Platt	:	Interrupt stacking fix.
@@ -96,14 +98,14 @@
 
 void show_net_buffers(void)
 {
-	printk(KERN_INFO "Networking buffers in use          : %u\n",
+	printk("Networking buffers in use          : %u\n",
 	       atomic_read(&net_skbcount));
-	printk(KERN_INFO "Total network buffer allocations   : %u\n",
+	printk("Total network buffer allocations   : %u\n",
 	       atomic_read(&net_allocs));
-	printk(KERN_INFO "Total failed network buffer allocs : %u\n",
+	printk("Total failed network buffer allocs : %u\n",
 	       atomic_read(&net_fails));
 #ifdef CONFIG_INET
-	printk(KERN_INFO "IP fragment buffer size            : %u\n",
+	printk("IP fragment buffer size            : %u\n",
 	       atomic_read(&ip_frag_mem));
 #endif	
 }
@@ -365,7 +367,7 @@
 }
 #endif
 
-__initfunc(void skb_init(void))
+void __init skb_init(void)
 {
 	skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
 					      sizeof(struct sk_buff),
diff -u --recursive --new-file v2.1.119/linux/net/core/sock.c linux/net/core/sock.c
--- v2.1.119/linux/net/core/sock.c	Tue Aug 18 22:02:08 1998
+++ linux/net/core/sock.c	Thu Aug 27 19:33:08 1998
@@ -7,7 +7,7 @@
  *		handler for protocols to use and generic option handler.
  *
  *
- * Version:	@(#)sock.c	1.0.17	06/02/93
+ * Version:	$Id: sock.c,v 1.70 1998/08/26 12:03:07 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -78,6 +78,7 @@
  *              Chris Evans     :       Call suser() check last on F_SETOWN
  *		Jay Schulist	:	Added SO_ATTACH_FILTER and SO_DETACH_FILTER.
  *		Andi Kleen	:	Add sock_kmalloc()/sock_kfree_s()
+ *		Andi Kleen	:	Fix write_space callback
  *
  * To Fix:
  *
@@ -445,6 +446,7 @@
 		case SO_RCVLOWAT:
 		case SO_SNDLOWAT:
 			v.val=1;
+			break; 
 
 		case SO_PASSCRED:
 			v.val = sock->passcred;
@@ -615,19 +617,6 @@
 }
 
 
-/* FIXME: this is also insane. See above comment */
-unsigned long sock_wspace(struct sock *sk)
-{
-	int amt = 0;
-
-	if (sk != NULL && !(sk->shutdown & SEND_SHUTDOWN)) {
-		amt = sk->sndbuf - atomic_read(&sk->wmem_alloc);
-		if (amt < 0) 
-			amt = 0;
-	}
-	return amt;
-}
-
 /* It is almost wait_for_tcp_memory minus release_sock/lock_sock.
    I think, these locks should be removed for datagram sockets.
  */
@@ -746,17 +735,15 @@
  
 void sklist_remove_socket(struct sock **list, struct sock *sk)
 {
-	unsigned long flags;
 	struct sock *s;
 
-	save_flags(flags);
-	cli();
+	start_bh_atomic();
 
 	s= *list;
 	if(s==sk)
 	{
 		*list = s->next;
-		restore_flags(flags);
+		end_bh_atomic();
 		return;
 	}
 	while(s && s->next)
@@ -764,22 +751,19 @@
 		if(s->next==sk)
 		{
 			s->next=sk->next;
-			restore_flags(flags);
-			return;
+			break;
 		}
 		s=s->next;
 	}
-	restore_flags(flags);
+	end_bh_atomic();
 }
 
 void sklist_insert_socket(struct sock **list, struct sock *sk)
 {
-	unsigned long flags;
-	save_flags(flags);
-	cli();
+	start_bh_atomic();
 	sk->next= *list;
 	*list=sk;
-	restore_flags(flags);
+	end_bh_atomic();
 }
 
 /*
@@ -914,6 +898,10 @@
 	return -EOPNOTSUPP;
 }
 
+/* 
+ * Note: if you add something that sleeps here then change sock_fcntl()
+ *       to do proper fd locking.
+ */
 int sock_no_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
 	struct sock *sk = sock->sk;
@@ -971,12 +959,15 @@
 	}
 }
 
-void sock_def_callback3(struct sock *sk)
+void sock_def_write_space(struct sock *sk)
 {
 	if(!sk->dead)
 	{
 		wake_up_interruptible(sk->sleep);
-		sock_wake_async(sk->socket, 2);
+
+		/* Should agree with poll, otherwise some programs break */ 
+		if (sock_writeable(sk))
+			sock_wake_async(sk->socket, 2);
 	}
 }
 
@@ -1011,7 +1002,7 @@
 
 	sk->state_change	=	sock_def_callback1;
 	sk->data_ready		=	sock_def_callback2;
-	sk->write_space		=	sock_def_callback3;
+	sk->write_space		=	sock_def_write_space;
 	sk->error_report	=	sock_def_callback1;
 	sk->destruct            =       sock_def_destruct;
 
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.1.119/linux/net/ipv4/af_inet.c	Thu May 14 19:47:44 1998
+++ linux/net/ipv4/af_inet.c	Thu Aug 27 19:33:08 1998
@@ -5,7 +5,7 @@
  *
  *		PF_INET protocol family socket handler.
  *
- * Version:	$Id: af_inet.c,v 1.74 1998/05/08 21:06:24 davem Exp $
+ * Version:	$Id: af_inet.c,v 1.75 1998/08/26 12:03:15 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -177,6 +177,8 @@
 	if(sk->opt)
 		kfree(sk->opt);
 	dst_release(sk->dst_cache);
+	if (atomic_read(&sk->omem_alloc))
+		printk(KERN_DEBUG "kill_sk_now: optmem leakage (%d bytes) detected.\n", atomic_read(&sk->omem_alloc));
 	sk_free(sk);
 }
 
@@ -576,6 +578,24 @@
 	return(0);
 }
 
+static void inet_wait_for_connect(struct sock *sk)
+{
+	struct wait_queue wait = { current, NULL };
+
+	add_wait_queue(sk->sleep, &wait);
+	current->state = TASK_INTERRUPTIBLE;
+	while (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
+		if (signal_pending(current))
+			break;
+		if (sk->err)
+			break;
+		schedule();
+		current->state = TASK_INTERRUPTIBLE;
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(sk->sleep, &wait);
+}
+
 /*
  *	Connect to a remote host. There is regrettably still a little
  *	TCP 'magic' in here.
@@ -623,6 +643,13 @@
 	if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) 
 	  	return (-EINPROGRESS);
 
+#if 1
+	if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
+		inet_wait_for_connect(sk);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+	}
+#else
 	cli();
 	while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
 		interruptible_sleep_on(sk->sleep);
@@ -639,6 +666,7 @@
 		}
 	}
 	sti();
+#endif
 
 	sock->state = SS_CONNECTED;
 	if ((sk->state != TCP_ESTABLISHED) && sk->err) {
@@ -876,7 +904,6 @@
 		case FIOGETOWN:
 		case SIOCGPGRP:
 			return put_user(sk->proc, (int *)arg);
-			return(0);			
 		case SIOCGSTAMP:
 			if(sk->stamp.tv_sec==0)
 				return -ENOENT;
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.1.119/linux/net/ipv4/arp.c	Tue Aug 18 22:02:08 1998
+++ linux/net/ipv4/arp.c	Thu Aug 27 19:33:08 1998
@@ -1,6 +1,6 @@
 /* linux/net/inet/arp.c
  *
- * Version:	$Id: arp.c,v 1.67 1998/06/19 13:22:31 davem Exp $
+ * Version:	$Id: arp.c,v 1.70 1998/08/26 12:03:18 davem Exp $
  *
  * Copyright (C) 1994 by Florian  La Roche
  *
@@ -760,7 +760,7 @@
 		r->arp_flags |= ATF_COM;
 	if (dev == NULL) {
 		struct rtable * rt;
-		if ((err = ip_route_output(&rt, ip, 0, 1, 0)) != 0)
+		if ((err = ip_route_output(&rt, ip, 0, RTO_ONLINK, 0)) != 0)
 			return err;
 		dev = rt->u.dst.dev;
 		ip_rt_put(rt);
@@ -843,11 +843,21 @@
 		return -EINVAL;
 	}
 
+	if (dev == NULL) {
+		struct rtable * rt;
+		if ((err = ip_route_output(&rt, ip, 0, RTO_ONLINK, 0)) != 0)
+			return err;
+		dev = rt->u.dst.dev;
+		ip_rt_put(rt);
+		if (!dev)
+			return -EINVAL;
+	}
 	err = -ENXIO;
 	start_bh_atomic();
 	neigh = __neigh_lookup(&arp_tbl, &ip, dev, 0);
 	if (neigh) {
-		err = neigh_update(neigh, NULL, NUD_FAILED, 1, 0);
+		if (neigh->nud_state&~NUD_NOARP)
+			err = neigh_update(neigh, NULL, NUD_FAILED, 1, 0);
 		neigh_release(neigh);
 	}
 	end_bh_atomic();
@@ -867,7 +877,7 @@
 	switch(cmd) {
 		case SIOCDARP:
 		case SIOCSARP:
-			if (!suser())
+			if (!capable(CAP_NET_ADMIN))
 				return -EPERM;
 		case SIOCGARP:
 			err = copy_from_user(&r, arg, sizeof(struct arpreq));
@@ -899,10 +909,8 @@
 		err = -EINVAL;
 		if ((r.arp_flags & ATF_COM) && r.arp_ha.sa_family != dev->type)
 			goto out;
-	} else if (cmd != SIOCSARP) {
-		/* dev has not been set ... */
-		printk(KERN_ERR "arp_ioctl: invalid, null device\n");
-		err = -EINVAL;
+	} else if (cmd == SIOCGARP) {
+		err = -ENODEV;
 		goto out;
 	}
 
@@ -911,7 +919,6 @@
 	        err = arp_req_delete(&r, dev);
 		break;
 	case SIOCSARP:
-		/* This checks for dev == NULL */
 		err = arp_req_set(&r, dev);
 		break;
 	case SIOCGARP:
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.1.119/linux/net/ipv4/devinet.c	Thu May 14 19:47:44 1998
+++ linux/net/ipv4/devinet.c	Thu Aug 27 19:33:08 1998
@@ -1,7 +1,7 @@
 /*
  *	NET3	IP device support routines.
  *
- *	Version: $Id: devinet.c,v 1.22 1998/05/08 21:06:26 davem Exp $
+ *	Version: $Id: devinet.c,v 1.23 1998/08/26 12:03: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
@@ -533,8 +533,6 @@
 				inet_del_ifa(in_dev, ifap, 0);
 				ifa->ifa_broadcast = 0;
 				ifa->ifa_anycast = 0;
-				ifa->ifa_prefixlen = 32;
-				ifa->ifa_mask = inet_make_mask(32);
 			}
 
 			ifa->ifa_address =
@@ -545,6 +543,9 @@
 				ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
 				if ((dev->flags&IFF_BROADCAST) && ifa->ifa_prefixlen < 31)
 					ifa->ifa_broadcast = ifa->ifa_address|~ifa->ifa_mask;
+			} else {
+				ifa->ifa_prefixlen = 32;
+				ifa->ifa_mask = inet_make_mask(32);
 			}
 			ret = inet_set_ifa(dev, ifa);
 			break;
@@ -702,6 +703,16 @@
 	case NETDEV_UNREGISTER:
 		inetdev_destroy(in_dev);
 		break;
+	case NETDEV_CHANGENAME:
+		if (in_dev->ifa_list) {
+			struct in_ifaddr *ifa;
+			for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next)
+				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
+			/* Do not notify about label change, this event is
+			   not interesting to applications using netlink.
+			 */
+		}
+		break;
 	}
 
 	return NOTIFY_DONE;
@@ -716,7 +727,7 @@
 #ifdef CONFIG_RTNETLINK
 
 static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
-			    pid_t pid, u32 seq, int event)
+			    u32 pid, u32 seq, int event)
 {
 	struct ifaddrmsg *ifm;
 	struct nlmsghdr  *nlh;
@@ -729,7 +740,7 @@
 	ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT;
 	ifm->ifa_scope = ifa->ifa_scope;
 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
-	if (ifa->ifa_prefixlen)
+	if (ifa->ifa_address)
 		RTA_PUT(skb, IFA_ADDRESS, 4, &ifa->ifa_address);
 	if (ifa->ifa_local)
 		RTA_PUT(skb, IFA_LOCAL, 4, &ifa->ifa_local);
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/fib_frontend.c linux/net/ipv4/fib_frontend.c
--- v2.1.119/linux/net/ipv4/fib_frontend.c	Tue Jun 23 10:01:30 1998
+++ linux/net/ipv4/fib_frontend.c	Thu Aug 27 19:33:08 1998
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: FIB frontend.
  *
- * Version:	$Id: fib_frontend.c,v 1.11 1998/06/11 03:15:40 davem Exp $
+ * Version:	$Id: fib_frontend.c,v 1.12 1998/08/26 12:03:24 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -300,10 +300,8 @@
 		if (attr) {
 			if (RTA_PAYLOAD(attr) < 4)
 				return -EINVAL;
-#ifndef	CONFIG_RTNL_OLD_IFINFO
 			if (i != RTA_MULTIPATH && i != RTA_METRICS)
-#endif
-			rta[i-1] = (struct rtattr*)RTA_DATA(attr);
+				rta[i-1] = (struct rtattr*)RTA_DATA(attr);
 		}
 	}
 	return 0;
@@ -527,6 +525,14 @@
 #undef BRD1_OK
 }
 
+static void fib_disable_ip(struct device *dev, int force)
+{
+	if (fib_sync_down(0, dev, force))
+		fib_flush();
+	rt_cache_flush(0);
+	arp_ifdown(dev);
+}
+
 static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
 	struct in_ifaddr *ifa = (struct in_ifaddr*)ptr;
@@ -537,8 +543,15 @@
 		rt_cache_flush(-1);
 		break;
 	case NETDEV_DOWN:
-		fib_del_ifaddr(ifa);
-		rt_cache_flush(-1);
+		if (ifa->ifa_dev && ifa->ifa_dev->ifa_list == NULL) {
+			/* Last address was deleted from this interface.
+			   Disable IP.
+			 */
+			fib_disable_ip(ifa->ifa_dev->dev, 1);
+		} else {
+			fib_del_ifaddr(ifa);
+			rt_cache_flush(-1);
+		}
 		break;
 	}
 	return NOTIFY_DONE;
@@ -563,18 +576,10 @@
 		rt_cache_flush(-1);
 		break;
 	case NETDEV_DOWN:
-		if (fib_sync_down(0, dev, 0))
-			fib_flush();
-		rt_cache_flush(0);
-		arp_ifdown(dev);
+		fib_disable_ip(dev, 0);
 		break;
 	case NETDEV_UNREGISTER:
-		if (in_dev->ifa_list)
-			printk("About to crash!\n");
-		if (fib_sync_down(0, dev, 1))
-			fib_flush();
-		rt_cache_flush(0);
-		arp_ifdown(dev);
+		fib_disable_ip(dev, 1);
 		break;
 	case NETDEV_CHANGEMTU:
 	case NETDEV_CHANGE:
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/fib_hash.c linux/net/ipv4/fib_hash.c
--- v2.1.119/linux/net/ipv4/fib_hash.c	Tue Jul 28 14:21:10 1998
+++ linux/net/ipv4/fib_hash.c	Thu Aug 27 19:33:08 1998
@@ -5,7 +5,7 @@
  *
  *		IPv4 FIB: lookup engine and maintenance routines.
  *
- * Version:	$Id: fib_hash.c,v 1.4 1998/07/15 05:05:08 davem Exp $
+ * Version:	$Id: fib_hash.c,v 1.5 1998/08/26 12:03:27 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -713,7 +713,7 @@
 		      struct nlmsghdr *n, struct netlink_skb_parms *req)
 {
 	struct sk_buff *skb;
-	pid_t pid = req ? req->pid : 0;
+	u32 pid = req ? req->pid : 0;
 	int size = NLMSG_SPACE(sizeof(struct rtmsg)+256);
 
 	skb = alloc_skb(size, GFP_KERNEL);
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/fib_rules.c linux/net/ipv4/fib_rules.c
--- v2.1.119/linux/net/ipv4/fib_rules.c	Sat May  2 14:19:55 1998
+++ linux/net/ipv4/fib_rules.c	Thu Aug 27 19:33:08 1998
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: policy rules.
  *
- * Version:	$Id: fib_rules.c,v 1.5 1998/04/28 06:21:57 davem Exp $
+ * Version:	$Id: fib_rules.c,v 1.6 1998/08/26 12:03:30 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -45,10 +45,6 @@
 
 #define FRprintk(a...)
 
-#ifndef CONFIG_RTNL_OLD_IFINFO
-#define RTA_IFNAME RTA_IIF
-#endif
-
 struct fib_rule
 {
 	struct fib_rule *r_next;
@@ -91,7 +87,7 @@
 		    rtm->rtm_tos == r->r_tos &&
 		    (!rtm->rtm_type || rtm->rtm_type == r->r_action) &&
 		    (!rta[RTA_PRIORITY-1] || memcmp(RTA_DATA(rta[RTA_PRIORITY-1]), &r->r_preference, 4) == 0) &&
-		    (!rta[RTA_IFNAME-1] || strcmp(RTA_DATA(rta[RTA_IFNAME-1]), r->r_ifname) == 0) &&
+		    (!rta[RTA_IIF-1] || strcmp(RTA_DATA(rta[RTA_IIF-1]), r->r_ifname) == 0) &&
 		    (!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) {
 			*rp = r->r_next;
 			if (r != &default_rule && r != &main_rule && r != &local_rule)
@@ -126,7 +122,7 @@
 	    (rtm->rtm_tos & ~IPTOS_TOS_MASK))
 		return -EINVAL;
 
-	if (rta[RTA_IFNAME-1] && RTA_PAYLOAD(rta[RTA_IFNAME-1]) > IFNAMSIZ)
+	if (rta[RTA_IIF-1] && RTA_PAYLOAD(rta[RTA_IIF-1]) > IFNAMSIZ)
 		return -EINVAL;
 
 	table_id = rtm->rtm_table;
@@ -159,9 +155,9 @@
 	if (rta[RTA_PRIORITY-1])
 		memcpy(&new_r->r_preference, RTA_DATA(rta[RTA_PRIORITY-1]), 4);
 	new_r->r_table = table_id;
-	if (rta[RTA_IFNAME-1]) {
+	if (rta[RTA_IIF-1]) {
 		struct device *dev;
-		memcpy(new_r->r_ifname, RTA_DATA(rta[RTA_IFNAME-1]), IFNAMSIZ);
+		memcpy(new_r->r_ifname, RTA_DATA(rta[RTA_IIF-1]), IFNAMSIZ);
 		new_r->r_ifname[IFNAMSIZ-1] = 0;
 		new_r->r_ifindex = -1;
 		dev = dev_get(new_r->r_ifname);
@@ -339,10 +335,6 @@
 	rtm->rtm_table = r->r_table;
 	rtm->rtm_protocol = 0;
 	rtm->rtm_scope = 0;
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	rtm->rtm_nhs = 0;
-	rtm->rtm_optlen = 0;
-#endif
 	rtm->rtm_type = r->r_action;
 	rtm->rtm_flags = r->r_flags;
 
@@ -351,7 +343,7 @@
 	if (r->r_src_len)
 		RTA_PUT(skb, RTA_SRC, 4, &r->r_src);
 	if (r->r_ifname[0])
-		RTA_PUT(skb, RTA_IFNAME, IFNAMSIZ, &r->r_ifname);
+		RTA_PUT(skb, RTA_IIF, IFNAMSIZ, &r->r_ifname);
 	if (r->r_preference)
 		RTA_PUT(skb, RTA_PRIORITY, 4, &r->r_preference);
 	if (r->r_srcmap)
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/fib_semantics.c linux/net/ipv4/fib_semantics.c
--- v2.1.119/linux/net/ipv4/fib_semantics.c	Tue Jun 23 10:01:30 1998
+++ linux/net/ipv4/fib_semantics.c	Thu Aug 27 19:33:08 1998
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: semantics.
  *
- * Version:	$Id: fib_semantics.c,v 1.9 1998/06/11 03:15:41 davem Exp $
+ * Version:	$Id: fib_semantics.c,v 1.10 1998/08/26 12:03:32 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -181,7 +181,6 @@
 	return 0;
 }
 
-#ifndef CONFIG_RTNL_OLD_IFINFO
 static int
 fib_count_nexthops(struct rtattr *rta)
 {
@@ -189,7 +188,7 @@
 	struct rtnexthop *nhp = RTA_DATA(rta);
 	int nhlen = RTA_PAYLOAD(rta);
 
-	while (nhlen >= sizeof(struct rtnexthop)) {
+	while (nhlen >= (int)sizeof(struct rtnexthop)) {
 		if ((nhlen -= nhp->rtnh_len) < 0)
 			return 0;
 		nhs++;
@@ -197,21 +196,12 @@
 	};
 	return nhs;
 }
-#endif
 
-#ifdef CONFIG_RTNL_OLD_IFINFO
-static int
-fib_get_nhs(struct fib_info *fi, const struct nlmsghdr *nlh, const struct rtmsg *r)
-{
-	struct rtnexthop *nhp = RTM_RTNH(r);
-	int nhlen = RTM_NHLEN(nlh, r);
-#else
 static int
 fib_get_nhs(struct fib_info *fi, const struct rtattr *rta, const struct rtmsg *r)
 {
 	struct rtnexthop *nhp = RTA_DATA(rta);
 	int nhlen = RTA_PAYLOAD(rta);
-#endif
 
 	change_nexthops(fi) {
 		int attrlen = nhlen - sizeof(struct rtnexthop);
@@ -249,18 +239,10 @@
 	}
 
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	if (r->rtm_nhs == 0)
-		return 0;
-
-	nhp = RTM_RTNH(r);
-	nhlen = RTM_NHLEN(nlh, r);
-#else
 	if (rta->rta_mp == NULL)
 		return 0;
 	nhp = RTA_DATA(rta->rta_mp);
 	nhlen = RTA_PAYLOAD(rta->rta_mp);
-#endif
 	
 	for_nexthops(fi) {
 		int attrlen = nhlen - sizeof(struct rtnexthop);
@@ -397,11 +379,7 @@
 	struct fib_info *fi = NULL;
 	struct fib_info *ofi;
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	int nhs = r->rtm_nhs ? : 1;
-#else
 	int nhs = 1;
-#endif
 #else
 	const int nhs = 1;
 #endif
@@ -411,14 +389,12 @@
 		goto err_inval;
 
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
-#ifndef CONFIG_RTNL_OLD_IFINFO
 	if (rta->rta_mp) {
 		nhs = fib_count_nexthops(rta->rta_mp);
 		if (nhs == 0)
 			goto err_inval;
 	}
 #endif
-#endif
 
 	fi = kmalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
 	err = -ENOBUFS;
@@ -429,14 +405,6 @@
 	fi->fib_protocol = r->rtm_protocol;
 	fi->fib_nhs = nhs;
 	fi->fib_flags = r->rtm_flags;
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	if (rta->rta_mtu)
-		fi->fib_mtu = *rta->rta_mtu;
-	if (rta->rta_rtt)
-		fi->fib_rtt = *rta->rta_rtt;
-	if (rta->rta_window)
-		fi->fib_window = *rta->rta_window;
-#else
 	if (rta->rta_mx) {
 		int attrlen = RTA_PAYLOAD(rta->rta_mx);
 		struct rtattr *attr = RTA_DATA(rta->rta_mx);
@@ -451,21 +419,12 @@
 			attr = RTA_NEXT(attr, attrlen);
 		}
 	}
-#endif
 	if (rta->rta_prefsrc)
 		memcpy(&fi->fib_prefsrc, rta->rta_prefsrc, 4);
 
-#ifndef CONFIG_RTNL_OLD_IFINFO
 	if (rta->rta_mp) {
-#else
-	if (r->rtm_nhs) {
-#endif
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
-#ifdef CONFIG_RTNL_OLD_IFINFO
-		if ((err = fib_get_nhs(fi, nlh, r)) != 0)
-#else
 		if ((err = fib_get_nhs(fi, rta->rta_mp, r)) != 0)
-#endif
 			goto failure;
 		if (rta->rta_oif && fi->fib_nh->nh_oif != *rta->rta_oif)
 			goto err_inval;
@@ -504,11 +463,7 @@
 #endif
 
 	if (fib_props[r->rtm_type].error) {
-#ifndef CONFIG_RTNL_OLD_IFINFO
 		if (rta->rta_gw || rta->rta_oif || rta->rta_mp)
-#else
-		if (rta->rta_gw || rta->rta_oif || r->rtm_nhs)
-#endif
 			goto err_inval;
 		goto link_it;
 	}
@@ -637,16 +592,13 @@
 #ifdef CONFIG_RTNETLINK
 
 int
-fib_dump_info(struct sk_buff *skb, pid_t pid, u32 seq, int event,
+fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
 	      u8 tb_id, u8 type, u8 scope, void *dst, int dst_len, u8 tos,
 	      struct fib_info *fi)
 {
 	struct rtmsg *rtm;
 	struct nlmsghdr  *nlh;
 	unsigned char	 *b = skb->tail;
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	unsigned char 	 *o;
-#endif
 
 	nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*rtm));
 	rtm = NLMSG_DATA(nlh);
@@ -658,22 +610,9 @@
 	rtm->rtm_type = type;
 	rtm->rtm_flags = fi->fib_flags;
 	rtm->rtm_scope = scope;
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	rtm->rtm_nhs = 0;
-
-	o = skb->tail;
-#endif
 	if (rtm->rtm_dst_len)
 		RTA_PUT(skb, RTA_DST, 4, dst);
 	rtm->rtm_protocol = fi->fib_protocol;
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	if (fi->fib_mtu)
-		RTA_PUT(skb, RTA_MTU, sizeof(unsigned), &fi->fib_mtu);
-	if (fi->fib_window)
-		RTA_PUT(skb, RTA_WINDOW, sizeof(unsigned), &fi->fib_window);
-	if (fi->fib_rtt)
-		RTA_PUT(skb, RTA_RTT, sizeof(unsigned), &fi->fib_rtt);
-#else
 #ifdef CONFIG_NET_CLS_ROUTE
 	if (fi->fib_nh[0].nh_tclassid)
 		RTA_PUT(skb, RTA_FLOW, 4, &fi->fib_nh[0].nh_tclassid);
@@ -688,7 +627,6 @@
 		}
 		mx->rta_len = skb->tail - (u8*)mx;
 	}
-#endif
 	if (fi->fib_prefsrc)
 		RTA_PUT(skb, RTA_PREFSRC, 4, &fi->fib_prefsrc);
 	if (fi->fib_nhs == 1) {
@@ -697,18 +635,14 @@
 		if (fi->fib_nh->nh_oif)
 			RTA_PUT(skb, RTA_OIF, sizeof(int), &fi->fib_nh->nh_oif);
 	}
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	rtm->rtm_optlen = skb->tail - o;
-#endif
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
 	if (fi->fib_nhs > 1) {
 		struct rtnexthop *nhp;
-#ifndef CONFIG_RTNL_OLD_IFINFO
 		struct rtattr *mp_head;
 		if (skb_tailroom(skb) <= RTA_SPACE(0))
 			goto rtattr_failure;
 		mp_head = (struct rtattr*)skb_put(skb, RTA_SPACE(0));
-#endif
+
 		for_nexthops(fi) {
 			if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
 				goto rtattr_failure;
@@ -719,14 +653,9 @@
 			if (nh->nh_gw)
 				RTA_PUT(skb, RTA_GATEWAY, 4, &nh->nh_gw);
 			nhp->rtnh_len = skb->tail - (unsigned char*)nhp;
-#ifdef CONFIG_RTNL_OLD_IFINFO
-			rtm->rtm_nhs++;
-#endif
 		} endfor_nexthops(fi);
-#ifndef CONFIG_RTNL_OLD_IFINFO
 		mp_head->rta_type = RTA_MULTIPATH;
 		mp_head->rta_len = skb->tail - (u8*)mp_head;
-#endif
 	}
 #endif
 	nlh->nlmsg_len = skb->tail - b;
@@ -848,24 +777,6 @@
 	if (r->rt_flags&RTF_GATEWAY && rta->rta_gw == NULL)
 		return -EINVAL;
 
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	/* Ugly conversion from rtentry types to unsigned */
-
-	if (r->rt_flags&RTF_IRTT) {
-		rta->rta_rtt = (unsigned*)&r->rt_pad3;
-		*rta->rta_rtt = r->rt_irtt;
-	}
-	if (r->rt_flags&RTF_WINDOW) {
-		rta->rta_window = (unsigned*)&r->rt_window;
-		if (sizeof(*rta->rta_window) != sizeof(r->rt_window))
-			*rta->rta_window = r->rt_window;
-	}
-	if (r->rt_flags&RTF_MTU) {
-		rta->rta_mtu = (unsigned*)&r->rt_mtu;
-		if (sizeof(*rta->rta_mtu) != sizeof(r->rt_mtu))
-			*rta->rta_mtu = r->rt_mtu;
-	}
-#else
 	if (r->rt_flags&(RTF_MTU|RTF_WINDOW|RTF_IRTT)) {
 		struct rtattr *rec;
 		struct rtattr *mx = kmalloc(RTA_LENGTH(3*RTA_LENGTH(4)), GFP_KERNEL);
@@ -896,7 +807,6 @@
 			*(u32*)RTA_DATA(rec) = r->rt_irtt;
 		}
 	}
-#endif
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v2.1.119/linux/net/ipv4/icmp.c	Tue Jul 28 14:21:10 1998
+++ linux/net/ipv4/icmp.c	Thu Aug 27 19:33:08 1998
@@ -3,7 +3,7 @@
  *	
  *		Alan Cox, <alan@cymru.net>
  *
- *	Version: $Id: icmp.c,v 1.44 1998/06/16 04:38:27 davem Exp $
+ *	Version: $Id: icmp.c,v 1.45 1998/08/26 12:03:35 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -47,6 +47,9 @@
  *					into the dest entry and use a token
  *					bucket filter (thanks to ANK). Make
  *					the rates sysctl configurable.
+ *		Yu Tianli	:	Fixed two ugly bugs in icmp_send
+ *					- IP option length was accounted wrongly
+ *					- ICMP header length was not accounted at all.
  *
  * RFC1122 (Host Requirements -- Comm. Layer) Status:
  * (boy, are there a lot of rules for ICMP)
@@ -363,7 +366,7 @@
 
 	now = jiffies;
 	dst->rate_tokens += now - dst->rate_last;
-	if (dst->rate_tokens > 6*timeout)
+	if (dst->rate_tokens > XRLIM_BURST_FACTOR*timeout)
 		dst->rate_tokens = XRLIM_BURST_FACTOR*timeout;
 	if (dst->rate_tokens >= timeout) {
 		dst->rate_tokens -= timeout;
@@ -537,7 +540,17 @@
 	/*
 	 *	Construct source address and options.
 	 */
-	
+
+#ifdef CONFIG_IP_ROUTE_NAT	
+	/*
+	 *	Restore original addresses if packet has been translated.
+	 */
+	if (rt->rt_flags&RTCF_NAT && IPCB(skb_in)->flags&IPSKB_TRANSLATED) {
+		iph->daddr = rt->key.dst;
+		iph->saddr = rt->key.src;
+	}
+#endif
+
 	saddr = iph->daddr;
 	if (!(rt->rt_flags & RTCF_LOCAL))
 		saddr = 0;
@@ -587,8 +600,9 @@
 	room = rt->u.dst.pmtu;
 	if (room > 576)
 		room = 576;
-	room -= sizeof(struct iphdr) - icmp_param.replyopts.optlen;
-	
+	room -= sizeof(struct iphdr) + icmp_param.replyopts.optlen;
+	room -= sizeof(struct icmphdr);
+
 	icmp_param.data_len=(iph->ihl<<2)+skb_in->len;
 	if (icmp_param.data_len > room)
 		icmp_param.data_len = room;
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v2.1.119/linux/net/ipv4/igmp.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/igmp.c	Thu Aug 27 19:33:08 1998
@@ -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.26 1998/03/08 05:56:19 davem Exp $
+ *	Version: $Id: igmp.c,v 1.27 1998/08/26 12:03:39 davem Exp $
  *
  *	Authors:
  *		Alan Cox <Alan.Cox@linux.org>
@@ -563,7 +563,7 @@
 		goto done;
 	}
 
-	iml = (struct ip_mc_socklist *)kmalloc(sizeof(*iml), GFP_KERNEL);
+	iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
 
 	err = -EADDRINUSE;
 	for (i=sk->ip_mc_list; i; i=i->next) {
@@ -590,7 +590,7 @@
 done:
 	rtnl_shunlock();
 	if (iml)
-		kfree(iml);
+		sock_kfree_s(sk, iml, sizeof(*iml));
 	return err;
 }
 
@@ -613,7 +613,7 @@
 			in_dev = inetdev_by_index(iml->multi.imr_ifindex);
 			if (in_dev)
 				ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr);
-			kfree_s(iml, sizeof(*iml));
+			sock_kfree_s(sk, iml, sizeof(*iml));
 			return 0;
 		}
 	}
@@ -633,7 +633,7 @@
 		sk->ip_mc_list = iml->next;
 		if ((in_dev = inetdev_by_index(iml->multi.imr_ifindex)) != NULL)
 			ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
-		kfree_s(iml, sizeof(*iml));
+		sock_kfree_s(sk, iml, sizeof(*iml));
 	}
 }
 
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v2.1.119/linux/net/ipv4/ip_forward.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/ip_forward.c	Thu Aug 27 19:33:08 1998
@@ -5,7 +5,7 @@
  *
  *		The IP forwarding functionality.
  *		
- * Version:	$Id: ip_forward.c,v 1.40 1998/03/08 05:56:20 davem Exp $
+ * Version:	$Id: ip_forward.c,v 1.41 1998/08/26 12:03:42 davem Exp $
  *
  * Authors:	see ip.c
  *
@@ -79,10 +79,8 @@
 	int fw_res = 0;
 #endif
 
-	if (IPCB(skb)->opt.router_alert) {
-		if (ip_call_ra_chain(skb))
-			return 0;
-	}
+	if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
+		return 0;
 
 	if (skb->pkt_type != PACKET_HOST)
 		goto drop;
@@ -110,7 +108,7 @@
                 goto local_pkt;
 #endif
 
-	if (ip_decrease_ttl(iph) <= 0)
+	if (iph->ttl <= 1)
                 goto too_many_hops;
 
 	if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
@@ -121,22 +119,30 @@
 	 *	after asking the firewall permission to do so.
 	 */
 
-	skb->priority = rt->u.dst.priority;
+	skb->priority = rt_tos2priority(iph->tos);
 	dev2 = rt->u.dst.dev;
-	mtu = dev2->mtu;
+	mtu = rt->u.dst.pmtu;
 
 #ifdef CONFIG_NET_SECURITY
 	call_fw_firewall(PF_SECURITY, dev2, NULL, &mtu, NULL);
 #endif	
 	
 	/*
-	 *	In IP you never have to forward a frame on the interface that it 
-	 *	arrived upon. We now generate an ICMP HOST REDIRECT giving the route
+	 *	We now generate an ICMP HOST REDIRECT giving the route
 	 *	we calculated.
 	 */
 	if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr)
 		ip_rt_send_redirect(skb);
-	
+
+	/* We are about to mangle packet. Copy it! */
+	if ((skb = skb_cow(skb, dev2->hard_header_len)) == NULL)
+		return -1;
+	iph = skb->nh.iph;
+	opt = &(IPCB(skb)->opt);
+
+	/* Decrease ttl after skb cow done */
+	ip_decrease_ttl(iph);
+
 	/*
 	 * We now may allocate a new buffer, and copy the datagram into it.
 	 * If the indicated interface is up and running, kick it.
@@ -147,14 +153,6 @@
 
 #ifdef CONFIG_IP_ROUTE_NAT
 	if (rt->rt_flags & RTCF_NAT) {
-		if (skb_headroom(skb) < dev2->hard_header_len || skb_cloned(skb)) {
-			struct sk_buff *skb2;
-			skb2 = skb_realloc_headroom(skb, (dev2->hard_header_len + 15)&~15);
-			kfree_skb(skb);
-			if (skb2 == NULL)
-				return -1;
-			skb = skb2;
-		}
 		if (ip_do_nat(skb)) {
 			kfree_skb(skb);
 			return -1;
@@ -243,18 +241,6 @@
 	}
 #endif
 
-	if (skb_headroom(skb) < dev2->hard_header_len || skb_cloned(skb)) {
-		struct sk_buff *skb2;
-		skb2 = skb_realloc_headroom(skb, (dev2->hard_header_len + 15)&~15);
-		kfree_skb(skb);
-
-		if (skb2 == NULL) {
-			NETDEBUG(printk(KERN_ERR "\nIP: No memory available for IP forward\n"));
-			return -1;
-		}
-		skb = skb2;
-		iph = skb2->nh.iph;
-	}
 
 #ifdef CONFIG_FIREWALL
 	if ((fw_res = call_out_firewall(PF_INET, dev2, iph, NULL,&skb)) < FW_ACCEPT) {
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.1.119/linux/net/ipv4/ip_fragment.c	Tue Aug 18 22:02:08 1998
+++ linux/net/ipv4/ip_fragment.c	Thu Aug 27 19:33:08 1998
@@ -5,7 +5,7 @@
  *
  *		The IP fragmentation functionality.
  *		
- * Version:	$Id: ip_fragment.c,v 1.38 1998/06/16 04:38:29 davem Exp $
+ * Version:	$Id: ip_fragment.c,v 1.39 1998/08/26 10:35:26 davem Exp $
  *
  * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
  *		Alan Cox <Alan.Cox@linux.org>
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v2.1.119/linux/net/ipv4/ip_fw.c	Sun Jul 26 11:57:20 1998
+++ linux/net/ipv4/ip_fw.c	Tue Sep  1 10:54:37 1998
@@ -427,18 +427,15 @@
 	printk("\n");
 }
 
-/* function for checking chain labels for user space.  Makes sure that
- * there are no special characters in the string */
+/* function for checking chain labels for user space. */
 static int check_label(ip_chainlabel label)
 {
 	unsigned int i;
-	
-	for (i = 0; i < IP_FW_MAX_LABEL_LENGTH + 1 && label[i]; i++)
-		if (label[i] <= ' ') 
-			return 0;
-	if (i == IP_FW_MAX_LABEL_LENGTH+1)
-		return 0;
-	return 1;
+	/* strlen must be < IP_FW_MAX_LABEL_LENGTH. */
+	for (i = 0; i < IP_FW_MAX_LABEL_LENGTH + 1; i++)
+		if (label[i] == '\0') return 1;
+
+	return 0;
 }	
 
 /*	This function returns a pointer to the first chain with a label
@@ -1098,6 +1095,9 @@
 {
 	struct ip_chain *tmp;
 
+	if (!check_label(label))
+		return EINVAL;
+
 	FWC_HAVE_LOCK(fwc_wlocks);
 	for (tmp = ip_fw_chains; tmp->next; tmp = tmp->next)
 		if (strcmp(tmp->label,label) == 0)
@@ -1512,14 +1512,14 @@
 		    "%9s "			/* Chain name */
 		    "%08lX/%08lX->%08lX/%08lX "	/* Source & Destination IPs */
 		    "%.16s "			/* Interface */
-		    "%hX %hX "			/* fw_flg and fw_invflg fields */
-		    "%hu "			/* Protocol */
+		    "%X %X "			/* fw_flg and fw_invflg fields */
+		    "%u "			/* Protocol */
 		    "%-9u %-9u %-9u %-9u "	/* Packet & byte counters */
-		    "%hu-%hu %hu-%hu "		/* Source & Dest port ranges */
+		    "%u-%u %u-%u "		/* Source & Dest port ranges */
 		    "A%02X X%02X "		/* TOS and and xor masks */
 		    "%08X "			/* Redirection port */
 		    "%u "			/* fw_mark field */
-		    "%hu "			/* output size */
+		    "%u "			/* output size */
 		    "%9s\n",			/* Target */
 		    chainlabel,
 		    ntohl(rule->ipfw.fw_src.s_addr),
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/ip_gre.c linux/net/ipv4/ip_gre.c
--- v2.1.119/linux/net/ipv4/ip_gre.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/ip_gre.c	Thu Aug 27 19:33:08 1998
@@ -684,7 +684,7 @@
 	else if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
 		struct rt6_info *rt6 = (struct rt6_info*)skb->dst;
 
-		if (rt6 && mtu < rt6->u.dst.pmtu && mtu >= 576) {
+		if (rt6 && mtu < rt6->u.dst.pmtu && mtu >= IPV6_MIN_MTU) {
 			if ((tunnel->parms.iph.daddr && !MULTICAST(tunnel->parms.iph.daddr)) ||
 			    rt6->rt6i_dst.plen == 128) {
 				rt6->rt6i_flags |= RTF_MODIFIED;
@@ -692,7 +692,7 @@
 			}
 		}
 
-		if (mtu >= 576 && mtu < skb->len - tunnel->hlen + gre_hlen) {
+		if (mtu >= IPV6_MIN_MTU && mtu < skb->len - tunnel->hlen + gre_hlen) {
 			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
 			ip_rt_put(rt);
 			goto tx_error;
@@ -722,6 +722,8 @@
 			tunnel->recursion--;
 			return 0;
 		}
+		if (skb->sk)
+			skb_set_owner_w(new_skb, skb->sk);
 		dev_kfree_skb(skb);
 		skb = new_skb;
 	}
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v2.1.119/linux/net/ipv4/ip_input.c	Tue Jun 23 10:01:31 1998
+++ linux/net/ipv4/ip_input.c	Thu Aug 27 19:33:08 1998
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) module.
  *
- * Version:	$Id: ip_input.c,v 1.31 1998/05/17 02:19:15 freitag Exp $
+ * Version:	$Id: ip_input.c,v 1.33 1998/08/26 12:03:47 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -115,38 +115,31 @@
  *		2 of the License, or (at your option) any later version.
  */
 
-#include <asm/uaccess.h>
 #include <asm/system.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/config.h>
 
+#include <linux/net.h>
 #include <linux/socket.h>
 #include <linux/sockios.h>
 #include <linux/in.h>
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
 
 #include <net/snmp.h>
 #include <net/ip.h>
 #include <net/protocol.h>
 #include <net/route.h>
-#include <net/tcp.h>
-#include <net/udp.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
 #include <net/arp.h>
 #include <net/icmp.h>
 #include <net/raw.h>
 #include <net/checksum.h>
-#include <linux/igmp.h>
 #include <linux/ip_fw.h>
 #ifdef CONFIG_IP_MASQUERADE
 #include <net/ip_masq.h>
@@ -154,7 +147,6 @@
 #include <linux/firewall.h>
 #include <linux/mroute.h>
 #include <linux/netlink.h>
-#include <linux/ipsec.h>
 
 /*
  *	SNMP management statistics
@@ -199,6 +191,9 @@
 	return 0;
 }
 
+/*
+ *	Process Router Attention IP option
+ */ 
 int ip_call_ra_chain(struct sk_buff *skb)
 {
 	struct ip_ra_chain *ra;
@@ -229,6 +224,9 @@
 	return 0;
 }
 
+/*
+ * 	Deliver IP Packets to the higher protocol layers.
+ */ 
 int ip_local_deliver(struct sk_buff *skb)
 {
 	struct iphdr *iph = skb->nh.iph;
@@ -282,9 +280,11 @@
         skb->h.raw = skb->nh.raw + iph->ihl*4;
 
 	/*
-	 *	Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies.
+	 *	Deliver to raw sockets. This is fun as to avoid copies we want to make no 
+	 *	surplus copies.
 	 *
 	 *	RFC 1122: SHOULD pass TOS value up to the transport layer.
+	 *	-> It does. And not only TOS, but all IP header.
 	 */
  
 	/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
@@ -309,10 +309,7 @@
 					skb1 = skb_clone(skb, GFP_ATOMIC);
 					if(skb1)
 					{
-						if(ipsec_sk_policy(raw_sk,skb1))	
-							raw_rcv(raw_sk, skb1);
-						else
-							kfree_skb(skb1);
+						raw_rcv(raw_sk, skb1);
 					}
 				}
 				raw_sk = sknext;
@@ -372,10 +369,8 @@
 
 	if(raw_sk!=NULL)	/* Shift to last raw user */
 	{
-		if(ipsec_sk_policy(raw_sk, skb))
-			raw_rcv(raw_sk, skb);
-		else
-			kfree_skb(skb);
+		raw_rcv(raw_sk, skb);
+
 	}
 	else if (!flag)		/* Free and report errors */
 	{
@@ -386,15 +381,16 @@
 	return(0);
 }
 
+/*
+ * 	Main IP Receive routine.
+ */ 
 int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
 	struct iphdr *iph = skb->nh.iph;
-	struct ip_options * opt = NULL;
-	int err;
 
 	/*
-	 * When interface is in promisc. mode, drop all the crap
-	 * that it receives, do not truing to analyse it.
+	 * 	When the interface is in promisc. mode, drop all the crap
+	 * 	that it receives, do not try to analyse it.
 	 */
 	if (skb->pkt_type == PACKET_OTHERHOST)
 		goto drop;
@@ -412,24 +408,32 @@
 	 *	4.	Doesn't have a bogus length
 	 */
 
-	if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4
-#ifndef CONFIG_IP_ROUTER
-	    || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0
-#endif
-		|| skb->len < ntohs(iph->tot_len))
-		goto inhdr_error;
+	if (skb->len < sizeof(struct iphdr))
+		goto inhdr_error; 
+	if (iph->ihl < 5 || iph->version != 4 || ip_fast_csum((u8 *)iph, iph->ihl) != 0)
+		goto inhdr_error; 
+
+	{
+	__u32 len = ntohs(iph->tot_len); 
+	if (skb->len < len)
+		goto inhdr_error; 
 
 	/*
 	 *	Our transport medium may have padded the buffer out. Now we know it
 	 *	is IP we can trim to the true length of the frame.
 	 *	Note this now means skb->len holds ntohs(iph->tot_len).
 	 */
-	__skb_trim(skb, ntohs(iph->tot_len));
 
+	__skb_trim(skb, len);
+	}
+	
+	/*
+	 *	Initialise the virtual path cache for the packet. It describes
+	 *	how the packet travels inside Linux networking.
+	 */ 
 	if (skb->dst == NULL) {
-		err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev);
-		if (err)
-			goto drop;
+		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) {
@@ -449,6 +453,21 @@
 #endif
 
 	if (iph->ihl > 5) {
+		struct ip_options *opt;
+
+		/* It looks as overkill, because not all
+		   IP options require packet mangling.
+		   But it is the easiest for now, especially taking
+		   into account that combination of IP options
+		   and running sniffer is extremely rare condition.
+		                                      --ANK (980813)
+		*/
+		   
+		skb = skb_cow(skb, skb_headroom(skb));
+		if (skb == NULL)
+			return 0;
+		iph = skb->nh.iph;
+
 		skb->ip_summed = 0;
 		if (ip_options_compile(NULL, skb))
 			goto inhdr_error;
@@ -458,8 +477,8 @@
 			struct in_device *in_dev = dev->ip_ptr;
 			if (in_dev && !IN_DEV_SOURCE_ROUTE(in_dev)) {
 				if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
-					printk(KERN_INFO "source route option %08lx -> %08lx\n",
-					       ntohl(iph->saddr), ntohl(iph->daddr));
+					printk(KERN_INFO "source route option %d.%d.%d.%d -> %d.%d.%d.%d\n",
+					       NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
 				goto drop;
 			}
 			if (ip_options_rcv_srr(skb))
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/ip_nat_dumb.c linux/net/ipv4/ip_nat_dumb.c
--- v2.1.119/linux/net/ipv4/ip_nat_dumb.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/ip_nat_dumb.c	Mon Aug 31 10:32:19 1998
@@ -5,7 +5,7 @@
  *
  *		Dumb Network Address Translation.
  *
- * Version:	$Id: ip_nat_dumb.c,v 1.3 1998/03/15 03:31:44 davem Exp $
+ * Version:	$Id: ip_nat_dumb.c,v 1.4 1998/08/26 12:03:49 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -17,11 +17,12 @@
  * Fixes:
  *		Rani Assaf	:	A zero checksum is a special case
  *					only in UDP
+ * 		Rani Assaf	:	Added ICMP messages rewriting
+ *
  *
  * NOTE:	It is just working model of real NAT.
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/mm.h>
 #include <linux/sched.h>
@@ -36,9 +37,6 @@
 #include <linux/udp.h>
 #include <linux/firewall.h>
 #include <linux/ip_fw.h>
-#ifdef CONFIG_IP_MASQUERADE
-#include <net/ip_masq.h>
-#endif
 #include <net/checksum.h>
 #include <linux/route.h>
 #include <net/route.h>
@@ -68,20 +66,48 @@
 
 		switch(iph->protocol) {
 		case IPPROTO_TCP:
-			cksum  = (u16*)&((struct tcphdr*)(((char*)iph) + iph->ihl*4))->check;
+			cksum  = (u16*)&((struct tcphdr*)(((char*)iph) + (iph->ihl<<2)))->check;
+			if ((u8*)(cksum+1) > skb->tail)
+				goto truncated;
 			check  = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~(*cksum));
 			*cksum = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
 			break;
 		case IPPROTO_UDP:
-			cksum  = (u16*)&((struct udphdr*)(((char*)iph) + iph->ihl*4))->check;
+			cksum  = (u16*)&((struct udphdr*)(((char*)iph) + (iph->ihl<<2)))->check;
+			if ((u8*)(cksum+1) > skb->tail)
+				goto truncated;
 			if ((check = *cksum) != 0) {
 				check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~check);
 				check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
 				*cksum = check ? : 0xFFFF;
 			}
+			break;
+		case IPPROTO_ICMP:
+		{
+			struct icmphdr *icmph = (struct icmphdr*)((char*)iph + (iph->ihl<<2));
+			struct   iphdr *ciph;
+
+			if ((icmph->type != ICMP_DEST_UNREACH) &&
+			    (icmph->type != ICMP_TIME_EXCEEDED) &&
+			    (icmph->type != ICMP_PARAMETERPROB)) break;
+
+			ciph = (struct iphdr *) (icmph + 1);
+
+			if ((u8*)(ciph+1) > skb->tail)
+				goto truncated;
+
+			if (rt->rt_flags&RTCF_DNAT && ciph->saddr == odaddr)
+				ciph->saddr = iph->daddr;
+			if (rt->rt_flags&RTCF_SNAT && ciph->daddr == osaddr)
+				ciph->daddr = iph->saddr;
+			break;
+		}
 		default:
 			break;
 		}
 	}
 	return 0;
+
+truncated:
+	return -EINVAL;
 }
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/ip_options.c linux/net/ipv4/ip_options.c
--- v2.1.119/linux/net/ipv4/ip_options.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/ip_options.c	Thu Aug 27 19:33:08 1998
@@ -5,7 +5,7 @@
  *
  *		The options processing module for ip.c
  *
- * Version:	$Id: ip_options.c,v 1.13 1998/02/12 07:43:12 davem Exp $
+ * Version:	$Id: ip_options.c,v 1.14 1998/08/26 12:03:51 davem Exp $
  *
  * Authors:	A.N.Kuznetsov
  *		
@@ -451,7 +451,7 @@
 
 error:
 	if (skb) {
-		icmp_send(skb, ICMP_PARAMETERPROB, 0, pp_ptr-iph);
+		icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((pp_ptr-iph)<<24));
 		kfree_skb(skb);
 	}
 	return -EINVAL;
@@ -579,7 +579,7 @@
 	if (rt->rt_type == RTN_UNICAST) {
 		if (!opt->is_strictroute)
 			return 0;
-		icmp_send(skb, ICMP_PARAMETERPROB, 0, 16);
+		icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl(16<<24));
 		return -EINVAL;
 	}
 	if (rt->rt_type != RTN_LOCAL)
@@ -587,7 +587,7 @@
 
 	for (srrptr=optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) {
 		if (srrptr + 3 > srrspace) {
-			icmp_send(skb, ICMP_PARAMETERPROB, 0, opt->srr+2);
+			icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((opt->srr+2)<<24));
 			return -EINVAL;
 		}
 		memcpy(&nexthop, &optptr[srrptr-1], 4);
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.1.119/linux/net/ipv4/ip_output.c	Tue Jul 28 14:21:10 1998
+++ linux/net/ipv4/ip_output.c	Tue Sep  1 10:55:12 1998
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) output module.
  *
- * Version:	$Id: ip_output.c,v 1.59 1998/07/15 05:05:15 davem Exp $
+ * Version:	$Id: ip_output.c,v 1.61 1998/08/26 12:03:54 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -171,14 +171,7 @@
 	 */
 
 	if (rt->rt_flags&RTCF_MULTICAST && (!sk || sk->ip_mc_loop)) {
-#ifndef CONFIG_IP_MROUTE
-#if 1
-		/* It should never occur. Delete it eventually. --ANK */
-		if (!(rt->rt_flags&RTCF_LOCAL) || (dev->flags&IFF_LOOPBACK))
-			printk(KERN_DEBUG "ip_mc_output (mc): it should never occur\n");
-		else
-#endif
-#else
+#ifdef CONFIG_IP_MROUTE
 		/* Small optimization: do not loopback not local frames,
 		   which returned after forwarding; they will be  dropped
 		   by ip_mr_input in any case.
@@ -199,15 +192,8 @@
 		}
 	}
 
-	if (rt->rt_flags&RTCF_BROADCAST) {
-#if 1
-		/* It should never occur. Delete it eventually. --ANK */
-		if (!(rt->rt_flags&RTCF_LOCAL) || (dev->flags&IFF_LOOPBACK))
-			printk(KERN_DEBUG "ip_mc_output (brd): it should never occur!\n");
-		else
-#endif
+	if (rt->rt_flags&RTCF_BROADCAST)
 		dev_loopback_xmit(skb);
-	}
 
 	return ip_finish_output(skb);
 }
@@ -281,8 +267,6 @@
 	iph->ihl      = 5;
 	iph->tos      = sk->ip_tos;
 	iph->frag_off = 0;
-	if(sk->ip_pmtudisc == IP_PMTUDISC_WANT && !(rt->u.dst.mxlock & (1 << RTAX_MTU)))
-		iph->frag_off |= __constant_htons(IP_DF);
 	iph->ttl      = sk->ip_ttl;
 	iph->daddr    = rt->rt_dst;
 	iph->saddr    = rt->rt_src;
@@ -316,6 +300,8 @@
 		kfree_skb(skb);
 		if (skb2 == NULL)
 			return;
+		if (sk)
+			skb_set_owner_w(skb, sk);
 		skb = skb2;
 		iph = skb->nh.iph;
 	}
@@ -326,6 +312,9 @@
 	if (tot_len > rt->u.dst.pmtu)
 		goto fragment;
 
+	if (sk->ip_pmtudisc == IP_PMTUDISC_WANT && !(rt->u.dst.mxlock & (1 << RTAX_MTU)))
+		iph->frag_off |= __constant_htons(IP_DF);
+
 	/* Add an IP checksum. */
 	ip_send_check(iph);
 
@@ -334,7 +323,15 @@
 	return;
 
 fragment:
-	if ((iph->frag_off & htons(IP_DF)) != 0) {
+	if (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
+	    !(rt->u.dst.mxlock & (1 << RTAX_MTU)) &&
+	    tot_len > (iph->ihl<<2) + sizeof(struct tcphdr)+16) {
+		/* Reject packet ONLY if TCP might fragment
+		   it itself, if were careful enough.
+		   Test is not precise (f.e. it does not take sacks
+		   into account). Actually, tcp should make it. --ANK (980801)
+		 */
+		iph->frag_off |= __constant_htons(IP_DF);
 		printk(KERN_DEBUG "sending pkt_too_big to self\n");
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
 			  htonl(rt->u.dst.pmtu));
@@ -701,7 +698,6 @@
 	unsigned int mtu, hlen, left, len; 
 	int offset;
 	int not_last_frag;
-	u16 dont_fragment;
 	struct rtable *rt = (struct rtable*)skb->dst;
 
 	dev = rt->u.dst.dev;
@@ -726,10 +722,14 @@
 	 *	The protocol doesn't seem to say what to do in the case that the
 	 *	frame + options doesn't fit the mtu. As it used to fall down dead
 	 *	in this case we were fortunate it didn't happen
+	 *
+	 *	It is impossible, because mtu>=68. --ANK (980801)
 	 */
 
+#ifdef CONFIG_NET_PARANOIA
 	if (mtu<8) 
 		goto fail;
+#endif
 
 	/*
 	 *	Fragment the datagram.
@@ -739,14 +739,6 @@
 	not_last_frag = iph->frag_off & htons(IP_MF);
 
 	/*
-	 *	Nice moment: if DF is set and we are here,
-	 *	it means that packet should be fragmented and
-	 *	DF is set on fragments. If it works,
-	 *	path MTU discovery can be done by ONE segment(!). --ANK
-	 */
-	dont_fragment = iph->frag_off & htons(IP_DF);
-
-	/*
 	 *	Keep copying data until we run out.
 	 */
 
@@ -805,7 +797,7 @@
 		 *	Fill in the new header fields.
 		 */
 		iph = skb2->nh.iph;
-		iph->frag_off = htons((offset >> 3))|dont_fragment;
+		iph->frag_off = htons((offset >> 3));
 
 		/* ANK: dirty, but effective trick. Upgrade options only if
 		 * the segment to be fragmented was THE FIRST (otherwise,
@@ -858,11 +850,6 @@
 	int len; 
 	int hdrflag = 1; 
 
-#if 0
-	printk("ip_reply_glue_bits: offset=%u,flen=%u iov[0].l=%u,iov[1].len=%u\n",
-	       offset,fraglen,dp->iov[0].iov_len,dp->iov[1].iov_len);
-#endif
-
 	iov = &dp->iov[0]; 
 	if (offset >= iov->iov_len) { 
 		offset -= iov->iov_len;
@@ -871,12 +858,6 @@
 	}
 	len = iov->iov_len - offset;
 	if (fraglen > len) { /* overlapping. */ 
-#if 1
-		if (iov > &dp->iov[0]) {
-			printk("frag too long! (o=%u,fl=%u)\n",offset,fraglen);
-			return -1;
-		}
-#endif
 		dp->csum = csum_partial_copy_nocheck(iov->iov_base+offset, to, len,
 					     dp->csum);
 		offset = 0;
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v2.1.119/linux/net/ipv4/ip_sockglue.c	Tue Jul 28 14:21:10 1998
+++ linux/net/ipv4/ip_sockglue.c	Thu Aug 27 19:33:08 1998
@@ -5,7 +5,7 @@
  *
  *		The IP to API glue.
  *		
- * Version:	$Id: ip_sockglue.c,v 1.36 1998/07/15 05:05:06 davem Exp $
+ * Version:	$Id: ip_sockglue.c,v 1.37 1998/08/26 12:03:57 davem Exp $
  *
  * Authors:	see ip.c
  *
@@ -28,6 +28,7 @@
 #include <net/sock.h>
 #include <net/ip.h>
 #include <net/icmp.h>
+#include <net/tcp.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/igmp.h>
@@ -36,6 +37,9 @@
 #include <linux/route.h>
 #include <linux/mroute.h>
 #include <net/route.h>
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#include <net/transp_v6.h>
+#endif
 
 #include <asm/uaccess.h>
 
@@ -140,6 +144,10 @@
 	struct cmsghdr *cmsg;
 
 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+		if ((unsigned long)(((char*)cmsg - (char*)msg->msg_control)
+				    + cmsg->cmsg_len) > msg->msg_controllen) {
+			return -EINVAL;
+		}
 		if (cmsg->cmsg_level != SOL_IP)
 			continue;
 		switch (cmsg->cmsg_type) {
@@ -255,22 +263,30 @@
 		case IP_OPTIONS:
 		{
 			struct ip_options * opt = NULL;
-			struct ip_options * old_opt;
 			if (optlen > 40 || optlen < 0)
 				return -EINVAL;
 			err = ip_options_get(&opt, optval, optlen, 1);
 			if (err)
 				return err;
-			/*
-			 * ANK: I'm afraid that receive handler may change
-			 * options from under us.
-			 */
-			cli();
-			old_opt = sk->opt;
-			sk->opt = opt;
-			sti();
-			if (old_opt)
-				kfree_s(old_opt, sizeof(struct ip_options) + old_opt->optlen);
+			start_bh_atomic();
+			if (sk->type == SOCK_STREAM) {
+				struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+				if (sk->family == PF_INET ||
+				    ((tcp_connected(sk->state) || sk->state == TCP_SYN_SENT)
+				     && sk->daddr != LOOPBACK4_IPV6)) {
+#endif
+					if (opt)
+						tp->ext_header_len = opt->optlen;
+					tcp_sync_mss(sk, tp->pmtu_cookie);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+				}
+#endif
+			}
+			opt = xchg(&sk->opt, opt);
+			end_bh_atomic();
+			if (opt)
+				kfree_s(opt, sizeof(struct ip_options) + opt->optlen);
 			return 0;
 		}
 		case IP_PKTINFO:
@@ -497,11 +513,11 @@
 			{
 				unsigned char optbuf[sizeof(struct ip_options)+40];
 				struct ip_options * opt = (struct ip_options*)optbuf;
-				cli();
+				start_bh_atomic();
 				opt->optlen = 0;
 				if (sk->opt)
 					memcpy(optbuf, sk->opt, sizeof(struct ip_options)+sk->opt->optlen);
-				sti();
+				end_bh_atomic();
 				if (opt->optlen == 0) 
 					return put_user(0, optlen);
 
@@ -511,7 +527,7 @@
 				if(put_user(len, optlen))
 					return -EFAULT;
 				if(copy_to_user(optval, opt->__data, len))
-					    return -EFAULT;
+					return -EFAULT;
 				return 0;
 			}
 		case IP_PKTINFO:
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v2.1.119/linux/net/ipv4/ipip.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv4/ipip.c	Thu Aug 27 19:33:08 1998
@@ -1,7 +1,7 @@
 /*
  *	Linux NET3:	IP/IP protocol decoder. 
  *
- *	Version: $Id: ipip.c,v 1.22 1998/03/08 05:56:27 davem Exp $
+ *	Version: $Id: ipip.c,v 1.23 1998/08/26 12:04:00 davem Exp $
  *
  *	Authors:
  *		Sam Lantinga (slouken@cs.ucdavis.edu)  02/01/95
@@ -551,6 +551,8 @@
 			tunnel->recursion--;
 			return 0;
 		}
+		if (skb->sk)
+			skb_set_owner_w(new_skb, skb->sk);
 		dev_kfree_skb(skb);
 		skb = new_skb;
 	}
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/ipmr.c linux/net/ipv4/ipmr.c
--- v2.1.119/linux/net/ipv4/ipmr.c	Thu May 14 19:47:44 1998
+++ linux/net/ipv4/ipmr.c	Thu Aug 27 19:33:08 1998
@@ -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.35 1998/05/13 06:23:24 davem Exp $
+ *	Version: $Id: ipmr.c,v 1.36 1998/08/26 12:04:03 davem Exp $
  *
  *	Fixes:
  *	Michael Chastain	:	Incorrect size of copying.
@@ -55,6 +55,8 @@
 #include <net/raw.h>
 #include <linux/notifier.h>
 #include <linux/if_arp.h>
+#include <linux/ip_fw.h>
+#include <linux/firewall.h>
 #include <net/ipip.h>
 #include <net/checksum.h>
 
@@ -1044,7 +1046,12 @@
 
 	dev = rt->u.dst.dev;
 
-	if (skb->len+encap > dev->mtu && (ntohs(iph->frag_off) & IP_DF)) {
+	if (skb->len+encap > rt->u.dst.pmtu /* && (ntohs(iph->frag_off) & IP_DF) */) {
+		/* Do not fragment multicasts. Alas, IPv4 does not
+		   allow to send ICMP, so that packets will disappear
+		   to blackhole.
+		 */
+
 		ip_statistics.IpFragFails++;
 		ip_rt_put(rt);
 		return;
@@ -1052,11 +1059,6 @@
 
 	encap += dev->hard_header_len;
 
-	if (skb->len+encap > 65534) {
-		ip_rt_put(rt);
-		return;
-	}
-
 	if (skb_headroom(skb) < encap || skb_cloned(skb) || !last)
 		skb2 = skb_realloc_headroom(skb, (encap + 15)&~15);
 	else if (atomic_read(&skb->users) != 1)
@@ -1076,18 +1078,37 @@
 
 	dst_release(skb2->dst);
 	skb2->dst = &rt->u.dst;
-
 	iph = skb2->nh.iph;
 	ip_decrease_ttl(iph);
 
+#ifdef CONFIG_FIREWALL
+	if (call_fw_firewall(PF_INET, vif->dev, skb2->nh.iph, NULL, &skb2) < FW_ACCEPT) {
+		kfree_skb(skb2);
+		return;
+	}
+	if (call_out_firewall(PF_INET, vif->dev, skb2->nh.iph, NULL, &skb2) < FW_ACCEPT) {
+		kfree_skb(skb2);
+		return;
+	}
+#endif
 	if (vif->flags & VIFF_TUNNEL) {
 		ip_encap(skb2, vif->local, vif->remote);
+#ifdef CONFIG_FIREWALL
+		/* Double output firewalling on tunnels: one is on tunnel
+		   another one is on real device.
+		 */
+		if (call_out_firewall(PF_INET, dev, skb2->nh.iph, NULL, &skb2) < FW_ACCEPT) {
+			kfree_skb(skb2);
+			return;
+		}
+#endif
 		((struct ip_tunnel *)vif->dev->priv)->stat.tx_packets++;
 		((struct ip_tunnel *)vif->dev->priv)->stat.tx_bytes+=skb2->len;
 	}
 
 	IPCB(skb2)->flags |= IPSKB_FORWARDED;
 
+
 	/*
 	 * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
 	 * not only before forwarding, but after forwarding on all output
@@ -1351,21 +1372,12 @@
 	struct rtnexthop *nhp;
 	struct device *dev = vif_table[c->mfc_parent].dev;
 	u8 *b = skb->tail;
-
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	if (dev) {
-		u8 *o = skb->tail;
-		RTA_PUT(skb, RTA_IIF, 4, &dev->ifindex);
-		rtm->rtm_optlen += skb->tail - o;
-	}
-#else
 	struct rtattr *mp_head;
 
 	if (dev)
 		RTA_PUT(skb, RTA_IIF, 4, &dev->ifindex);
 
 	mp_head = (struct rtattr*)skb_put(skb, RTA_LENGTH(0));
-#endif
 
 	for (ct = c->mfc_minvif; ct < c->mfc_maxvif; ct++) {
 		if (c->mfc_ttls[ct] < 255) {
@@ -1376,15 +1388,10 @@
 			nhp->rtnh_hops = c->mfc_ttls[ct];
 			nhp->rtnh_ifindex = vif_table[ct].dev->ifindex;
 			nhp->rtnh_len = sizeof(*nhp);
-#ifdef CONFIG_RTNL_OLD_IFINFO
-			rtm->rtm_nhs++;
-#endif
 		}
 	}
-#ifndef CONFIG_RTNL_OLD_IFINFO
 	mp_head->rta_type = RTA_MULTIPATH;
 	mp_head->rta_len = skb->tail - (u8*)mp_head;
-#endif
 	rtm->rtm_type = RTN_MULTICAST;
 	return 1;
 
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/proc.c linux/net/ipv4/proc.c
--- v2.1.119/linux/net/ipv4/proc.c	Sat May  2 14:19:55 1998
+++ linux/net/ipv4/proc.c	Thu Aug 27 19:33:08 1998
@@ -7,7 +7,7 @@
  *		PROC file system.  It is mainly used for debugging and
  *		statistics.
  *
- * Version:	$Id: proc.c,v 1.30 1998/04/16 16:29:05 freitag Exp $
+ * Version:	$Id: proc.c,v 1.31 1998/07/29 20:09:25 freitag Exp $
  *
  * Authors:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de>
@@ -357,12 +357,15 @@
 
 	len = sprintf(buffer,
 		      "TcpExt: SyncookiesSent SyncookiesRecv SyncookiesFailed"
-		      "EmbryonicRsts\n"
-		      "TcpExt: %lu %lu %lu %lu\n",
+		      " EmbryonicRsts PruneCalled RcvPruned OfoPruned\n"
+		      "TcpExt: %lu %lu %lu %lu %lu %lu %lu\n",
 		      net_statistics.SyncookiesSent,
 		      net_statistics.SyncookiesRecv,
 		      net_statistics.SyncookiesFailed,
-		      net_statistics.EmbryonicRsts);
+		      net_statistics.EmbryonicRsts,
+		      net_statistics.PruneCalled,
+		      net_statistics.RcvPruned,
+		      net_statistics.OfoPruned);
 
 	if (offset >= len)
 	{
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/raw.c linux/net/ipv4/raw.c
--- v2.1.119/linux/net/ipv4/raw.c	Thu Aug 27 19:56:30 1998
+++ linux/net/ipv4/raw.c	Thu Aug 27 19:33:08 1998
@@ -5,7 +5,7 @@
  *
  *		RAW - implementation of IP "raw" sockets.
  *
- * Version:	$Id: raw.c,v 1.36 1998/05/08 21:06:29 davem Exp $
+ * Version:	$Id: raw.c,v 1.37 1998/08/26 12:04:07 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -152,7 +152,7 @@
 	int type = skb->h.icmph->type;
 	int code = skb->h.icmph->code;
 
-	if (sk->ip_recverr && !atomic_read(&sk->sock_readers)) {
+	if (sk->ip_recverr) {
 		struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
 		if (skb2 && sock_queue_err_skb(sk, skb2))
 			kfree_skb(skb);
@@ -194,10 +194,6 @@
 	
 	skb->h.raw = skb->nh.raw;
 
-	if (atomic_read(&sk->sock_readers)) {
-		__skb_queue_tail(&sk->back_log, skb);
-		return 0;
-	}
 	raw_rcv_skb(sk, skb);
 	return 0;
 }
@@ -379,10 +375,33 @@
 
 static void raw_close(struct sock *sk, unsigned long timeout)
 {
+	/* Observation: when raw_close is called, processes have
+	   no access to socket anymore. But net still has.
+	   Step one, detach it from networking:
+
+	   A. Remove from hash tables.
+	 */
 	sk->state = TCP_CLOSE;
+	raw_v4_unhash(sk);
+        /*
+	   B. Raw sockets may have direct kernel refereneces. Kill them.
+	 */
 	ip_ra_control(sk, 0, NULL);
+
+	/* In this point socket cannot receive new packets anymore */
+
+
+	/* But we still have packets pending on receive
+	   queue and probably, our own packets waiting in device queues.
+	   sock_destroy will drain receive queue, but transmitted
+	   packets will delay socket destruction.
+	   Set sk->dead=1 in order to prevent wakeups, when these
+	   packet will be freed.
+	 */
 	sk->dead=1;
 	destroy_sock(sk);
+
+	/* That's all. No races here. */
 }
 
 /* This gets rid of all the nasties in af_inet. -DaveM */
@@ -474,14 +493,8 @@
 static int raw_init(struct sock *sk)
 {
 	struct raw_opt *tp = &(sk->tp_pinfo.tp_raw4);
-	if (sk->num == IPPROTO_ICMP) {
+	if (sk->num == IPPROTO_ICMP)
 		memset(&tp->filter, 0, sizeof(tp->filter));
-
-		/* By default block ECHO and TIMESTAMP requests */
-
-		set_bit(ICMP_ECHO, &tp->filter);
-		set_bit(ICMP_TIMESTAMP, &tp->filter);
-	}
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.1.119/linux/net/ipv4/route.c	Tue Jul 28 14:21:10 1998
+++ linux/net/ipv4/route.c	Thu Aug 27 19:33:08 1998
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.54 1998/07/15 05:05:22 davem Exp $
+ * Version:	$Id: route.c,v 1.57 1998/08/26 12:04:09 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -48,6 +48,7 @@
  *					route.c and rewritten from scratch.
  *		Andi Kleen	:	Load-limit warning messages.
  *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma.
+ *	Vitaly E. Lavrov	:	Race condition in ip_route_input_slow.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -90,6 +91,8 @@
 #include <linux/sysctl.h>
 #endif
 
+#define IP_MAX_MTU	0xFFF0
+
 #define RT_GC_TIMEOUT (300*HZ)
 
 int ip_rt_min_delay = 2*HZ;
@@ -166,7 +169,7 @@
  * Route cache.
  */
 
-static struct rtable 	*rt_hash_table[RT_HASH_DIVISOR];
+struct rtable 	*rt_hash_table[RT_HASH_DIVISOR];
 
 static struct rtable * rt_intern_hash(unsigned hash, struct rtable * rth);
 
@@ -246,6 +249,13 @@
 	dst_free(&rt->u.dst);
 }
 
+static __inline__ int rt_fast_clean(struct rtable *rth)
+{
+	/* Kill broadcast/multicast entries very aggresively, if they
+	   collide in hash table with more useful entries */
+	return ((rth->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST))
+		&& rth->key.iif && rth->u.rt_next);
+}
 
 static void rt_check_expire(unsigned long dummy)
 {
@@ -255,43 +265,30 @@
 	unsigned long now = jiffies;
 
 	for (i=0; i<RT_HASH_DIVISOR/5; i++) {
+		unsigned tmo = ip_rt_gc_timeout;
+
 		rover = (rover + 1) & (RT_HASH_DIVISOR-1);
 		rthp = &rt_hash_table[rover];
 
 		while ((rth = *rthp) != NULL) {
-			struct rtable * rth_next = rth->u.rt_next;
-
 			/*
 			 * Cleanup aged off entries.
 			 */
 
 			if (!atomic_read(&rth->u.dst.use) &&
-			    (now - rth->u.dst.lastuse > ip_rt_gc_timeout)) {
-				*rthp = rth_next;
-#if RT_CACHE_DEBUG >= 2
-				printk("rt_check_expire clean %02x@%08x\n", rover, rth->rt_dst);
-#endif
+			    (now - rth->u.dst.lastuse > tmo
+			     || rt_fast_clean(rth))) {
+				*rthp = rth->u.rt_next;
 				rt_free(rth);
 				continue;
 			}
 
-			if (!rth_next)
-				break;
-
-			if ( (long)(rth_next->u.dst.lastuse - rth->u.dst.lastuse) > RT_CACHE_BUBBLE_THRESHOLD ||
-			    ((long)(rth->u.dst.lastuse - rth_next->u.dst.lastuse) < 0 &&
-			     atomic_read(&rth->u.dst.refcnt) < atomic_read(&rth_next->u.dst.refcnt))) {
-#if RT_CACHE_DEBUG >= 2
-				printk("rt_check_expire bubbled %02x@%08x<->%08x\n", rover, rth->rt_dst, rth_next->rt_dst);
-#endif
-				*rthp = rth_next;
- 				rth->u.rt_next = rth_next->u.rt_next;
-				rth_next->u.rt_next = rth;
-				rthp = &rth_next->u.rt_next;
-				continue;
-			}
+			tmo >>= 1;
 			rthp = &rth->u.rt_next;
 		}
+
+		if ((jiffies - now) > 0)
+			break;
 	}
 	rt_periodic_timer.expires = now + ip_rt_gc_interval;
 	add_timer(&rt_periodic_timer);
@@ -305,21 +302,14 @@
 	rt_deadline = 0;
 
 	for (i=0; i<RT_HASH_DIVISOR; i++) {
-		int nr=0;
-
 		if ((rth = xchg(&rt_hash_table[i], NULL)) == NULL)
 			continue;
 
 		for (; rth; rth=next) {
 			next = rth->u.rt_next;
-			nr++;
 			rth->u.rt_next = NULL;
 			rt_free(rth);
 		}
-#if RT_CACHE_DEBUG >= 2
-		if (nr > 0)
-			printk("rt_cache_flush: %d@%02x\n", nr, i);
-#endif
 	}
 }
   
@@ -384,17 +374,23 @@
 	expire++;
 
 	for (i=0; i<RT_HASH_DIVISOR; i++) {
+		unsigned tmo;
 		if (!rt_hash_table[i])
 			continue;
+		tmo = expire;
 		for (rthp=&rt_hash_table[i]; (rth=*rthp); rthp=&rth->u.rt_next)	{
 			if (atomic_read(&rth->u.dst.use) ||
-			    now - rth->u.dst.lastuse < expire)
+			    (now - rth->u.dst.lastuse < tmo && !rt_fast_clean(rth))) {
+				tmo >>= 1;
 				continue;
+			}
 			*rthp = rth->u.rt_next;
 			rth->u.rt_next = NULL;
 			rt_free(rth);
 			break;
 		}
+		if ((jiffies-now)>0)
+			break;
 	}
 
 	last_gc = now;
@@ -412,8 +408,6 @@
 	struct rtable	*rth, **rthp;
 	unsigned long	now = jiffies;
 
-	rt->u.dst.priority = rt_tos2priority(rt->key.tos);
-
 	start_bh_atomic();
 
 	rthp = &rt_hash_table[hash];
@@ -793,19 +787,17 @@
 	if (fi) {
 		if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK)
 			rt->rt_gateway = FIB_RES_GW(*res);
-#ifndef CONFIG_RTNL_OLD_IFINFO
 		rt->u.dst.mxlock = fi->fib_metrics[RTAX_LOCK-1];
 		rt->u.dst.pmtu = fi->fib_mtu;
 		if (fi->fib_mtu == 0) {
 			rt->u.dst.pmtu = rt->u.dst.dev->mtu;
+			if (rt->u.dst.pmtu > IP_MAX_MTU)
+				rt->u.dst.pmtu = IP_MAX_MTU;
 			if (rt->u.dst.mxlock&(1<<RTAX_MTU) &&
 			    rt->rt_gateway != rt->rt_dst &&
 			    rt->u.dst.pmtu > 576)
 				rt->u.dst.pmtu = 576;
 		}
-#else
-		rt->u.dst.pmtu	= fi->fib_mtu ? : rt->u.dst.dev->mtu;
-#endif
 		rt->u.dst.window= fi->fib_window ? : 0;
 		rt->u.dst.rtt	= fi->fib_rtt ? : TCP_TIMEOUT_INIT;
 #ifdef CONFIG_NET_CLS_ROUTE
@@ -813,6 +805,8 @@
 #endif
 	} else {
 		rt->u.dst.pmtu	= rt->u.dst.dev->mtu;
+		if (rt->u.dst.pmtu > IP_MAX_MTU)
+			rt->u.dst.pmtu = IP_MAX_MTU;
 		rt->u.dst.window= 0;
 		rt->u.dst.rtt	= TCP_TIMEOUT_INIT;
 	}
@@ -930,7 +924,7 @@
 	if (MULTICAST(saddr) || BADCLASS(saddr) || LOOPBACK(saddr))
 		goto martian_source;
 
-	if (daddr == 0xFFFFFFFF)
+	if (daddr == 0xFFFFFFFF || (saddr == 0 && daddr == 0))
 		goto brd_input;
 
 	/* Accept zero addresses only to limited broadcast;
@@ -991,6 +985,11 @@
 		fib_select_multipath(&key, &res);
 #endif
 	out_dev = FIB_RES_DEV(res)->ip_ptr;
+	if (out_dev == NULL) {
+		if (net_ratelimit())
+			printk(KERN_CRIT "Bug in ip_route_input_slow(). Please, report\n");
+		return -EINVAL;
+	}
 
 	err = fib_validate_source(saddr, daddr, tos, FIB_RES_OIF(res), dev, &spec_dst);
 	if (err < 0)
@@ -1312,15 +1311,14 @@
 			   tables are looked up with only one purpose:
 			   to catch if destination is gatewayed, rather than
 			   direct. Moreover, if MSG_DONTROUTE is set,
-			   we send packet, no matter of routing tables
-			   of ifaddr state. --ANK
+			   we send packet, ignoring both routing tables
+			   and ifaddr state. --ANK
 
 
 			   We could make it even if oif is unknown,
 			   likely IPv6, but we do not.
 			 */
 
-			printk(KERN_DEBUG "Dest not on link. Forcing...\n");
 			if (key.src == 0)
 				key.src = inet_select_addr(dev_out, 0, RT_SCOPE_LINK);
 			goto make_route;
@@ -1475,7 +1473,7 @@
 
 #ifdef CONFIG_RTNETLINK
 
-static int rt_fill_info(struct sk_buff *skb, pid_t pid, u32 seq, int event, int nowait)
+static int rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int event, int nowait)
 {
 	struct rtable *rt = (struct rtable*)skb->dst;
 	struct rtmsg *r;
@@ -1485,11 +1483,7 @@
 #ifdef CONFIG_IP_MROUTE
 	struct rtattr *eptr;
 #endif
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	unsigned char 	 *o;
-#else
 	struct rtattr *mx;
-#endif
 
 	nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*r));
 	r = NLMSG_DATA(nlh);
@@ -1503,11 +1497,6 @@
 	r->rtm_scope = RT_SCOPE_UNIVERSE;
 	r->rtm_protocol = RTPROT_UNSPEC;
 	r->rtm_flags = (rt->rt_flags&~0xFFFF) | RTM_F_CLONED;
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	r->rtm_nhs = 0;
-
-	o = skb->tail;
-#endif
 	RTA_PUT(skb, RTA_DST, 4, &rt->rt_dst);
 	if (rt->key.src) {
 		r->rtm_src_len = 32;
@@ -1521,11 +1510,6 @@
 		RTA_PUT(skb, RTA_PREFSRC, 4, &rt->rt_src);
 	if (rt->rt_dst != rt->rt_gateway)
 		RTA_PUT(skb, RTA_GATEWAY, 4, &rt->rt_gateway);
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	RTA_PUT(skb, RTA_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
-	RTA_PUT(skb, RTA_WINDOW, sizeof(unsigned), &rt->u.dst.window);
-	RTA_PUT(skb, RTA_RTT, sizeof(unsigned), &rt->u.dst.rtt);
-#else
 	mx = (struct rtattr*)skb->tail;
 	RTA_PUT(skb, RTA_METRICS, 0, NULL);
 	if (rt->u.dst.mxlock)
@@ -1539,7 +1523,6 @@
 	mx->rta_len = skb->tail - (u8*)mx;
 	if (mx->rta_len == RTA_LENGTH(0))
 		skb_trim(skb, (u8*)mx - skb->data);
-#endif
 	ci.rta_lastuse = jiffies - rt->u.dst.lastuse;
 	ci.rta_used = atomic_read(&rt->u.dst.refcnt);
 	ci.rta_clntref = atomic_read(&rt->u.dst.use);
@@ -1549,9 +1532,6 @@
 	eptr = (struct rtattr*)skb->tail;
 #endif
 	RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci);
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	r->rtm_optlen = skb->tail - o;
-#endif
 	if (rt->key.iif) {
 #ifdef CONFIG_IP_MROUTE
 		u32 dst = rt->rt_dst;
@@ -1573,9 +1553,6 @@
 #endif
 		{
 			RTA_PUT(skb, RTA_IIF, sizeof(int), &rt->key.iif);
-#ifdef CONFIG_RTNL_OLD_IFINFO
-			r->rtm_optlen = skb->tail - o;
-#endif
 		}
 	}
 
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.119/linux/net/ipv4/tcp.c	Thu Aug 27 19:56:30 1998
+++ linux/net/ipv4/tcp.c	Mon Aug 31 12:08:27 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.116 1998/07/26 03:06:54 davem Exp $
+ * Version:	$Id: tcp.c,v 1.119 1998/08/26 12:04:14 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -201,6 +201,7 @@
  *					tcp_do_sendmsg to avoid burstiness.
  *		Eric Schenk	:	Fix fast close down bug with
  *					shutdown() followed by close().
+ *		Andi Kleen :	Make poll agree with SIGIO
  *					
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -383,13 +384,14 @@
  *
  * ICMP messages (4.2.3.9)
  *   MUST act on ICMP errors. (does)
- *   MUST slow transmission upon receipt of a Source Quench. (does)
+ *   MUST slow transmission upon receipt of a Source Quench. (doesn't anymore 
+ *   because that is deprecated now by the IETF, can be turned on)
  *   MUST NOT abort connection upon receipt of soft Destination
  *     Unreachables (0, 1, 5), Time Exceededs and Parameter
  *     Problems. (doesn't)
  *   SHOULD report soft Destination Unreachables etc. to the
- *     application. (does, but may drop them in the ICMP error handler
- *	during an accept())
+ *     application. (does, except during SYN_RECV and may drop messages
+ *     in some rare cases before accept() - ICMP is unreliable)	
  *   SHOULD abort connection upon receipt of hard Destination Unreachable
  *     messages (2, 3, 4). (does, but see above)
  *
@@ -397,7 +399,7 @@
  *   MUST reject as an error OPEN for invalid remote IP address. (does)
  *   MUST ignore SYN with invalid source address. (does)
  *   MUST silently discard incoming SYN for broadcast/multicast
- *     address. (I'm not sure if it does. Someone should check this.)
+ *     address. (does)
  *
  * Asynchronous Reports (4.2.4.1)
  * MUST provide mechanism for reporting soft errors to application
@@ -537,6 +539,21 @@
 }
 
 /*
+ *	Compute minimal free write space needed to queue new packets. 
+ */
+static inline int tcp_min_write_space(struct sock *sk, struct tcp_opt *tp)
+{
+	int space;
+#if 1 /* This needs benchmarking and real world tests */
+	space = max(tp->mss_cache + 128, MIN_WRITE_SPACE);
+#else /* 2.0 way */
+	/* More than half of the socket queue free? */
+	space = atomic_read(&sk->wmem_alloc) / 2;
+#endif
+	return space;
+}
+
+/*
  *	Wait for a TCP event.
  *
  *	Note that we don't need to lock the socket, as the upper poll layers
@@ -556,36 +573,56 @@
 	mask = 0;
 	if (sk->err)
 		mask = POLLERR;
+
+	/*
+	 * POLLHUP is certainly not done right. But poll() doesn't
+	 * have a notion of HUP in just one direction, and for a
+	 * socket the read side is more interesting.
+	 *
+	 * Some poll() documentation says that POLLHUP is incompatible
+	 * with the POLLOUT/POLLWR flags, so somebody should check this
+	 * all. But careful, it tends to be safer to return too many
+	 * bits than too few, and you can easily break real applications
+	 * if you don't tell them that something has hung up!
+	 *
+	 * Check-me.
+	 */
+	if (sk->shutdown & RCV_SHUTDOWN)
+		mask |= POLLHUP;
+
 	/* Connected? */
 	if ((1 << sk->state) & ~(TCPF_SYN_SENT|TCPF_SYN_RECV)) {
-		int space;
-
-		if (sk->shutdown & RCV_SHUTDOWN)
-			mask |= POLLHUP;
-		
 		if ((tp->rcv_nxt != tp->copied_seq) &&
 		    (tp->urg_seq != tp->copied_seq ||
 		     tp->rcv_nxt != tp->copied_seq+1 ||
 		     sk->urginline || !tp->urg_data))
 			mask |= POLLIN | POLLRDNORM;
 
-#if 1 /* This needs benchmarking and real world tests */
-		space = (sk->dst_cache ? sk->dst_cache->pmtu : sk->mss) + 128;
-		if (space < 2048) /* XXX */
-			space = 2048;
-#else /* 2.0 way */
-		/* More than half of the socket queue free? */
-		space = atomic_read(&sk->wmem_alloc) / 2;
-#endif
 		/* Always wake the user up when an error occurred */
-		if (sock_wspace(sk) >= space || sk->err)
+		if (sock_wspace(sk) >= tcp_min_write_space(sk, tp) || sk->err)
 			mask |= POLLOUT | POLLWRNORM;
 		if (tp->urg_data & URG_VALID)
-		    	mask |= POLLPRI;
+			mask |= POLLPRI;
 	}
 	return mask;
 }
 
+/*
+ *	Socket write_space callback.
+ *	This (or rather the sock_wake_async) should agree with poll. 
+ */
+void tcp_write_space(struct sock *sk)
+{
+	if (sk->dead)
+		return; 
+
+	wake_up_interruptible(sk->sleep);
+	if (sock_wspace(sk) >=
+	    tcp_min_write_space(sk, &(sk->tp_pinfo.af_tcp)))
+		sock_wake_async(sk->socket, 2);
+}
+
+
 int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
 {
 	int answ;
@@ -1025,7 +1062,7 @@
 		 * which don't advertize a larger window.
 		 */
 		if((copied >= rcv_window_now) &&
-		   ((rcv_window_now + sk->mss) <= tp->window_clamp))
+		   ((rcv_window_now + tp->mss_cache) <= tp->window_clamp))
 			tcp_read_wakeup(sk);
 	}
 }
@@ -1543,16 +1580,18 @@
 
 	tcp_synq_unlink(tp, req, prev);
 	newsk = req->sk;
+	req->class->destructor(req);
 	tcp_openreq_free(req);
 	sk->ack_backlog--; 
 
-	/* FIXME: need to check here if newsk has already
-	 * an soft_err or err set.
-	 * We have two options here then: reply (this behaviour matches
-	 * Solaris) or return the error to the application (old Linux)
-	 */
+	/*
+	 * This does not pass any already set errors on the new socket
+	 * to the user, but they will be returned on the first socket operation
+	 * after the accept.
+	 */ 
+
 	error = 0;
- out:
+out:
 	release_sock(sk);
 	sk->err = error;
 	return newsk;
@@ -1586,7 +1625,7 @@
  */
 	  		if(val<1||val>MAX_WINDOW)
 				return -EINVAL;
-			sk->user_mss=val;
+			tp->user_mss=val;
 			return 0;
 		case TCP_NODELAY:
 			sk->nonagle=(val==0)?0:1;
@@ -1614,7 +1653,7 @@
 
 	switch(optname) {
 		case TCP_MAXSEG:
-			val=sk->user_mss;
+			val=tp->user_mss;
 			break;
 		case TCP_NODELAY:
 			val=sk->nonagle;
@@ -1640,7 +1679,7 @@
 
 extern void __skb_cb_too_small_for_tcp(int, int);
 
-__initfunc(void tcp_init(void))
+void __init tcp_init(void)
 {
 	struct sk_buff *skb = NULL;
 
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.1.119/linux/net/ipv4/tcp_input.c	Tue Jul 28 14:21:10 1998
+++ linux/net/ipv4/tcp_input.c	Thu Aug 27 19:33:09 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.121 1998/07/15 04:39:12 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.127 1998/08/26 12:04:20 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -50,6 +50,9 @@
  *		Andi Kleen:		Make sure we never ack data there is not
  *					enough room for. Also make this condition
  *					a fatal error if it might still happen.
+ *		Andi Kleen:		Add tcp_measure_rcv_mss to make 
+ *					connections with MSS<min(MTU,ann. MSS)
+ *					work without delayed acks. 
  */
 
 #include <linux/config.h>
@@ -214,7 +217,7 @@
 
 #define PAWS_24DAYS	(HZ * 60 * 60 * 24 * 24)
 
-extern __inline__ int tcp_paws_discard(struct tcp_opt *tp, struct tcphdr *th, __u16 len)
+extern __inline__ int tcp_paws_discard(struct tcp_opt *tp, struct tcphdr *th, unsigned len)
 {
 	/* ts_recent must be younger than 24 days */
 	return (((jiffies - tp->ts_recent_stamp) >= PAWS_24DAYS) ||
@@ -346,9 +349,11 @@
 	  			switch(opcode) {
 				case TCPOPT_MSS:
 					if(opsize==TCPOLEN_MSS && th->syn) {
-						tp->in_mss = ntohs(*(__u16 *)ptr);
-						if (tp->in_mss == 0)
-							tp->in_mss = 536;
+						u16 in_mss = ntohs(*(__u16 *)ptr);
+						if (in_mss == 0)
+							in_mss = 536;
+						if (tp->mss_clamp > in_mss)
+							tp->mss_clamp = in_mss;
 					}
 					break;
 				case TCPOPT_WINDOW:
@@ -863,7 +868,7 @@
  * reconnects and SYN/RST bits being set in the TCP header.
  */
 int tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb,
-			       struct tcphdr *th, void *opt, __u16 len)
+			       struct tcphdr *th, unsigned len)
 {
 	/*	RFC 1122:
 	 *	"When a connection is [...] on TIME-WAIT state [...]
@@ -893,7 +898,7 @@
 			return 0;
 		skb_set_owner_r(skb, sk);
 		af_specific = sk->tp_pinfo.af_tcp.af_specific;
-		if(af_specific->conn_request(sk, skb, opt, isn) < 0)
+		if(af_specific->conn_request(sk, skb, isn) < 0)
 			return 1; /* Toss a reset back. */
 		return 0; /* Discard the frame. */
 	}
@@ -1309,7 +1314,7 @@
 			tp->delayed_acks++;
 
 			/* Tiny-grams with PSH set make us ACK quickly. */
-			if(skb->h.th->psh && (skb->len < (sk->mss >> 1)))
+			if(skb->h.th->psh && (skb->len < (tp->mss_cache >> 1)))
 				tp->ato = HZ/50;
 		}
 		/* This may have eaten into a SACK block. */
@@ -1429,7 +1434,6 @@
 		}
 	}
 
-	/* We no longer have anyone receiving data on this connection. */
 	tcp_data_queue(sk, skb);
 
 	if (before(tp->rcv_nxt, tp->copied_seq)) {
@@ -1464,6 +1468,26 @@
 	}
 }
 
+/* 
+ * Adapt the MSS value used to make delayed ack decision to the 
+ * real world. 
+ */ 
+static __inline__ void tcp_measure_rcv_mss(struct sock *sk, struct sk_buff *skb)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	unsigned int len = skb->len, lss; 
+
+	if (len > tp->rcv_mss) 
+		tp->rcv_mss = len; 
+	lss = tp->last_seg_size; 
+	tp->last_seg_size = 0; 
+	if (len >= 536) {
+		if (len == lss) 
+			tp->rcv_mss = len; 
+		tp->last_seg_size = len; 
+	}
+}
+
 /*
  * Check if sending an ack is needed.
  */
@@ -1486,7 +1510,7 @@
 	 */
 
 	    /* Two full frames received or... */
-	if (((tp->rcv_nxt - tp->rcv_wup) >= sk->mss * MAX_DELAY_ACK) ||
+	if (((tp->rcv_nxt - tp->rcv_wup) >= tp->rcv_mss * MAX_DELAY_ACK) ||
 	    /* We will update the window "significantly" or... */
 	    tcp_raise_window(sk) ||
 	    /* We entered "quick ACK" mode or... */
@@ -1595,11 +1619,14 @@
 
 	SOCK_DEBUG(sk, "prune_queue: c=%x\n", tp->copied_seq);
 
+	net_statistics.PruneCalled++; 
+
 	/* First Clean the out_of_order queue. */
 	/* Start with the end because there are probably the least
 	 * useful packets (crossing fingers).
 	 */
 	while ((skb = __skb_dequeue_tail(&tp->out_of_order_queue))) { 
+		net_statistics.OfoPruned += skb->len; 
 		kfree_skb(skb);
 		if (atomic_read(&sk->rmem_alloc) <= sk->rcvbuf)
 			return 0;
@@ -1620,6 +1647,9 @@
 				   tp->last_ack_sent);
 			return -1;
 		}
+
+		net_statistics.RcvPruned += skb->len; 
+
 		__skb_unlink(skb, skb->list);
 		tp->rcv_nxt = TCP_SKB_CB(skb)->seq;
 		SOCK_DEBUG(sk, "prune_queue: removing %x-%x (c=%x)\n",
@@ -1633,7 +1663,7 @@
 }
 
 int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
-			struct tcphdr *th, __u16 len)
+			struct tcphdr *th, unsigned len)
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	int queued = 0;
@@ -1704,7 +1734,9 @@
 				goto discard;
 			}
 			
-			skb_pull(skb,th->doff*4);
+			__skb_pull(skb,th->doff*4);
+
+			tcp_measure_rcv_mss(sk, skb); 
 
 			/* DO NOT notify forward progress here.
 			 * It saves dozen of CPU instructions in fast path. --ANK
@@ -1719,7 +1751,7 @@
 			tcp_delack_estimator(tp);
 
 			/* Tiny-grams with PSH set make us ACK quickly. */
-			if(th->psh && (skb->len < (sk->mss >> 1)))
+			if(th->psh && (skb->len < (tp->mss_cache >> 1)))
 				tp->ato = HZ/50;
 
 			tp->delayed_acks++;
@@ -1767,6 +1799,25 @@
 	/* step 7: process the segment text */
 	queued = tcp_data(skb, sk, len);
 
+	/* This must be after tcp_data() does the skb_pull() to
+	 * remove the header size from skb->len.
+	 *
+	 * Dave!!! Phrase above (and all about rcv_mss) has 
+	 * nothing to do with reality. rcv_mss must measure TOTAL
+	 * size, including sacks, IP options etc. Hence, measure_rcv_mss
+	 * must occure before pulling etc, otherwise it will flap
+	 * like hell. Even putting it before tcp_data is wrong,
+	 * it should use skb->tail - skb->nh.raw instead.
+	 *					--ANK (980805)
+	 * 
+	 * BTW I broke it. Now all TCP options are handled equally
+	 * in mss_clamp calculations (i.e. ignored, rfc1122),
+	 * and mss_cache does include all of them (i.e. tstamps)
+	 * except for sacks, to calulate effective mss faster.
+	 * 					--ANK (980805)
+	 */
+	tcp_measure_rcv_mss(sk, skb); 
+
 	/* Be careful, tcp_data() may have put this into TIME_WAIT. */
 	if(sk->state != TCP_CLOSE) {
 		tcp_data_snd_check(sk);
@@ -1853,7 +1904,7 @@
  */
 	
 int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
-			  struct tcphdr *th, void *opt, __u16 len)
+			  struct tcphdr *th, unsigned len)
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	int queued = 0;
@@ -1868,7 +1919,7 @@
 			return 1;
 		
 		if(th->syn) {
-			if(tp->af_specific->conn_request(sk, skb, opt, 0) < 0)
+			if(tp->af_specific->conn_request(sk, skb, 0) < 0)
 				return 1;
 
 			/* Now we have several options: In theory there is 
@@ -1961,28 +2012,6 @@
 			/* Can't be earlier, doff would be wrong. */
 			tcp_send_ack(sk);
 
-			/* Check for the case where we tried to advertise
-			 * a window including timestamp options, but did not
-			 * end up using them for this connection.
-			 */
-			if((tp->tstamp_ok == 0) && sysctl_tcp_timestamps)
-				sk->mss += TCPOLEN_TSTAMP_ALIGNED;
-			
-			/* Now limit it if the other end negotiated a smaller
-			 * value.
-			 */
-			if (tp->in_mss) {
-				int real_mss = tp->in_mss;
-
-				/* We store MSS locally with the timestamp bytes
-				 * subtracted, TCP's advertise it with them
-				 * included.  Account for this fact.
-				 */
-				if(tp->tstamp_ok)
-					real_mss -= TCPOLEN_TSTAMP_ALIGNED;
-				sk->mss = min(sk->mss, real_mss);
-			}
-
 			sk->dport = th->source;
 			tp->copied_seq = tp->rcv_nxt;
 
@@ -1990,9 +2019,6 @@
 				sk->state_change(sk);
 				sock_wake_async(sk->socket, 0);
 			}
-
-			/* Drop through step 6 */
-			goto step6;
 		} else {
 			if(th->syn && !th->rst) {
 				/* The previous version of the code
@@ -2017,11 +2043,20 @@
 				tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
 				
 				tcp_send_synack(sk);
-				goto discard;
-			}		
-
+			} else
+				break; 
 		}
-		break;
+
+		/* tp->tcp_header_len and tp->mss_clamp
+		   probably changed, synchronize mss.
+		   */
+		tcp_sync_mss(sk, tp->pmtu_cookie);
+		tp->rcv_mss = tp->mss_cache;
+
+		if (sk->state == TCP_SYN_RECV)
+			goto discard;
+		
+		goto step6; 
 	}
 
 	/*   Parse the tcp_options present on this header.
@@ -2167,6 +2202,11 @@
 		
 	case TCP_ESTABLISHED: 
 		queued = tcp_data(skb, sk, len);
+
+		/* This must be after tcp_data() does the skb_pull() to
+		 * remove the header size from skb->len.
+		 */
+		tcp_measure_rcv_mss(sk, skb); 
 		break;
 	}
 
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.119/linux/net/ipv4/tcp_ipv4.c	Thu Aug 27 19:56:30 1998
+++ linux/net/ipv4/tcp_ipv4.c	Thu Aug 27 19:43:53 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.150 1998/07/28 17:45:07 freitag Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.157 1998/08/28 00:27:47 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -44,6 +44,7 @@
  *		Andi Kleen:		various fixes.
  *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma.
  *	Andi Kleen		:	Fix new listen.
+ *	Andi Kleen		:	Fix accept error reporting.
  */
 
 #include <linux/config.h>
@@ -140,7 +141,8 @@
 		if(tb->port == snum) {
 			if(tb->owners == NULL &&
 			   (tb->flags & TCPB_FLAG_LOCKED)) {
-				tb->flags &= ~TCPB_FLAG_LOCKED;
+				tb->flags &= ~(TCPB_FLAG_LOCKED |
+					       TCPB_FLAG_FASTREUSE);
 				tcp_inc_slow_timer(TCP_SLT_BUCKETGC);
 			}
 			break;
@@ -208,7 +210,7 @@
 
 			/* We must walk the whole port owner list in this case. -DaveM */
 			for(sk2 = tb->owners; sk2; sk2 = sk2->bind_next) {
-				if(sk->bound_dev_if == sk2->bound_dev_if) {
+				if (sk->bound_dev_if == sk2->bound_dev_if) {
 					if(!sk_reuse || !sk2->reuse || sk2->state == TCP_LISTEN) {
 						if(!sk2->rcv_saddr		||
 						   !sk->rcv_saddr		||
@@ -223,16 +225,33 @@
 	}
 	if(result == 0) {
 		if(tb == NULL) {
-			if(tcp_bucket_create(snum) == NULL)
+			if((tb = tcp_bucket_create(snum)) == NULL)
 				result = 1;
+			else if (sk->reuse && sk->state != TCP_LISTEN)
+				tb->flags |= TCPB_FLAG_FASTREUSE;
 		} else {
 			/* It could be pending garbage collection, this
 			 * kills the race and prevents it from disappearing
 			 * out from under us by the time we use it.  -DaveM
 			 */
-			if(tb->owners == NULL && !(tb->flags & TCPB_FLAG_LOCKED)) {
-				tb->flags = TCPB_FLAG_LOCKED;
-				tcp_dec_slow_timer(TCP_SLT_BUCKETGC);
+			if(tb->owners == NULL) {
+				if (!(tb->flags & TCPB_FLAG_LOCKED)) {
+					tb->flags = (TCPB_FLAG_LOCKED |
+						     ((sk->reuse &&
+						       sk->state != TCP_LISTEN) ?
+						      TCPB_FLAG_FASTREUSE : 0));
+					tcp_dec_slow_timer(TCP_SLT_BUCKETGC);
+				} else if (!(tb->flags & TCPB_FLAG_GOODSOCKNUM)) {
+					/* Someone is in between the bind
+					 * and the actual connect or listen.
+					 * See if it was a legitimate reuse
+					 * and we are as well, else punt.
+					 */
+					if (sk->reuse == 0 ||
+					    !(tb->flags & TCPB_FLAG_FASTREUSE))
+						result = 1;
+				} else
+					tb->flags &= ~TCPB_FLAG_GOODSOCKNUM;
 			}
 		}
 	}
@@ -264,8 +283,11 @@
 	next:
 	} while(--remaining > 0);
 	tcp_port_rover = rover;
-	if((remaining <= 0) || (tcp_bucket_create(rover) == NULL))
+	tb = NULL;
+	if((remaining <= 0) || ((tb = tcp_bucket_create(rover)) == NULL))
 		rover = 0;
+	if (tb != NULL)
+		tb->flags |= TCPB_FLAG_GOODSOCKNUM;
 	SOCKHASH_UNLOCK();
 
 	return rover;
@@ -543,8 +565,8 @@
 	struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
 	struct sk_buff *buff;
 	struct rtable *rt;
+	u32 daddr, nexthop;
 	int tmp;
-	int mss;
 
 	if (sk->state != TCP_CLOSE) 
 		return(-EISCONN);
@@ -564,7 +586,14 @@
 			printk(KERN_DEBUG "%s forgot to set AF_INET in " __FUNCTION__ "\n", current->comm);
 	}
 
-	tmp = ip_route_connect(&rt, usin->sin_addr.s_addr, sk->saddr,
+	nexthop = daddr = usin->sin_addr.s_addr;
+	if (sk->opt && sk->opt->srr) {
+		if (daddr == 0)
+			return -EINVAL;
+		nexthop = sk->opt->faddr;
+	}
+
+	tmp = ip_route_connect(&rt, nexthop, sk->saddr,
 			       RT_TOS(sk->ip_tos)|sk->localroute, sk->bound_dev_if);
 	if (tmp < 0)
 		return tmp;
@@ -592,6 +621,8 @@
 	 */
 	sk->dport = usin->sin_port;
 	sk->daddr = rt->rt_dst;
+	if (sk->opt && sk->opt->srr)
+		sk->daddr = daddr;
 	if (!sk->saddr)
 		sk->saddr = rt->rt_src;
 	sk->rcv_saddr = sk->saddr;
@@ -601,22 +632,28 @@
 		return -EADDRNOTAVAIL;
 	}
 
-	sk->mtu = rt->u.dst.pmtu;
-	if ((sk->ip_pmtudisc == IP_PMTUDISC_DONT ||
-	     (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
-	      (rt->u.dst.mxlock&(1<<RTAX_MTU)))) &&
-	    rt->u.dst.pmtu > 576 && rt->rt_dst != rt->rt_gateway)
-		sk->mtu = 576;
+	tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
+						   sk->sport, usin->sin_port);
 
-	if (sk->mtu < 64)
-		sk->mtu = 64;	/* Sanity limit */
+	tp->ext_header_len = 0;
+	if (sk->opt)
+		tp->ext_header_len = sk->opt->optlen;
 
-	mss = sk->mtu - sizeof(struct iphdr);
+	/* Reset mss clamp */
+	tp->mss_clamp = ~0;
 
-	tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
-						   sk->sport, usin->sin_port);
+	if ((sk->ip_pmtudisc == IP_PMTUDISC_DONT ||
+	     (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
+	      (rt->u.dst.mxlock&(1<<RTAX_MTU)))) &&
+	    rt->u.dst.pmtu > 576 && rt->rt_dst != rt->rt_gateway) {
+		/* Clamp mss at maximum of 536 and user_mss.
+		   Probably, user ordered to override tiny segment size
+		   in gatewayed case.
+		 */
+		tp->mss_clamp = max(tp->user_mss, 536);
+	}
 
-	tcp_connect(sk, buff, mss);
+	tcp_connect(sk, buff, rt->u.dst.pmtu);
 	return 0;
 }
 
@@ -694,7 +731,6 @@
  */
 static inline void do_pmtu_discovery(struct sock *sk, struct iphdr *ip)
 {
-	int new_mtu; 
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 
 	/* Don't interested in TCP_LISTEN and open_requests (SYN-ACKs
@@ -711,21 +747,19 @@
 	 * route, but I think that's acceptable.
 	 */
 	if (sk->ip_pmtudisc != IP_PMTUDISC_DONT && sk->dst_cache) {
-		new_mtu = sk->dst_cache->pmtu - 
-			(ip->ihl<<2) - tp->tcp_header_len; 
-		if (new_mtu < sk->mss && new_mtu > 0) {
-			sk->mss = new_mtu;
+		if (tp->pmtu_cookie > sk->dst_cache->pmtu &&
+		    !atomic_read(&sk->sock_readers)) {
+			lock_sock(sk); 
+			tcp_sync_mss(sk, sk->dst_cache->pmtu);
+
 			/* Resend the TCP packet because it's  
 			 * clear that the old packet has been
 			 * dropped. This is the new "fast" path mtu
 			 * discovery.
 			 */
-			if (!atomic_read(&sk->sock_readers)) {
-				lock_sock(sk); 
-				tcp_simple_retransmit(sk);
-				release_sock(sk);
-			} /* else let the usual retransmit timer handle it */
-		}
+			tcp_simple_retransmit(sk);
+			release_sock(sk);
+		} /* else let the usual retransmit timer handle it */
 	}
 }
 
@@ -821,8 +855,15 @@
 			return;
 		}
 
+		/* The final ACK of the handshake should be already 
+		 * handled in the new socket context, not here.
+		 * Strictly speaking - an ICMP error for the final
+		 * ACK should set the opening flag, but that is too
+		 * complicated right now. 
+		 */ 
 		if (!th->syn && !th->ack)
 			return;
+
 		req = tcp_v4_search_req(tp, iph, th, &prev); 
 		if (!req)
 			return;
@@ -833,17 +874,33 @@
 				       seq, req->snt_isn);
 			return;
 		}
-		if (req->sk) {	/* not yet accept()ed */
-			sk = req->sk; /* report error in accept */
+		if (req->sk) {	
+			/* 
+			 * Already in ESTABLISHED and a big socket is created,
+			 * set error code there.
+			 * The error will _not_ be reported in the accept(),
+			 * but only with the next operation on the socket after
+			 * accept. 
+			 */
+			sk = req->sk;
 		} else {
+			/* 
+			 * Still in SYN_RECV, just remove it silently.
+			 * There is no good way to pass the error to the newly
+			 * created socket, and POSIX does not want network
+			 * errors returned from accept(). 
+			 */ 
 			tp->syn_backlog--;
 			tcp_synq_unlink(tp, req, prev);
 			req->class->destructor(req);
 			tcp_openreq_free(req);
+			return; 
 		}
-		/* FALL THOUGH */
+		break;
 	case TCP_SYN_SENT:
 	case TCP_SYN_RECV: 
+		if (!th->syn)
+			return; 
 		opening = 1; 
 		break;
 	}
@@ -855,10 +912,13 @@
 			tcp_statistics.TcpAttemptFails++;
 			if (sk->state != TCP_LISTEN)
 				tcp_set_state(sk,TCP_CLOSE);
+			mb(); 
 			sk->error_report(sk);		/* Wake people up to see the error (see connect in sock.c) */
 		}
-	} else	/* Only an error on timeout */
+	} else	{ /* Only an error on timeout */
 		sk->err_soft = icmp_err_convert[code].errno;
+		mb(); 
+	}
 }
 
 /* This routine computes an IPv4 TCP checksum. */
@@ -916,7 +976,7 @@
 				      IPPROTO_TCP,
 				      0); 
 	arg.n_iov = 1;
-	arg.csumoffset = offsetof(struct tcphdr, check) / sizeof(u16); 
+	arg.csumoffset = offsetof(struct tcphdr, check) / 2; 
 
 	ip_send_reply(tcp_socket->sk, skb, &arg, sizeof rth);
 
@@ -950,6 +1010,11 @@
 }
 #endif
 
+/*
+ *	Send a SYN-ACK after having received an ACK. 
+ *	This still operates on a open_request only, not on a big
+ *	socket.
+ */ 
 static void tcp_v4_send_synack(struct sock *sk, struct open_request *req)
 {
 	struct rtable *rt;
@@ -974,7 +1039,7 @@
 		return;
 	}
 
-	mss = (rt->u.dst.pmtu - sizeof(struct iphdr) - sizeof(struct tcphdr));
+	mss = rt->u.dst.pmtu - sizeof(struct iphdr) - sizeof(struct tcphdr);
 
 	skb = tcp_make_synack(sk, &rt->u.dst, req, mss);
 	if (skb) {
@@ -994,6 +1059,9 @@
 	ip_rt_put(rt);
 }
 
+/*
+ *	IPv4 open_request destructor.
+ */ 
 static void tcp_v4_or_free(struct open_request *req)
 {
 	if(!req->sk && req->af.v4_req.opt)
@@ -1016,9 +1084,9 @@
  * Save and compile IPv4 options into the open_request if needed. 
  */
 static inline struct ip_options * 
-tcp_v4_save_options(struct sock *sk, struct sk_buff *skb, 
-		    struct ip_options *opt)
+tcp_v4_save_options(struct sock *sk, struct sk_buff *skb)
 {
+	struct ip_options *opt = &(IPCB(skb)->opt);
 	struct ip_options *dopt = NULL; 
 
 	if (opt && opt->optlen) {
@@ -1052,8 +1120,7 @@
 #define BACKLOG(sk) ((sk)->tp_pinfo.af_tcp.syn_backlog) /* lvalue! */
 #define BACKLOGMAX(sk) sysctl_max_syn_backlog
 
-int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, 
-						__u32 isn)
+int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, __u32 isn)
 {
 	struct tcp_opt tp;
 	struct open_request *req;
@@ -1070,6 +1137,11 @@
 	if (sk->dead) 
 		goto dead; 
 
+	/* Never answer to SYNs send to broadcast or multicast */
+	if (((struct rtable *)skb->dst)->rt_flags & 
+	    (RTCF_BROADCAST|RTCF_MULTICAST))
+		goto drop; 
+
 	/* XXX: Check against a global syn pool counter. */
 	if (BACKLOG(sk) > BACKLOGMAX(sk)) {
 #ifdef CONFIG_SYN_COOKIES
@@ -1094,13 +1166,18 @@
 
 	req->rcv_isn = TCP_SKB_CB(skb)->seq;
  	tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
-	tp.in_mss = 536;
+
+	tp.mss_clamp = 65535;
 	tcp_parse_options(NULL, th, &tp, want_cookie);
-	req->mss = tp.in_mss;
-	if (tp.saw_tstamp) {
-		req->mss -= TCPOLEN_TSTAMP_ALIGNED;
+	if (tp.mss_clamp == 65535)
+		tp.mss_clamp = 576 - sizeof(struct iphdr) - sizeof(struct iphdr);
+
+	if (sk->tp_pinfo.af_tcp.user_mss && sk->tp_pinfo.af_tcp.user_mss < tp.mss_clamp)
+		tp.mss_clamp = sk->tp_pinfo.af_tcp.user_mss;
+	req->mss = tp.mss_clamp;
+
+	if (tp.saw_tstamp)
 		req->ts_recent = tp.rcv_tsval;
-	}
 	req->tstamp_ok = tp.tstamp_ok;
 	req->sack_ok = tp.sack_ok;
 	req->snd_wscale = tp.snd_wscale;
@@ -1120,7 +1197,7 @@
 
 	req->snt_isn = isn;
 
-	req->af.v4_req.opt = tcp_v4_save_options(sk, skb, ptr);
+	req->af.v4_req.opt = tcp_v4_save_options(sk, skb);
 
 	req->class = &or_ipv4;
 	req->retrans = 0;
@@ -1139,7 +1216,6 @@
 		tcp_synq_queue(&sk->tp_pinfo.af_tcp, req);
 	}
 
-	sk->data_ready(sk, 0);
 	return 0;
 
 dead:
@@ -1160,8 +1236,7 @@
  *
  * This function wants to be moved to a common for IPv[46] file. --ANK
  */
-struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, struct sk_buff *skb,
-				      int snd_mss)
+struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, struct sk_buff *skb)
 {
 	struct sock *newsk = sk_alloc(PF_INET, GFP_ATOMIC, 0);
 
@@ -1180,6 +1255,7 @@
 		skb_queue_head_init(&newsk->receive_queue);
 		atomic_set(&newsk->wmem_alloc, 0);
 		skb_queue_head_init(&newsk->write_queue);
+		atomic_set(&newsk->omem_alloc, 0);
 
 		newsk->done = 0;
 		newsk->proc = 0;
@@ -1231,7 +1307,7 @@
 		newtp->copied_seq = req->rcv_isn + 1;
 
 		newtp->saw_tstamp = 0;
-		newtp->in_mss = 536;
+		newtp->mss_clamp = req->mss;
 
 		init_timer(&newtp->probe_timer);
 		newtp->probe_timer.function = &tcp_probe_timer;
@@ -1242,12 +1318,14 @@
 		newtp->urg_data = 0;
 		tcp_synq_init(newtp);
 		newtp->syn_backlog = 0;
+		if (skb->len >= 536)
+			newtp->last_seg_size = skb->len; 
 
 		/* Back to base struct sock members. */
 		newsk->err = 0;
 		newsk->ack_backlog = 0;
 		newsk->max_ack_backlog = SOMAXCONN;
-		newsk->priority = 1;
+		newsk->priority = 0;
 
 		/* IP layer stuff */
 		newsk->timeout = 0;
@@ -1276,14 +1354,6 @@
 		} else {
 			newtp->tcp_header_len = sizeof(struct tcphdr);
 		}
-
-		snd_mss -= newtp->tcp_header_len;
-
-		if (sk->user_mss)
-			snd_mss = min(snd_mss, sk->user_mss);
-
-		newsk->mss = min(req->mss, snd_mss);
-
 	}
 	return newsk;
 }
@@ -1299,8 +1369,6 @@
 	struct ip_options *opt = req->af.v4_req.opt;
 	struct tcp_opt *newtp;
 	struct sock *newsk;
-	int snd_mss;
-	int mtu;
 
 	if (sk->ack_backlog > sk->max_ack_backlog)
 		goto exit; /* head drop */
@@ -1324,12 +1392,7 @@
 		goto exit;
 #endif
 
-	mtu = dst->pmtu;
-	if (mtu < 68) /* XXX: we should turn pmtu disc off when this happens. */
-		mtu = 68;
-	snd_mss = mtu - sizeof(struct iphdr);
-
-	newsk = tcp_create_openreq_child(sk, req, skb, snd_mss);
+	newsk = tcp_create_openreq_child(sk, req, skb);
 	if (!newsk) 
 		goto exit;
 
@@ -1347,15 +1410,22 @@
 	newsk->sport = req->lcl_port;
 #endif
 	newsk->opt = req->af.v4_req.opt;
-	newsk->mtu = mtu;
-
-	if (newsk->rcvbuf < (3 * newsk->mtu))
-		newsk->rcvbuf = min ((3 * newsk->mtu), sysctl_rmem_max);
-	if (newsk->sndbuf < (3 * newsk->mtu))
-		newsk->sndbuf = min ((3 * newsk->mtu), sysctl_wmem_max);
+	newtp->ext_header_len = 0;
+	if (newsk->opt)
+		newtp->ext_header_len = newsk->opt->optlen;
+
+	tcp_sync_mss(newsk, dst->pmtu);
+	newtp->rcv_mss = newtp->mss_clamp;
+
+	/* It would be better to use newtp->mss_clamp here */
+	if (newsk->rcvbuf < (3 * newtp->pmtu_cookie))
+		newsk->rcvbuf = min ((3 * newtp->pmtu_cookie), sysctl_rmem_max);
+	if (newsk->sndbuf < (3 * newtp->pmtu_cookie))
+		newsk->sndbuf = min ((3 * newtp->pmtu_cookie), sysctl_wmem_max);
  
 	tcp_v4_hash(newsk);
 	add_to_prot_sklist(newsk);
+	sk->data_ready(sk, 0); /* Deliver SIGIO */ 
 
 	return newsk;
 
@@ -1373,8 +1443,8 @@
 	if (!req)
 		return;
 	/* Sequence number check required by RFC793 */
-	if (before(TCP_SKB_CB(skb)->seq, req->snt_isn) ||
-	    after(TCP_SKB_CB(skb)->seq, req->snt_isn+1))
+	if (before(TCP_SKB_CB(skb)->seq, req->rcv_isn) ||
+	    after(TCP_SKB_CB(skb)->seq, req->rcv_isn+1))
 		return;
 	tcp_synq_unlink(tp, req, prev);
 	(req->sk ? sk->ack_backlog : tp->syn_backlog)--;
@@ -1461,7 +1531,7 @@
 		sk = nsk;
 	}
 	
-	if (tcp_rcv_state_process(sk, skb, skb->h.th, &(IPCB(skb)->opt), skb->len))
+	if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len))
 		goto reset;
 	release_sock(sk); 
 	return 0;
@@ -1559,7 +1629,7 @@
 
 do_time_wait:
 	if(tcp_timewait_state_process((struct tcp_tw_bucket *)sk,
-				      skb, th, &(IPCB(skb)->opt), skb->len))
+				      skb, th, skb->len))
 		goto no_tcp_socket;
 	goto discard_it;
 }
@@ -1665,6 +1735,8 @@
 	tcp_v4_conn_request,
 	tcp_v4_syn_recv_sock,
 	tcp_v4_get_sock,
+	sizeof(struct iphdr),
+
 	ip_setsockopt,
 	ip_getsockopt,
 	v4_addr2sockaddr,
@@ -1683,7 +1755,7 @@
 
 	tp->rto  = TCP_TIMEOUT_INIT;		/*TCP_WRITE_TIME*/
 	tp->mdev = TCP_TIMEOUT_INIT;
-	tp->in_mss = 536;
+	tp->mss_clamp = ~0;
       
 	/* See draft-stevens-tcpca-spec-01 for discussion of the
 	 * initialization of these values.
@@ -1691,11 +1763,11 @@
 	tp->snd_cwnd = (1 << TCP_CWND_SHIFT);
 	tp->snd_ssthresh = 0x7fffffff;	/* Infinity */
 
-	sk->priority = 1;
 	sk->state = TCP_CLOSE;
 	sk->max_ack_backlog = SOMAXCONN;
-	sk->mtu = 576;
-	sk->mss = 536;
+	tp->rcv_mss = 536; 
+
+	sk->write_space = tcp_write_space; 
 
 	/* Init SYN queue. */
 	tcp_synq_init(tp);
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.1.119/linux/net/ipv4/tcp_output.c	Tue Jul 28 14:21:10 1998
+++ linux/net/ipv4/tcp_output.c	Thu Aug 27 19:33:09 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_output.c,v 1.92 1998/06/19 13:22:44 davem Exp $
+ * Version:	$Id: tcp_output.c,v 1.93 1998/08/26 12:04:32 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -117,7 +117,7 @@
 			 * is never scaled.
 			 */
 			th->window	= htons(tp->rcv_wnd);
-			tcp_syn_build_options((__u32 *)(th + 1), sk->mss,
+			tcp_syn_build_options((__u32 *)(th + 1), tp->mss_clamp,
 					      sysctl_tcp_timestamps,
 					      sysctl_tcp_sack,
 					      sysctl_tcp_window_scaling,
@@ -227,6 +227,65 @@
 	return 0;
 }
 
+/* This function synchronize snd mss to current pmtu/exthdr set.
+
+   tp->user_mss is mss set by user by TCP_MAXSEG. It does NOT counts
+   for TCP options, but includes only bare TCP header.
+
+   tp->mss_clamp is mss negotiated at connection setup.
+   It is minumum of user_mss and mss received with SYN.
+   It also does not include TCP options.
+
+   tp->pmtu_cookie is last pmtu, seen by this function.
+
+   tp->mss_cache is current effective sending mss, including
+   all tcp options except for SACKs. It is evaluated,
+   taking into account current pmtu, but never exceeds
+   tp->mss_clamp.
+
+   NOTE1. rfc1122 clearly states that advertised MSS
+   DOES NOT include either tcp or ip options.
+
+   NOTE2. tp->pmtu_cookie and tp->mss_cache are READ ONLY outside
+   this function.			--ANK (980731)
+ */
+
+int tcp_sync_mss(struct sock *sk, u32 pmtu)
+{
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	int mss_now;
+
+	/* Calculate base mss without TCP options:
+	   It is MMS_S - sizeof(tcphdr) of rfc1122
+	*/
+	mss_now = pmtu - tp->af_specific->net_header_len - sizeof(struct tcphdr);
+
+	/* Clamp it (mss_clamp does not include tcp options) */
+	if (mss_now > tp->mss_clamp)
+		mss_now = tp->mss_clamp;
+
+	/* Now subtract TCP options size, not including SACKs */
+	mss_now -= tp->tcp_header_len - sizeof(struct tcphdr);
+
+	/* Now subtract optional transport overhead */
+	mss_now -= tp->ext_header_len;
+
+	/* It we got too small (or even negative) value,
+	   clamp it by 8 from below. Why 8 ?
+	   Well, it could be 1 with the same success,
+	   but if IP accepted segment of length 1,
+	   it would love 8 even more 8)		--ANK (980731)
+	 */
+	if (mss_now < 8)
+		mss_now = 8;
+
+	/* And store cached results */
+	tp->pmtu_cookie = pmtu;
+	tp->mss_cache = mss_now;
+	return mss_now;
+}
+
+
 /* This routine writes packets to the network.  It advances the
  * send_head.  This happens as incoming acks open up the remote
  * window for us.
@@ -334,7 +393,7 @@
 u32 __tcp_select_window(struct sock *sk, u32 cur_win)
 {
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
-	unsigned int mss = sk->mss;
+	unsigned int mss = tp->mss_cache;
 	int free_space;
 	u32 window;
 
@@ -624,7 +683,7 @@
 		 */
 		if(tp->send_head == skb &&
 		   !sk->nonagle &&
-		   skb->len < (sk->mss >> 1) &&
+		   skb->len < (tp->mss_cache >> 1) &&
 		   tp->packets_out &&
 		   !(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_URG)) {
 			update_send_head(sk);
@@ -738,20 +797,15 @@
 
 	skb->dst = dst_clone(dst);
 
-	if (sk->user_mss)
-		mss = min(mss, sk->user_mss);
-	if (req->tstamp_ok)
-		mss -= TCPOLEN_TSTAMP_ALIGNED;
-
 	/* Don't offer more than they did.
 	 * This way we don't have to memorize who said what.
 	 * FIXME: maybe this should be changed for better performance
 	 * with syncookies.
 	 */
 	req->mss = min(mss, req->mss);
-	if (req->mss < 1) {
-		printk(KERN_DEBUG "initial req->mss below 1\n");
-		req->mss = 1;
+	if (req->mss < 8) {
+		printk(KERN_DEBUG "initial req->mss below 8\n");
+		req->mss = 8;
 	}
 
 	tcp_header_size = (sizeof(struct tcphdr) + TCPOLEN_MSS +
@@ -796,7 +850,7 @@
 	return skb;
 }
 
-void tcp_connect(struct sock *sk, struct sk_buff *buff, int mss)
+void tcp_connect(struct sock *sk, struct sk_buff *buff, int mtu)
 {
 	struct dst_entry *dst = sk->dst_cache;
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
@@ -804,9 +858,6 @@
 	/* Reserve space for headers. */
 	skb_reserve(buff, MAX_HEADER + sk->prot->max_header);
 
-	if (sk->priority == 0)
-		sk->priority = dst->priority;
-
 	tp->snd_wnd = 0;
 	tp->snd_wl1 = 0;
 	tp->snd_wl2 = tp->write_seq;
@@ -821,17 +872,25 @@
 	tp->tcp_header_len = sizeof(struct tcphdr) +
 		(sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);
 
-	mss -= tp->tcp_header_len;
-
-	if (sk->user_mss)
-		mss = min(mss, sk->user_mss);
-
-	if (mss < 1) {
-		printk(KERN_DEBUG "initial sk->mss below 1\n");
-		mss = 1;	/* Sanity limit */
-	}
-
-	sk->mss = mss;
+	/* If user gave his TCP_MAXSEG, record it to clamp */
+	if (tp->user_mss)
+		tp->mss_clamp = tp->user_mss;
+	tcp_sync_mss(sk, mtu);
+
+	/* Now unpleasant action: if initial pmtu is too low
+	   set lower clamp. I am not sure that it is good.
+	   To be more exact, I do not think that clamping at value, which
+	   is apparently transient and may improve in future is good idea.
+	   It would be better to wait until peer will returns its MSS
+	   (probably 65535 too) and now advertise something sort of 65535
+	   or at least first hop device mtu. Is it clear, what I mean?
+	   We should tell peer what maximal mss we expect to RECEIVE,
+	   it has nothing to do with pmtu.
+	   I am afraid someone will be confused by such huge value.
+	                                                   --ANK (980731)
+	 */
+	if (tp->mss_cache + tp->tcp_header_len - sizeof(struct tcphdr) < tp->mss_clamp )
+		tp->mss_clamp = tp->mss_cache + tp->tcp_header_len - sizeof(struct tcphdr);
 
 	TCP_SKB_CB(buff)->flags = TCPCB_FLAG_SYN;
 	TCP_SKB_CB(buff)->sacked = 0;
@@ -842,7 +901,7 @@
 	tp->snd_nxt = TCP_SKB_CB(buff)->end_seq;
 
 	tp->window_clamp = dst->window;
-	tcp_select_initial_window(sock_rspace(sk)/2,sk->mss,
+	tcp_select_initial_window(sock_rspace(sk)/2,tp->mss_clamp,
 		&tp->rcv_wnd,
 		&tp->window_clamp,
 		sysctl_tcp_window_scaling,
diff -u --recursive --new-file v2.1.119/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.1.119/linux/net/ipv4/udp.c	Thu Aug 27 19:56:31 1998
+++ linux/net/ipv4/udp.c	Sun Aug 30 14:32:12 1998
@@ -5,7 +5,7 @@
  *
  *		The User Datagram Protocol (UDP).
  *
- * Version:	$Id: udp.c,v 1.57 1998/05/14 06:32:44 davem Exp $
+ * Version:	$Id: udp.c,v 1.61 1998/08/29 17:11:10 freitag Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -59,6 +59,8 @@
  *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma.
  *		Melvin Smith	:	Check msg_name not msg_namelen in sendto(),
  *					return ENOTCONN for unconnected sockets (POSIX)
+ *		Janos Farkas	:	don't deliver multi/broadcasts to a different
+ *					bound-to-device socket
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -80,7 +82,7 @@
      MUST provide facility for checksumming (OK)
      MAY allow application to control checksumming (OK)
      MUST default to checksumming on (OK)
-     MUST discard silently datagrams with bad csums (OK)
+     MUST discard silently datagrams with bad csums (OK, except during debugging)
    4.1.3.5 (UDP Multihoming)
      MUST allow application to specify source address (OK)
      SHOULD be able to communicate the chosen src addr up to application
@@ -93,14 +95,12 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <linux/types.h>
-#include <linux/sched.h>
 #include <linux/fcntl.h>
 #include <linux/socket.h>
 #include <linux/sockios.h>
 #include <linux/in.h>
 #include <linux/errno.h>
 #include <linux/timer.h>
-#include <linux/termios.h>
 #include <linux/mm.h>
 #include <linux/config.h>
 #include <linux/inet.h>
@@ -108,14 +108,12 @@
 #include <net/snmp.h>
 #include <net/ip.h>
 #include <net/protocol.h>
-#include <net/tcp.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
 #include <net/udp.h>
 #include <net/icmp.h>
 #include <net/route.h>
 #include <net/checksum.h>
-#include <linux/ipsec.h>
 
 /*
  *	Snmp MIB for the UDP layer
@@ -447,7 +445,8 @@
 					     unsigned short num,
 					     unsigned long raddr,
 					     unsigned short rnum,
-					     unsigned long laddr)
+					     unsigned long laddr,
+					     int dif)
 {
 	struct sock *s = sk;
 	unsigned short hnum = ntohs(num);
@@ -455,8 +454,9 @@
 		if ((s->num != hnum)					||
 		    (s->dead && (s->state == TCP_CLOSE))		||
 		    (s->daddr && s->daddr!=raddr)			||
-		    (s->dport != rnum && s->dport != 0) ||
-		    (s->rcv_saddr  && s->rcv_saddr != laddr))
+		    (s->dport != rnum && s->dport != 0)			||
+		    (s->rcv_saddr  && s->rcv_saddr != laddr)		||
+		    (s->bound_dev_if && s->bound_dev_if != dif))
 			continue;
 		break;
   	}
@@ -619,7 +619,8 @@
 	struct ipcm_cookie ipc;
 	struct udpfakehdr ufh;
 	struct rtable *rt = NULL;
-	int free = 0, localroute = 0;
+	int free = 0;
+	int connected = 0;
 	u32 daddr;
 	u8  tos;
 	int err;
@@ -674,27 +675,15 @@
 		ufh.uh.dest = usin->sin_port;
 		if (ufh.uh.dest == 0)
 			return -EINVAL;
-		/* XXX: is a one-behind cache for the dst_entry worth it?
-
-		   Nope. ip_route_output is slower than nothing, but it
-		   is enough fast to forget about caching its results.
-		   Really, checking route validity in general case
-		   is not much faster complete lookup.
-		   It was main reason why I removed it from 2.1.
-		   The second reason was that idle sockets held
-		   a lot of stray destinations.		--ANK
-		 */
 	} else {
 		if (sk->state != TCP_ESTABLISHED)
 			return -ENOTCONN;
 		ufh.daddr = sk->daddr;
 		ufh.uh.dest = sk->dport;
-
-		/*
-		   BUGGG Khm... And who will validate it? Fixing it fastly...
-		                                                        --ANK
+		/* Open fast path for connected socket.
+		   Route will not be used, if at least one option is set.
 		 */
-		rt = (struct rtable *)dst_check(&sk->dst_cache, 0);
+		connected = 1;
   	}
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 	if (msg->msg_flags&MSG_PROXY) {
@@ -710,6 +699,7 @@
 		ufh.uh.source = from->sin_port;
 		if (ipc.addr == 0)
 			ipc.addr = sk->saddr;
+		connected = 0;
 	} else
 #endif
 	{
@@ -725,6 +715,7 @@
 			return err;
 		if (ipc.opt)
 			free = 1;
+		connected = 0;
 	}
 	if (!ipc.opt)
 		ipc.opt = sk->opt;
@@ -736,12 +727,13 @@
 		if (!daddr)
 			return -EINVAL;
 		daddr = ipc.opt->faddr;
+		connected = 0;
 	}
 	tos = RT_TOS(sk->ip_tos);
 	if (sk->localroute || (msg->msg_flags&MSG_DONTROUTE) || 
 	    (ipc.opt && ipc.opt->is_strictroute)) {
 		tos |= RTO_ONLINK;
-		rt = NULL; /* sorry */
+		connected = 0;
 	}
 
 	if (MULTICAST(daddr)) {
@@ -749,8 +741,12 @@
 			ipc.oif = sk->ip_mc_index;
 		if (!ufh.saddr)
 			ufh.saddr = sk->ip_mc_addr;
+		connected = 0;
 	}
 
+	if (connected)
+		rt = (struct rtable*)dst_clone(sk->dst_cache);
+
 	if (rt == NULL) {
 		err = ip_route_output(&rt, daddr, ufh.saddr,
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
@@ -759,7 +755,6 @@
 			 tos, ipc.oif);
 		if (err) 
 			goto out;
-		localroute = 1;
 
 		err = -EACCES;
 		if (rt->rt_flags&RTCF_BROADCAST && !sk->broadcast) 
@@ -777,17 +772,13 @@
 
 	/* RFC1122: OK.  Provides the checksumming facility (MUST) as per */
 	/* 4.1.3.4. It's configurable by the application via setsockopt() */
-	/* (MAY) and it defaults to on (MUST).  Almost makes up for the */
-	/* violation above. -- MS */
+	/* (MAY) and it defaults to on (MUST). */
 
-	lock_sock(sk);
 	err = ip_build_xmit(sk,sk->no_check ? udp_getfrag_nosum : udp_getfrag,
 			    &ufh, ulen, &ipc, rt, msg->msg_flags);
-	release_sock(sk);
 
 out:
-	if (localroute)
-		ip_rt_put(rt);
+	ip_rt_put(rt);
 	if (free)
 		kfree(ipc.opt);
 	if (!err) {
@@ -822,7 +813,9 @@
 			if (sk->state == TCP_LISTEN)
 				return(-EINVAL);
 			amount = 0;
-			/* N.B. Is this interrupt safe?? */
+			/* N.B. Is this interrupt safe??
+			   -> Yes. Interrupts do not remove skbs. --ANK (980725)
+			 */
 			skb = skb_peek(&sk->receive_queue);
 			if (skb != NULL) {
 				/*
@@ -841,6 +834,9 @@
 	return(0);
 }
 
+#if defined(CONFIG_FILTER) || !defined(HAVE_CSUM_COPY_USER) 
+#undef CONFIG_UDP_DELAY_CSUM
+#endif
 
 /*
  * 	This should be easy, if there is something there we
@@ -848,7 +844,7 @@
  */
 
 int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
-	     int noblock, int flags, int *addr_len)
+		int noblock, int flags, int *addr_len)
 {
   	struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
   	struct sk_buff *skb;
@@ -880,18 +876,36 @@
 		goto out;
   
   	copied = skb->len - sizeof(struct udphdr);
-	if (copied > len)
-	{
+	if (copied > len) {
 		copied = len;
 		msg->msg_flags |= MSG_TRUNC;
 	}
 
-  	/*
-  	 *	FIXME : should use udp header size info value 
-  	 */
-  	 
+#ifndef CONFIG_UDP_DELAY_CSUM
 	err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
 					copied);
+#else
+	if (sk->no_check || skb->ip_summed==CHECKSUM_UNNECESSARY) {
+		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
+					      copied);
+	} else if (copied > msg->msg_iov[0].iov_len || (msg->msg_flags&MSG_TRUNC)) {
+		if (csum_fold(csum_partial(skb->h.raw, ntohs(skb->h.uh->len), skb->csum))) 
+			goto csum_copy_err;
+		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
+					      copied);
+	} else {
+		unsigned int csum;
+
+		err = 0;
+		csum = csum_partial(skb->h.raw, sizeof(struct udphdr), skb->csum);
+		csum = csum_and_copy_to_user((char*)&skb->h.uh[1], msg->msg_iov[0].iov_base, 
+					     copied, csum, &err);
+		if (err)
+			goto out_free;
+		if (csum_fold(csum)) 
+			goto csum_copy_err;
+	}
+#endif
 	if (err)
 		goto out_free;
 	sk->stamp=skb->stamp;
@@ -928,6 +942,18 @@
   	skb_free_datagram(sk, skb);
 out:
   	return err;
+
+#ifdef CONFIG_UDP_DELAY_CSUM
+csum_copy_err:
+	udp_statistics.UdpInErrors++;
+	skb_free_datagram(sk, skb);
+
+	/* 
+	 * Error for blocking case is chosen to masquerade
+   	 * as some normal condition.
+	 */
+	return (msg->msg_flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;	
+#endif
 }
 
 int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
@@ -986,29 +1012,16 @@
 
 static void udp_close(struct sock *sk, unsigned long timeout)
 {
-	lock_sock(sk);
+	/* See for explanation: raw_close in ipv4/raw.c */
 	sk->state = TCP_CLOSE;
-	if(uh_cache_sk == sk)
-		uh_cache_sk = NULL;
-	sk->dead = 1;
-	release_sock(sk);
 	udp_v4_unhash(sk);
+	sk->dead = 1;
 	destroy_sock(sk);
 }
 
 static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
 {
 	/*
-	 *	Check the security clearance
-	 */
-	 
-	if(!ipsec_sk_policy(sk,skb))
-	{	
-		kfree_skb(skb);
-		return(0);
-	}
-	 
-	/*
 	 *	Charge it to the socket, dropping if the queue is full.
 	 */
 
@@ -1026,10 +1039,6 @@
 
 static inline void udp_deliver(struct sock *sk, struct sk_buff *skb)
 {
-	if (atomic_read(&sk->sock_readers)) {
-		__skb_queue_tail(&sk->back_log, skb);
-		return;
-	}
 	udp_queue_rcv_skb(sk, skb);
 }
 
@@ -1043,9 +1052,11 @@
 				 u32 saddr, u32 daddr)
 {
 	struct sock *sk;
+	int dif;
 
 	sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
-	sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr);
+	dif = skb->dev->ifindex;
+	sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr, dif);
 	if (sk) {
 		struct sock *sknext = NULL;
 
@@ -1053,7 +1064,7 @@
 			struct sk_buff *skb1 = skb;
 
 			sknext = udp_v4_mcast_next(sk->next, uh->dest, saddr,
-						   uh->source, daddr);
+						   uh->source, daddr, dif);
 			if(sknext)
 				skb1 = skb_clone(skb, GFP_ATOMIC);
 
@@ -1113,7 +1124,8 @@
 	 */
 	 
   	uh = skb->h.uh;
-  	
+	__skb_pull(skb, skb->h.raw - skb->data);
+
   	ip_statistics.IpInDelivers++;
 
 	/*
@@ -1121,44 +1133,31 @@
 	 */
 	 
 	ulen = ntohs(uh->len);
-	
-	if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh)) {
+
+	if (ulen > len || ulen < sizeof(*uh)) {
 		NETDEBUG(printk(KERN_DEBUG "UDP: short packet: %d/%d\n", ulen, len));
 		udp_statistics.UdpInErrors++;
 		kfree_skb(skb);
 		return(0);
 	}
+	skb_trim(skb, ulen);
 
+#ifndef CONFIG_UDP_DELAY_CSUM
 	if (uh->check &&
-	    (((skb->ip_summed==CHECKSUM_HW)&&udp_check(uh,len,saddr,daddr,skb->csum)) ||
+	    (((skb->ip_summed==CHECKSUM_HW)&&udp_check(uh,ulen,saddr,daddr,skb->csum)) ||
 	     ((skb->ip_summed==CHECKSUM_NONE) &&
-	      (udp_check(uh,len,saddr,daddr, csum_partial((char*)uh, len, 0)))))) {
-		/* <mea@utu.fi> wants to know, who sent it, to
-		   go and stomp on the garbage sender... */
-
-		/* RFC1122: OK.  Discards the bad packet silently (as far as */
-		/* the network is concerned, anyway) as per 4.1.3.4 (MUST). */
-
-		NETDEBUG(printk(KERN_DEBUG "UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n",
-		       ntohl(saddr),ntohs(uh->source),
-		       ntohl(daddr),ntohs(uh->dest),
-		       ulen));
-		udp_statistics.UdpInErrors++;
-		kfree_skb(skb);
-		return(0);
-	}
-
-
-	len = ulen;
-
-	/*
-	 *	FIXME:
-	 *	Trimming things wrongly. We must adjust the base/end to allow
-	 *	for the headers we keep!
-	 *		 --ANK 
-	 */
-	skb_trim(skb,len);
-
+	      (udp_check(uh,ulen,saddr,daddr, csum_partial((char*)uh, ulen, 0)))))) 
+		goto csum_error;
+#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);
+#endif
 
 	if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
 		return udp_v4_mcast_deliver(skb, uh, saddr, daddr);
@@ -1173,6 +1172,11 @@
 	sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, skb->dev->ifindex);
 	
 	if (sk == NULL) {
+#ifdef CONFIG_UDP_DELAY_CSUM
+		if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
+		    csum_fold(csum_partial((char*)uh, ulen, skb->csum))) 
+			goto csum_error;
+#endif
   		udp_statistics.UdpNoPorts++;
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 
@@ -1185,6 +1189,19 @@
   	}
 	udp_deliver(sk, skb);
 	return 0;
+
+csum_error:
+	/* 
+	 * RFC1122: OK.  Discards the bad packet silently (as far as 
+	 * the network is concerned, anyway) as per 4.1.3.4 (MUST). 
+	 */
+	NETDEBUG(printk(KERN_DEBUG "UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n",
+			ntohl(saddr),ntohs(uh->source),
+			ntohl(daddr),ntohs(uh->dest),
+			ulen));
+	udp_statistics.UdpInErrors++;
+	kfree_skb(skb);
+	return(0);
 }
 
 struct proto udp_prot = {
@@ -1214,7 +1231,7 @@
 	udp_v4_verify_bind,		/* verify_bind */
 	128,				/* max_header */
 	0,				/* retransmits */
-	"UDP",				/* name */
+ 	"UDP",				/* name */
 	0,				/* inuse */
 	0				/* highestinuse */
 };
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.1.119/linux/net/ipv6/addrconf.c	Tue Jul 28 14:21:10 1998
+++ linux/net/ipv6/addrconf.c	Thu Aug 27 19:33:09 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: addrconf.c,v 1.43 1998/07/15 05:05:32 davem Exp $
+ *	$Id: addrconf.c,v 1.45 1998/08/26 12:04:41 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -38,6 +38,7 @@
 #ifdef CONFIG_SYSCTL
 #include <linux/sysctl.h>
 #endif
+#include <linux/delay.h>
 
 #include <linux/proc_fs.h>
 #include <net/sock.h>
@@ -53,7 +54,6 @@
 #include <linux/rtnetlink.h>
 
 #include <asm/uaccess.h>
-#include <asm/delay.h>
 
 /* Set to 3 to get tracing... */
 #define ACONF_DEBUG 2
@@ -100,7 +100,7 @@
 {
 	0,				/* forwarding		*/
 	IPV6_DEFAULT_HOPLIMIT,		/* hop limit		*/
-	576,				/* mtu			*/
+	IPV6_MIN_MTU,			/* mtu			*/
 	1,				/* accept RAs		*/
 	1,				/* accept redirects	*/
 	1,				/* autoconfiguration	*/
@@ -114,7 +114,7 @@
 {
 	0,				/* forwarding		*/
 	IPV6_DEFAULT_HOPLIMIT,		/* hop limit		*/
-	576,				/* mtu			*/
+	IPV6_MIN_MTU,			/* mtu			*/
 	1,				/* accept RAs		*/
 	1,				/* accept redirects	*/
 	1,				/* autoconfiguration	*/
@@ -185,7 +185,7 @@
 	struct inet6_dev *ndev, **bptr, *iter;
 	int hash;
 
-	if (dev->mtu < 576)
+	if (dev->mtu < IPV6_MIN_MTU)
 		return NULL;
 
 	ndev = kmalloc(sizeof(struct inet6_dev), gfp_any());
@@ -548,7 +548,6 @@
 		      unsigned long expires, unsigned flags)
 {
 	struct in6_rtmsg rtmsg;
-	int err;
 
 	memset(&rtmsg, 0, sizeof(rtmsg));
 	memcpy(&rtmsg.rtmsg_dst, pfx, sizeof(struct in6_addr));
@@ -566,7 +565,7 @@
 	if (dev->type == ARPHRD_SIT && (dev->flags&IFF_POINTOPOINT))
 		rtmsg.rtmsg_flags |= RTF_NONEXTHOP;
 
-	ip6_route_add(&rtmsg, &err);
+	ip6_route_add(&rtmsg);
 }
 
 /* Create "default" multicast route to the interface */
@@ -574,7 +573,6 @@
 static void addrconf_add_mroute(struct device *dev)
 {
 	struct in6_rtmsg rtmsg;
-	int err;
 
 	memset(&rtmsg, 0, sizeof(rtmsg));
 	ipv6_addr_set(&rtmsg.rtmsg_dst,
@@ -584,13 +582,12 @@
 	rtmsg.rtmsg_ifindex = dev->ifindex;
 	rtmsg.rtmsg_flags = RTF_UP|RTF_ADDRCONF;
 	rtmsg.rtmsg_type = RTMSG_NEWROUTE;
-	ip6_route_add(&rtmsg, &err);
+	ip6_route_add(&rtmsg);
 }
 
 static void sit_route_add(struct device *dev)
 {
 	struct in6_rtmsg rtmsg;
-	int err;
 
 	memset(&rtmsg, 0, sizeof(rtmsg));
 
@@ -602,7 +599,7 @@
 	rtmsg.rtmsg_flags	= RTF_UP|RTF_NONEXTHOP;
 	rtmsg.rtmsg_ifindex	= dev->ifindex;
 
-	ip6_route_add(&rtmsg, &err);
+	ip6_route_add(&rtmsg);
 }
 
 static void addrconf_add_lroute(struct device *dev)
@@ -690,13 +687,12 @@
 	else
 		rt_expires = jiffies + valid_lft * HZ;
 
-	rt = rt6_lookup(&pinfo->prefix, NULL, dev->ifindex, RTF_LINKRT);
+	rt = rt6_lookup(&pinfo->prefix, NULL, dev->ifindex, 1);
 
 	if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
 		if (rt->rt6i_flags&RTF_EXPIRES) {
 			if (pinfo->onlink == 0 || valid_lft == 0) {
 				ip6_del_rt(rt);
-				rt = NULL;
 			} else {
 				rt->rt6i_expires = rt_expires;
 			}
@@ -705,6 +701,8 @@
 		addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
 				      dev, rt_expires, RTF_ADDRCONF|RTF_EXPIRES);
 	}
+	if (rt)
+		dst_release(&rt->u.dst);
 
 	/* Try to figure out our local address for this prefix */
 
@@ -1118,11 +1116,17 @@
 		break;
 
 	case NETDEV_CHANGEMTU:
-		/* BUGGG... Should scan FIB to change pmtu on routes. --ANK */
-		if (dev->mtu >= 576)
+		if (dev->mtu >= IPV6_MIN_MTU) {
+			struct inet6_dev *idev;
+
+			if ((idev = ipv6_find_idev(dev)) == NULL)
+				break;
+			idev->cnf.mtu6 = dev->mtu;
+			rt6_mtu_change(dev, dev->mtu);
 			break;
+		}
 
-		/* MTU falled under 576. Stop IPv6 on this interface. */
+		/* MTU falled under IPV6_MIN_MTU. Stop IPv6 on this interface. */
 
 	case NETDEV_DOWN:
 	case NETDEV_UNREGISTER:
@@ -1240,7 +1244,6 @@
 		add_timer(&ifp->timer);
 	} else {
 		struct in6_rtmsg rtmsg;
-		int err;
 
 		printk(KERN_DEBUG "%s: no IPv6 routers present\n",
 		       ifp->idev->dev->name);
@@ -1253,7 +1256,7 @@
 
 		rtmsg.rtmsg_ifindex = ifp->idev->dev->ifindex;
 
-		ip6_route_add(&rtmsg, &err);
+		ip6_route_add(&rtmsg);
 	}
 }
 
@@ -1501,7 +1504,7 @@
 }
 
 static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
-			    pid_t pid, u32 seq, int event)
+			     u32 pid, u32 seq, int event)
 {
 	struct ifaddrmsg *ifm;
 	struct nlmsghdr  *nlh;
@@ -1659,8 +1662,11 @@
 
 		addrconf_forward_change(idev);
 
-		if (*valp)
+		if (*valp) {
+			start_bh_atomic();
 			rt6_purge_dflt_routers(0);
+			end_bh_atomic();
+		}
 	}
 
         return ret;
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.1.119/linux/net/ipv6/af_inet6.c	Tue Jun 23 10:01:31 1998
+++ linux/net/ipv6/af_inet6.c	Thu Aug 27 19:33:09 1998
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/af_inet.c
  *
- *	$Id: af_inet6.c,v 1.36 1998/06/10 07:29:25 davem Exp $
+ *	$Id: af_inet6.c,v 1.37 1998/08/26 12:04:45 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -64,6 +64,7 @@
 extern int tcp6_get_info(char *, char **, off_t, int, int);
 extern int udp6_get_info(char *, char **, off_t, int, int);
 extern int afinet6_get_info(char *, char **, off_t, int, int);
+extern int afinet6_get_snmp(char *, char **, off_t, int, int);
 #endif
 
 #ifdef CONFIG_SYSCTL
@@ -243,10 +244,49 @@
 
 static int inet6_release(struct socket *sock, struct socket *peer)
 {
+	struct sock *sk = sock->sk;
+
+	if (sk == NULL)
+		return -EINVAL;
+
+	/* Free mc lists */
+	ipv6_sock_mc_close(sk);
+
+	/* Huh! MOD_DEC_USE_COUNT was here :-(
+	   It is impossible by two reasons: socket destroy
+	   may be delayed and inet_release may sleep and
+	   return to nowhere then. It should be moved to
+	   inet6_destroy_sock(), but we have no explicit constructor :-(
+	                                    --ANK (980802)
+	 */
 	MOD_DEC_USE_COUNT;
 	return inet_release(sock, peer);
 }
 
+int inet6_destroy_sock(struct sock *sk)
+{
+	struct sk_buff *skb;
+	struct ipv6_txoptions *opt;
+
+	/*
+	 *	Release destination entry
+	 */
+
+	dst_release(xchg(&sk->dst_cache,NULL));
+
+	/* Release rx options */
+
+	if ((skb = xchg(&sk->net_pinfo.af_inet6.pktoptions, NULL)) != NULL)
+		kfree_skb(skb);
+
+	/* Free tx options */
+
+	if ((opt = xchg(&sk->net_pinfo.af_inet6.opt, NULL)) != NULL)
+		sock_kfree_s(sk, opt, opt->tot_len);
+
+	return 0;
+}
+
 /*
  *	This does both peername and sockname.
  */
@@ -412,6 +452,12 @@
 	0, &proc_net_inode_operations,
 	afinet6_get_info
 };
+static struct proc_dir_entry proc_net_snmp6 = {
+	PROC_NET_SNMP6, 5, "snmp6",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	afinet6_get_snmp
+};
 #endif	/* CONFIG_PROC_FS */
 
 #ifdef MODULE
@@ -445,7 +491,7 @@
 
 	printk(KERN_INFO "IPv6 v0.2 for NET3.037\n");
 
-	if (sizeof(struct ipv6_options) > sizeof(dummy_skb->cb))
+	if (sizeof(struct inet6_skb_parm) > sizeof(dummy_skb->cb))
 	{
 		printk(KERN_CRIT "inet6_proto_init: size fault\n");
 #ifdef MODULE
@@ -490,6 +536,7 @@
 	proc_net_register(&proc_net_tcp6);
 	proc_net_register(&proc_net_udp6);
 	proc_net_register(&proc_net_sockstat6);
+	proc_net_register(&proc_net_snmp6);
 #endif
 
 	/* Now the userspace is allowed to create INET6 sockets. */
@@ -526,6 +573,7 @@
 	proc_net_unregister(proc_net_tcp6.low_ino);
 	proc_net_unregister(proc_net_udp6.low_ino);
 	proc_net_unregister(proc_net_sockstat6.low_ino);
+	proc_net_unregister(proc_net_snmp6.low_ino);
 #endif
 	/* Cleanup code parts. */
 	sit_cleanup();
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/datagram.c linux/net/ipv6/datagram.c
--- v2.1.119/linux/net/ipv6/datagram.c	Thu Mar 26 15:57:13 1998
+++ linux/net/ipv6/datagram.c	Thu Aug 27 19:33:09 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: datagram.c,v 1.14 1998/03/20 09:12:15 davem Exp $
+ *	$Id: datagram.c,v 1.15 1998/08/26 12:04:47 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -32,48 +32,72 @@
 int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-	struct ipv6_options *opt = (struct ipv6_options *) skb->cb;
-	
-	if (np->rxinfo) {
+	struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb;
+
+	if (np->rxopt.bits.rxinfo) {
 		struct in6_pktinfo src_info;
 
-		src_info.ipi6_ifindex = skb->dev->ifindex;
+		src_info.ipi6_ifindex = opt->iif;
 		ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr);
 		put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
 	}
 
-	if (np->rxhlim) {
+	if (np->rxopt.bits.rxhlim) {
 		int hlim = skb->nh.ipv6h->hop_limit;
 		put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
 	}
 
-	if (opt->srcrt) {
-		int hdrlen = sizeof(struct rt0_hdr) + (opt->srcrt->hdrlen << 3);
-
-		put_cmsg(msg, SOL_IPV6, IPV6_RXSRCRT, hdrlen, opt->srcrt);
+	if (np->rxopt.bits.hopopts && opt->hop) {
+		u8 *ptr = skb->nh.raw + opt->hop;
+		put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
+	}
+	if (np->rxopt.bits.dstopts && opt->dst0) {
+		u8 *ptr = skb->nh.raw + opt->dst0;
+		put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr);
+	}
+	if (np->rxopt.bits.srcrt && opt->srcrt) {
+		struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt);
+		put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
+	}
+	if (np->rxopt.bits.authhdr && opt->auth) {
+		u8 *ptr = skb->nh.raw + opt->auth;
+		put_cmsg(msg, SOL_IPV6, IPV6_AUTHHDR, (ptr[1]+1)<<2, ptr);
+	}
+	if (np->rxopt.bits.dstopts && opt->dst1) {
+		u8 *ptr = skb->nh.raw + opt->dst1;
+		put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr);
 	}
 	return 0;
 }
 
 int datagram_send_ctl(struct msghdr *msg, int *oif,
-		      struct in6_addr **src_addr, struct ipv6_options *opt, 
+		      struct in6_addr **src_addr, struct ipv6_txoptions *opt,
 		      int *hlimit)
 {
 	struct in6_pktinfo *src_info;
 	struct cmsghdr *cmsg;
 	struct ipv6_rt_hdr *rthdr;
+	struct ipv6_opt_hdr *hdr;
 	int len;
 	int err = 0;
 
 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+
+		if ((unsigned long)(((char*)cmsg - (char*)msg->msg_control)
+				    + cmsg->cmsg_len) > msg->msg_controllen) {
+			err = -EINVAL;
+			goto exit_f;
+		}
+
 		if (cmsg->cmsg_level != SOL_IPV6) {
-			printk(KERN_DEBUG "invalid cmsg_level %d\n", cmsg->cmsg_level);
+			if (net_ratelimit())
+				printk(KERN_DEBUG "invalid cmsg_level %d\n", cmsg->cmsg_level);
 			continue;
 		}
 
 		switch (cmsg->cmsg_type) {
  		case IPV6_PKTINFO:
- 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo))) {
+ 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) {
 				err = -EINVAL;
 				goto exit_f;
 			}
@@ -100,14 +124,77 @@
 			}
 
 			break;
-			
-		case IPV6_RXSRCRT:
+
+		case IPV6_HOPOPTS:
+                        if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
+				err = -EINVAL;
+				goto exit_f;
+			}
+
+			hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
+			len = ((hdr->hdrlen + 1) << 3);
+			if (cmsg->cmsg_len < CMSG_LEN(len)) {
+				err = -EINVAL;
+				goto exit_f;
+			}
+			if (!capable(CAP_NET_RAW)) {
+				err = -EPERM;
+				goto exit_f;
+			}
+			opt->opt_nflen += len;
+			opt->hopopt = hdr;
+			break;
+
+		case IPV6_DSTOPTS:
+                        if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
+				err = -EINVAL;
+				goto exit_f;
+			}
+
+			hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
+			len = ((hdr->hdrlen + 1) << 3);
+			if (cmsg->cmsg_len < CMSG_LEN(len)) {
+				err = -EINVAL;
+				goto exit_f;
+			}
+			if (!capable(CAP_NET_RAW)) {
+				err = -EPERM;
+				goto exit_f;
+			}
+			if (opt->dst1opt) {
+				err = -EINVAL;
+				goto exit_f;
+			}
+			opt->opt_flen += len;
+			opt->dst1opt = hdr;
+			break;
+
+		case IPV6_AUTHHDR:
+                        if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
+				err = -EINVAL;
+				goto exit_f;
+			}
+
+			hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
+			len = ((hdr->hdrlen + 2) << 2);
+			if (cmsg->cmsg_len < CMSG_LEN(len)) {
+				err = -EINVAL;
+				goto exit_f;
+			}
+			if (len & ~7) {
+				err = -EINVAL;
+				goto exit_f;
+			}
+			opt->opt_flen += len;
+			opt->auth = hdr;
+			break;
+
+		case IPV6_RTHDR:
                         if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) {
 				err = -EINVAL;
 				goto exit_f;
 			}
 
-			len = cmsg->cmsg_len - sizeof(struct cmsghdr);
 			rthdr = (struct ipv6_rt_hdr *)CMSG_DATA(cmsg);
 
 			/*
@@ -118,7 +205,9 @@
 				goto exit_f;
 			}
 
-			if (((rthdr->hdrlen + 1) << 3) < len) {
+			len = ((rthdr->hdrlen + 1) << 3);
+
+                        if (cmsg->cmsg_len < CMSG_LEN(len)) {
 				err = -EINVAL;
 				goto exit_f;
 			}
@@ -128,12 +217,21 @@
 				err = -EINVAL;
 				goto exit_f;
 			}
-			
-			opt->opt_nflen += ((rthdr->hdrlen + 1) << 3);
+
+			opt->opt_nflen += len;
 			opt->srcrt = rthdr;
 
+			if (opt->dst1opt) {
+				int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3);
+
+				opt->opt_nflen += dsthdrlen;
+				opt->dst0opt = opt->dst1opt;
+				opt->dst1opt = NULL;
+				opt->opt_flen -= dsthdrlen;
+			}
+
 			break;
-			
+
 		case IPV6_HOPLIMIT:
 			if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
 				err = -EINVAL;
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/exthdrs.c linux/net/ipv6/exthdrs.c
--- v2.1.119/linux/net/ipv6/exthdrs.c	Thu May  7 22:51:56 1998
+++ linux/net/ipv6/exthdrs.c	Thu Aug 27 19:33:09 1998
@@ -5,8 +5,9 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *	Andi Kleen		<ak@muc.de>
+ *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
  *
- *	$Id: exthdrs.c,v 1.6 1998/04/30 16:24:20 freitag Exp $
+ *	$Id: exthdrs.c,v 1.7 1998/08/26 12:04:49 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -37,55 +38,192 @@
 
 #include <asm/uaccess.h>
 
-#define swap(a,b) do { typeof (a) tmp; tmp = (a); (a) = (b); (b) = (tmp); } while(0)
+/*
+ *	Parsing inbound headers.
+ *
+ *	Parsing function "func" returns pointer to the place,
+ *	where next nexthdr value is stored or NULL, if parsing
+ *	failed. It should also update skb->h.
+ */
+
+struct hdrtype_proc
+{
+	int	type;
+	u8*	(*func) (struct sk_buff **, u8 *ptr);
+};
 
 /*
- *	inbound
+ *	Parsing tlv encoded headers.
+ *
+ *	Parsing function "func" returns 1, if parsing succeed
+ *	and 0, if it failed.
+ *	It MUST NOT touch skb->h.
  */
-#if 0
-int ipv6_routing_header(struct sk_buff **skb_ptr, struct device *dev,
-			__u8 *nhptr, struct ipv6_options *opt)
+
+struct tlvtype_proc
+{
+	int	type;
+	int	(*func) (struct sk_buff *, __u8 *ptr);
+};
+
+/*********************
+  Generic functions
+ *********************/
+
+/* An unknown option is detected, decide what to do */
+
+int ip6_tlvopt_unknown(struct sk_buff *skb, u8 *opt)
+{
+	switch ((opt[0] & 0xC0) >> 6) {
+	case 0: /* ignore */
+		return 1;
+		
+	case 1: /* drop packet */
+		break;
+
+	case 3: /* Send ICMP if not a multicast address and drop packet */
+		/* Actually, it is redundant check. icmp_send
+		   will recheck in any case.
+		 */
+		if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr))
+			break;
+	case 2: /* send ICMP PARM PROB regardless and drop packet */
+		icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, opt);
+		return 0;
+	};
+
+	kfree_skb(skb);
+	return 0;
+}
+
+/* Parse tlv encoded option header (hop-by-hop or destination) */
+
+static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb,
+			 __u8 *nhptr)
+{
+	struct tlvtype_proc *curr;
+	u8 *ptr = skb->h.raw;
+	int len = ((ptr[1]+1)<<3) - 2;
+
+	ptr += 2;
+
+	if (skb->tail - (ptr + len) < 0) {
+		kfree_skb(skb);
+		return 0;
+	}
+
+	while (len > 0) {
+		int optlen = ptr[1]+2;
+
+		switch (ptr[0]) {
+		case IPV6_TLV_PAD0:
+			optlen = 1;
+			break;
+
+		case IPV6_TLV_PADN:
+			break;
+
+		default: /* Other TLV code so scan list */
+			for (curr=procs; curr->type >= 0; curr++) {
+				if (curr->type == ptr[0]) {
+					if (curr->func(skb, ptr) == 0)
+						return 0;
+					break;
+				}
+			}
+			if (curr->type < 0) {
+				if (ip6_tlvopt_unknown(skb, ptr) == 0)
+					return 0;
+			}
+			break;
+		}
+		ptr += optlen;
+		len -= optlen;
+	}
+	if (len == 0)
+		return 1;
+	kfree_skb(skb);
+	return 0;
+}
+
+/*****************************
+  Destination options header.
+ *****************************/
+
+struct tlvtype_proc tlvprocdestopt_lst[] = {
+	/* No destination options are defined now */
+	{-1,			NULL}
+};
+
+static u8 *ipv6_dest_opt(struct sk_buff **skb_ptr, u8 *nhptr)
+{
+	struct sk_buff *skb=*skb_ptr;
+	struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
+	struct ipv6_destopt_hdr *hdr = (struct ipv6_destopt_hdr *) skb->h.raw;
+
+	opt->dst1 = (u8*)hdr - skb->nh.raw;
+
+	if (ip6_parse_tlv(tlvprocdestopt_lst, skb, nhptr)) {
+		skb->h.raw += ((hdr->hdrlen+1)<<3);
+		return &hdr->nexthdr;
+	}
+
+	return NULL;
+}
+
+/********************************
+  NONE header. No data in packet.
+ ********************************/
+
+static u8 *ipv6_nodata(struct sk_buff **skb_ptr, u8 *nhptr)
+{
+	kfree_skb(*skb_ptr);
+	return NULL;
+}
+
+/********************************
+  Routing header.
+ ********************************/
+
+static u8* ipv6_routing_header(struct sk_buff **skb_ptr, u8 *nhptr)
 {
 	struct sk_buff *skb = *skb_ptr;
+	struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
 	struct in6_addr *addr;
 	struct in6_addr daddr;
-	int addr_type = 0;
-	int strict = 0;
-	__u32 bit_map;
-	int pos;
+	int addr_type;
 	int n, i;
 
 	struct ipv6_rt_hdr *hdr = (struct ipv6_rt_hdr *) skb->h.raw;
 	struct rt0_hdr *rthdr;
 
-	if (hdr->segments_left == 0) {
-		struct ipv6_options *opt;
-
-		opt = (struct ipv6_options *) skb->cb;
-		opt->srcrt = hdr;
+	if (((hdr->hdrlen+1)<<3) > skb->tail - skb->h.raw) {
+		ipv6_statistics.Ip6InHdrErrors++;
+		kfree_skb(skb);
+		return NULL;
+	}
 
+looped_back:
+	if (hdr->segments_left == 0) {
+		opt->srcrt = (u8*)hdr - skb->nh.raw;
 		skb->h.raw += (hdr->hdrlen + 1) << 3;
-		return hdr->nexthdr;		
+		opt->dst0 = opt->dst1;
+		opt->dst1 = 0;
+		return &hdr->nexthdr;		
 	}
 
-	if (hdr->type != IPV6_SRCRT_TYPE_0 || hdr->hdrlen & 0x01 ||
-	    hdr->hdrlen > 46) {
-                /* 
-		 *	Discard 
-		 */
-		
-		pos = (__u8 *) hdr - (__u8 *) skb->nh.ipv6h + 2;
+	if (hdr->type != IPV6_SRCRT_TYPE_0 || hdr->hdrlen & 0x01) {
+		u8 *pos = (u8*) hdr;
 
-		if (hdr->type)
+		if (hdr->type != IPV6_SRCRT_TYPE_0)
 			pos += 2;
 		else
 			pos += 1;
 
-		icmpv6_send(skb, ICMPV6_PARAMETER_PROB, 0, pos, dev);
-		kfree_skb(skb);
-		return 0;	
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, pos);
+		return NULL;	
 	}
-
+	
 	/*
 	 *	This is the routing header forwarding algorithm from
 	 *	RFC 1883, page 17.
@@ -94,13 +232,21 @@
 	n = hdr->hdrlen >> 1;
 
 	if (hdr->segments_left > n) {
-		pos = (__u8 *) hdr - (__u8 *) skb->nh.ipv6h + 2;
-
-		pos += 3;
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, &hdr->segments_left);
+		return NULL;
+	}
 
-		icmpv6_send(skb, ICMPV6_PARAMETER_PROB, 0, pos, dev);
+	/* We are about to mangle packet header. Be careful!
+	   Do not damage packets queued somewhere.
+	 */
+	if (skb_cloned(skb)) {
+		struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
 		kfree_skb(skb);
-		return 0;
+		if (skb2 == NULL)
+			return NULL;
+		*skb_ptr = skb = skb2;
+		opt = (struct inet6_skb_parm *)skb2->cb;
+		hdr = (struct ipv6_rt_hdr *) skb2->h.raw;
 	}
 
 	i = n - --hdr->segments_left;
@@ -113,58 +259,429 @@
 
 	if (addr_type == IPV6_ADDR_MULTICAST) {
 		kfree_skb(skb);
-		return 0;
+		return NULL;
 	}
 
 	ipv6_addr_copy(&daddr, addr);
 	ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr);
 	ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr);
 
-	/*
-	 *	Check Strick Source Route
+	dst_release(xchg(&skb->dst, NULL));
+	ip6_route_input(skb);
+	if (skb->dst->error) {
+		skb->dst->input(skb);
+		return NULL;
+	}
+	if (skb->dst->dev->flags&IFF_LOOPBACK) {
+		if (skb->nh.ipv6h->hop_limit <= 1) {
+			icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
+				    0, skb->dev);
+			kfree_skb(skb);
+			return NULL;
+		}
+		skb->nh.ipv6h->hop_limit--;
+		goto looped_back;
+	}
+
+	skb->dst->input(skb);
+	return NULL;
+}
+
+/*
+   This function inverts received rthdr.
+   NOTE: specs allow to make it automatically only if
+   packet authenticated.
+
+   I will not discuss it here (though, I am really pissed off at
+   this stupid requirement making rthdr idea useless)
+
+   Actually, it creates severe problems  for us.
+   Embrionic requests has no associated sockets,
+   so that user have no control over it and
+   cannot not only to set reply options, but
+   even to know, that someone wants to connect
+   without success. :-(
+
+   For now we need to test the engine, so that I created
+   temporary (or permanent) backdoor.
+   If listening socket set IPV6_RTHDR to 2, then we invert header.
+                                                   --ANK (980729)
+ */
+
+struct ipv6_txoptions *
+ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
+{
+	/* Received rthdr:
+
+	   [ H1 -> H2 -> ... H_prev ]  daddr=ME
+
+	   Inverted result:
+	   [ H_prev -> ... -> H1 ] daddr =sender
+
+	   Note, that IP output engine will rewrire this rthdr
+	   by rotating it left by one addr.
 	 */
 
-	bit_map = ntohl(rthdr->bitmap);
+	int n, i;
+	struct rt0_hdr *rthdr = (struct rt0_hdr*)hdr;
+	struct rt0_hdr *irthdr;
+	struct ipv6_txoptions *opt;
+	int hdrlen = ipv6_optlen(hdr);
+
+	if (hdr->segments_left ||
+	    hdr->type != IPV6_SRCRT_TYPE_0 ||
+	    hdr->hdrlen & 0x01)
+		return NULL;
+
+	n = hdr->hdrlen >> 1;
+	opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC);
+	if (opt == NULL)
+		return NULL;
+	memset(opt, 0, sizeof(*opt));
+	opt->tot_len = sizeof(*opt) + hdrlen;
+	opt->srcrt = (void*)(opt+1);
+	opt->opt_nflen = hdrlen;
+
+	memcpy(opt->srcrt, hdr, sizeof(*hdr));
+	irthdr = (struct rt0_hdr*)opt->srcrt;
+	/* Obsolete field, MBZ, when originated by us */
+	irthdr->bitmap = 0;
+	opt->srcrt->segments_left = n;
+	for (i=0; i<n; i++)
+		memcpy(irthdr->addr+i, rthdr->addr+(n-1-i), 16);
+	return opt;
+}
+
+/********************************
+  AUTH header.
+ ********************************/
+
+/*
+   rfc1826 said, that if a host does not implement AUTH header
+   it MAY ignore it. We use this hole 8)
+
+   Actually, now we can implement OSPFv6 without kernel IPsec.
+   Authentication for poors may be done in user space with the same success.
+
+   Yes, it means, that we allow application to send/receive
+   raw authentication header. Apparently, we suppose, that it knows
+   what it does and calculates authentication data correctly.
+   Certainly, it is possible only for udp and raw sockets, but not for tcp.
+
+   BTW I beg pardon, it is not good place for flames, but
+   I cannot be silent 8) It is very sad, but fools prevail 8)
+   AUTH header has 4byte granular length, what kills all the idea
+   behind AUTOMATIC 64bit alignment of IPv6. Now we will loose
+   cpu ticks, checking that sender did not something stupid
+   and opt->hdrlen is even. Shit!		--ANK (980730)
+ */
+
+static u8 *ipv6_auth_hdr(struct sk_buff **skb_ptr, u8 *nhptr)
+{
+	struct sk_buff *skb=*skb_ptr;
+	struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
+	struct ipv6_opt_hdr *hdr = (struct ipv6_opt_hdr *)skb->h.raw;
+	int len = (hdr->hdrlen+2)<<2;
+
+	opt->auth = (u8*)hdr - skb->nh.raw;
+	if (skb->h.raw + len > skb->tail)
+		return NULL;
+	skb->h.raw += len;
+	return &hdr->nexthdr;
+}
+
+/* This list MUST NOT contain entry for NEXTHDR_HOP.
+   It is parsed immediately after packet received
+   and if it occurs somewhere in another place we must
+   generate error.
+ */
+
+struct hdrtype_proc hdrproc_lst[] = {
+	{NEXTHDR_FRAGMENT,	ipv6_reassembly},
+	{NEXTHDR_ROUTING,	ipv6_routing_header},
+	{NEXTHDR_DEST,		ipv6_dest_opt},
+	{NEXTHDR_NONE,		ipv6_nodata},
+	{NEXTHDR_AUTH,		ipv6_auth_hdr},
+   /*
+	{NEXTHDR_ESP,		ipv6_esp_hdr},
+    */
+	{-1,			NULL}
+};
+
+u8 *ipv6_parse_exthdrs(struct sk_buff **skb_in, u8 *nhptr)
+{
+	struct hdrtype_proc *hdrt;
+	u8 nexthdr = *nhptr;
+
+restart:
+	for (hdrt=hdrproc_lst; hdrt->type >= 0; hdrt++) {
+		if (hdrt->type == nexthdr) {
+			if ((nhptr = hdrt->func(skb_in, nhptr)) != NULL) {
+				nexthdr = *nhptr;
+				goto restart;
+			}
+			return NULL;
+		}
+	}
+	return nhptr;
+}
+
+
+/**********************************
+  Hop-by-hop options.
+ **********************************/
+
+/* Router Alert as of draft-ietf-ipngwg-ipv6router-alert-04 */
+
+static int ipv6_hop_ra(struct sk_buff *skb, u8 *ptr)
+{
+	if (ptr[1] == 2) {
+		((struct inet6_skb_parm*)skb->cb)->ra = ptr - skb->nh.raw;
+		return 1;
+	}
+	if (net_ratelimit())
+		printk(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n", ptr[1]);
+	kfree_skb(skb);
+	return 0;
+}
+
+/* Jumbo payload */
+
+static int ipv6_hop_jumbo(struct sk_buff *skb, u8 *ptr)
+{
+	u32 pkt_len;
+
+	if (ptr[1] != 4 || ((ptr-skb->nh.raw)&3) != 2) {
+		if (net_ratelimit())
+			printk(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", ptr[1]);
+		goto drop;
+	}
 
-	if ((bit_map & (1 << i)) == IPV6_SRCRT_STRICT)
-		strict = 1;
+	pkt_len = ntohl(*(u32*)(ptr+2));
+	if (pkt_len < 0x10000) {
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, ptr+2);
+		return 0;
+	}
+	if (skb->nh.ipv6h->payload_len) {
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, ptr);
+		return 0;
+	}
 
-	ipv6_forward(skb, dev, (strict ? IP6_FW_STRICT : 0) | IP6_FW_SRCRT);
+	if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
+		ipv6_statistics.Ip6InTruncatedPkts++;
+		goto drop;
+	}
+	skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+	return 1;
 
+drop:
+	kfree_skb(skb);
 	return 0;
 }
 
+struct tlvtype_proc tlvprochopopt_lst[] = {
+	{IPV6_TLV_ROUTERALERT,	ipv6_hop_ra},
+	{IPV6_TLV_JUMBO,	ipv6_hop_jumbo},
+	{-1,			NULL}
+};
+
+u8 * ipv6_parse_hopopts(struct sk_buff *skb, u8 *nhptr)
+{
+	((struct inet6_skb_parm*)skb->cb)->hop = sizeof(struct ipv6hdr);
+	if (ip6_parse_tlv(tlvprochopopt_lst, skb, nhptr))
+		return nhptr+((nhptr[1]+1)<<3);
+	return NULL;
+}
 
 /*
- *	outbound
+ *	Creating outbound headers.
+ *
+ *	"build" functions work when skb is filled from head to tail (datagram)
+ *	"push"	functions work when headers are added from tail to head (tcp)
+ *
+ *	In both cases we assume, that caller reserved enough room
+ *	for headers.
  */
 
-int ipv6opt_bld_rthdr(struct sk_buff *skb, struct ipv6_options *opt,
-		      struct in6_addr *addr)		      
+u8 *ipv6_build_rthdr(struct sk_buff *skb, u8 *prev_hdr,
+		     struct ipv6_rt_hdr *opt, struct in6_addr *addr)
 {
 	struct rt0_hdr *phdr, *ihdr;
 	int hops;
 
-	ihdr = (struct rt0_hdr *) opt->srcrt;
+	ihdr = (struct rt0_hdr *) opt;
 	
 	phdr = (struct rt0_hdr *) skb_put(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
-	memcpy(phdr, ihdr, sizeof(struct ipv6_rt_hdr));
+	memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
 
 	hops = ihdr->rt_hdr.hdrlen >> 1;
-	
+
 	if (hops > 1)
 		memcpy(phdr->addr, ihdr->addr + 1,
 		       (hops - 1) * sizeof(struct in6_addr));
 
 	ipv6_addr_copy(phdr->addr + (hops - 1), addr);
+
+	phdr->rt_hdr.nexthdr = *prev_hdr;
+	*prev_hdr = NEXTHDR_ROUTING;
+	return &phdr->rt_hdr.nexthdr;
+}
+
+static u8 *ipv6_build_exthdr(struct sk_buff *skb, u8 *prev_hdr, u8 type, struct ipv6_opt_hdr *opt)
+{
+	struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_put(skb, ipv6_optlen(opt));
+
+	memcpy(h, opt, ipv6_optlen(opt));
+	h->nexthdr = *prev_hdr;
+	*prev_hdr = type;
+	return &h->nexthdr;
+}
+
+static u8 *ipv6_build_authhdr(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_opt_hdr *opt)
+{
+	struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_put(skb, (opt->hdrlen+2)<<2);
+
+	memcpy(h, opt, (opt->hdrlen+2)<<2);
+	h->nexthdr = *prev_hdr;
+	*prev_hdr = NEXTHDR_AUTH;
+	return &h->nexthdr;
+}
+
+
+u8 *ipv6_build_nfrag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt,
+			  struct in6_addr *daddr, u32 jumbolen)
+{
+	struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb->data;
+
+	if (opt && opt->hopopt)
+		prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_HOP, opt->hopopt);
+
+	if (jumbolen) {
+		u8 *jumboopt = (u8 *)skb_put(skb, 8);
+
+		if (opt && opt->hopopt) {
+			*jumboopt++ = IPV6_TLV_PADN;
+			*jumboopt++ = 0;
+			h->hdrlen++;
+		} else {
+			h = (struct ipv6_opt_hdr *)jumboopt;
+			h->nexthdr = *prev_hdr;
+			h->hdrlen = 0;
+			jumboopt += 2;
+			*prev_hdr = NEXTHDR_HOP;
+			prev_hdr = &h->nexthdr;
+		}
+		jumboopt[0] = IPV6_TLV_JUMBO;
+		jumboopt[1] = 4;
+		*(u32*)(jumboopt+2) = htonl(jumbolen);
+	}
+	if (opt) {
+		if (opt->dst0opt)
+			prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst0opt);
+		if (opt->srcrt)
+			prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr);
+	}
+	return prev_hdr;
+}
+
+u8 *ipv6_build_frag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt)
+{
+	if (opt->auth)
+		prev_hdr = ipv6_build_authhdr(skb, prev_hdr, opt->auth);
+	if (opt->dst1opt)
+		prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst1opt);
+	return prev_hdr;
+}
+
+static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
+			    struct ipv6_rt_hdr *opt,
+			    struct in6_addr **addr_p)
+{
+	struct rt0_hdr *phdr, *ihdr;
+	int hops;
+
+	ihdr = (struct rt0_hdr *) opt;
 	
-	phdr->rt_hdr.nexthdr = proto; 
-	return NEXTHDR_ROUTING;
+	phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
+	memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
+
+	hops = ihdr->rt_hdr.hdrlen >> 1;
+
+	if (hops > 1)
+		memcpy(phdr->addr, ihdr->addr + 1,
+		       (hops - 1) * sizeof(struct in6_addr));
+
+	ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p);
+	*addr_p = ihdr->addr;
+
+	phdr->rt_hdr.nexthdr = *proto;
+	*proto = NEXTHDR_ROUTING;
+}
+
+static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
+{
+	struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
+
+	memcpy(h, opt, ipv6_optlen(opt));
+	h->nexthdr = *proto;
+	*proto = type;
+}
+
+static void ipv6_push_authhdr(struct sk_buff *skb, u8 *proto, struct ipv6_opt_hdr *opt)
+{
+	struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, (opt->hdrlen+2)<<2);
+
+	memcpy(h, opt, (opt->hdrlen+2)<<2);
+	h->nexthdr = *proto;
+	*proto = NEXTHDR_AUTH;
 }
-#endif
+
+void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
+			  u8 *proto,
+			  struct in6_addr **daddr)
+{
+	if (opt->srcrt)
+		ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
+	if (opt->dst0opt)
+		ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
+	if (opt->hopopt)
+		ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
+}
+
+void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
+{
+	if (opt->dst1opt)
+		ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
+	if (opt->auth)
+		ipv6_push_authhdr(skb, proto, opt->auth);
+}
+
+struct ipv6_txoptions *
+ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
+{
+	struct ipv6_txoptions *opt2;
+
+	opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
+	if (opt2) {
+		long dif = (char*)opt2 - (char*)opt;
+		memcpy(opt2, opt, opt->tot_len);
+		if (opt2->hopopt)
+			*((char**)&opt2->hopopt) += dif;
+		if (opt2->dst0opt)
+			*((char**)&opt2->dst0opt) += dif;
+		if (opt2->dst1opt)
+			*((char**)&opt2->dst1opt) += dif;
+		if (opt2->auth)
+			*((char**)&opt2->auth) += dif;
+		if (opt2->srcrt)
+			*((char**)&opt2->srcrt) += dif;
+	}
+	return opt2;
+}
+
 
 /* 
- * find out if nexthdr is an extension header or a protocol
+ * find out if nexthdr is a well-known extension header or a protocol
  */
 
 static __inline__ int ipv6_ext_hdr(u8 nexthdr)
@@ -175,11 +692,9 @@
 	return ( (nexthdr == NEXTHDR_HOP)	||
 		 (nexthdr == NEXTHDR_ROUTING)	||
 		 (nexthdr == NEXTHDR_FRAGMENT)	||
-		 (nexthdr == NEXTHDR_ESP)	||
 		 (nexthdr == NEXTHDR_AUTH)	||
 		 (nexthdr == NEXTHDR_NONE)	||
 		 (nexthdr == NEXTHDR_DEST) );
-		 
 }
 
 /*
@@ -200,34 +715,57 @@
  * 
  * But I see no other way to do this. This might need to be reexamined
  * when Linux implements ESP (and maybe AUTH) headers.
+ * --AK
+ *
+ * This function parses (probably truncated) exthdr set "hdr"
+ * of length "len". "nexthdrp" initially points to some place,
+ * where type of the first header can be found.
+ *
+ * It skips all well-known exthdrs, and returns pointer to the start
+ * of unparsable area i.e. the first header with unknown type.
+ * If it is not NULL *nexthdr is updated by type/protocol of this header.
+ *
+ * NOTES: - if packet terminated with NEXTHDR_NONE it returns NULL.
+ *        - it may return pointer pointing beyond end of packet,
+ *	    if the last recognized header is truncated in the middle.
+ *        - if packet is truncated, so that all parsed headers are skipped,
+ *	    it returns NULL.
+ *	  - First fragment header is skipped, not-first ones
+ *	    are considered as unparsable.
+ *	  - ESP is unparsable for now and considered like
+ *	    normal payload protocol.
+ *	  - Note also special handling of AUTH header. Thanks to IPsec wizards.
+ *
+ * --ANK (980726)
  */
-struct ipv6_opt_hdr *ipv6_skip_exthdr(struct ipv6_opt_hdr *hdr, 
-				      u8 *nexthdrp, int len)
+
+u8 *ipv6_skip_exthdr(struct ipv6_opt_hdr *hdr, u8 *nexthdrp, int len)
 {
 	u8 nexthdr = *nexthdrp;
 
 	while (ipv6_ext_hdr(nexthdr)) {
 		int hdrlen; 
-		
-		if (nexthdr == NEXTHDR_NONE)
+
+		if (len < sizeof(struct ipv6_opt_hdr))
 			return NULL;
-		if (len < sizeof(struct ipv6_opt_hdr)) /* be anal today */
+		if (nexthdr == NEXTHDR_NONE)
 			return NULL;
-
-		hdrlen = ipv6_optlen(hdr); 
-		if (len < hdrlen)
-			return NULL; 
+		if (nexthdr == NEXTHDR_FRAGMENT) {
+			struct frag_hdr *fhdr = (struct frag_hdr *) hdr;
+			if (ntohs(fhdr->frag_off) & ~0x7)
+				break;
+			hdrlen = 8;
+		} else if (nexthdr == NEXTHDR_AUTH)
+			hdrlen = (hdr->hdrlen+2)<<2; 
+		else
+			hdrlen = ipv6_optlen(hdr); 
 
 		nexthdr = hdr->nexthdr;
 		hdr = (struct ipv6_opt_hdr *) ((u8*)hdr + hdrlen);
 		len -= hdrlen;
 	}
 
-	/* Hack.. Do the same for AUTH headers? */
-	if (nexthdr == NEXTHDR_ESP) 
-		return NULL; 
-
 	*nexthdrp = nexthdr;
-	return hdr;
+	return (u8*)hdr;
 }
 
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/icmp.c linux/net/ipv6/icmp.c
--- v2.1.119/linux/net/ipv6/icmp.c	Thu May 14 19:47:45 1998
+++ linux/net/ipv6/icmp.c	Thu Aug 27 19:33:09 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: icmp.c,v 1.18 1998/05/07 15:42:59 davem Exp $
+ *	$Id: icmp.c,v 1.19 1998/08/26 12:04:52 davem Exp $
  *
  *	Based on net/ipv4/icmp.c
  *
@@ -58,16 +58,15 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 
+struct icmpv6_mib icmpv6_statistics;
+
 /*
  *	ICMP socket for flow control.
  */
 
 struct socket *icmpv6_socket;
 
-int icmpv6_rcv(struct sk_buff *skb, struct device *dev,
-	       struct in6_addr *saddr, struct in6_addr *daddr,
-	       struct ipv6_options *opt, unsigned short len,
-	       int redo, struct inet6_protocol *protocol);
+int icmpv6_rcv(struct sk_buff *skb, unsigned long len);
 
 static struct inet6_protocol icmpv6_protocol = 
 {
@@ -80,8 +79,6 @@
 	"ICMPv6"	       	/* name			*/
 };
 
-
-
 struct icmpv6_msg {
 	struct icmp6hdr		icmph;
 	__u8 			*data;
@@ -105,8 +102,11 @@
 
 	/* 
 	 *	in theory offset must be 0 since we never send more 
-	 *	than 576 bytes on an error or more than the path mtu
+	 *	than IPV6_MIN_MTU bytes on an error or more than the path mtu
 	 *	on an echo reply. (those are the rules on RFC 1883)
+	 *
+	 * 	Luckily, this statement is obsolete after
+	 *	draft-ietf-ipngwg-icmp-v2-00           --ANK (980730)
 	 */
 
 	if (offset) {
@@ -143,13 +143,36 @@
 	kfree_skb(skb);
 }
 
-static inline int is_icmp(struct ipv6hdr *hdr, int len)
+/*
+ * Figure out, may we reply to this packet with icmp error.
+ *
+ * We do not reply, if:
+ *	- it was icmp error message.
+ *	- it is truncated, so that it is known, that protocol is ICMPV6
+ *	  (i.e. in the middle of some exthdr)
+ *	- it is not the first fragment. BTW IPv6 specs say nothing about
+ *	  this case, but it is clear, that our reply would be useless
+ *	  for sender.
+ *
+ *	--ANK (980726)
+ */
+
+static int is_ineligible(struct ipv6hdr *hdr, int len)
 {
-	__u8 nexthdr = hdr->nexthdr; 
+	u8 *ptr;
+	__u8 nexthdr = hdr->nexthdr;
+
+	if (len < (int)sizeof(*hdr))
+		return 1;
 
-	if (!ipv6_skip_exthdr((struct ipv6_opt_hdr *)(hdr+1), &nexthdr, len))
-		return 0; 
-	return nexthdr == IPPROTO_ICMP; 
+	ptr = ipv6_skip_exthdr((struct ipv6_opt_hdr *)(hdr+1), &nexthdr, len - sizeof(*hdr));
+	if (!ptr)
+		return 0;
+	if (nexthdr == IPPROTO_ICMPV6) {
+		struct icmp6hdr *ihdr =	(struct icmp6hdr *)ptr;
+		return (ptr - (u8*)hdr) > len || !(ihdr->icmp6_type & 0x80); 
+	}
+	return nexthdr == NEXTHDR_FRAGMENT;
 }
 
 int sysctl_icmpv6_time = 1*HZ; 
@@ -160,31 +183,37 @@
 static inline int icmpv6_xrlim_allow(struct sock *sk, int type,
 				     struct flowi *fl)
 {
-#if 0
-	struct dst_entry *dst; 
-	int allow = 0;
-#endif
+	struct dst_entry *dst;
+	int res = 0;
+
 	/* Informational messages are not limited. */
 	if (type & 0x80)
-		return 1; 
+		return 1;
 
-#if 0 /* not yet, first fix routing COW */
+	/* Do not limit pmtu discovery, it would break it. */
+	if (type == ICMPV6_PKT_TOOBIG)
+		return 1;
 
 	/* 
 	 * Look up the output route.
 	 * XXX: perhaps the expire for routing entries cloned by
 	 * this lookup should be more aggressive (not longer than timeout).
 	 */
-	dst = ip6_route_output(sk, fl, 1);
-	if (dst->error) 
+	dst = ip6_route_output(sk, fl);
+	if (dst->error)
 		ipv6_statistics.Ip6OutNoRoutes++;
-	else 
-		allow = xrlim_allow(dst, sysctl_icmpv6_time);
+	else {
+		struct rt6_info *rt = (struct rt6_info *)dst;
+		int tmo = sysctl_icmpv6_time;
+
+		/* Give more bandwidth to wider prefixes. */
+		if (rt->rt6i_dst.plen < 128)
+			tmo >>= ((128 - rt->rt6i_dst.plen)>>5);
+
+		res = xrlim_allow(dst, tmo);
+	}
 	dst_release(dst);
-	return allow;
-#else
-	return 1;
-#endif
+	return res;
 }
 
 /*
@@ -196,7 +225,7 @@
 
 static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
 {
-	char *buff = skb->nh.raw;
+	u8 *buff = skb->nh.raw;
 
 	return ( ( *(buff + offset) & 0xC0 ) == 0x80 );
 }
@@ -215,7 +244,6 @@
 	struct icmpv6_msg msg;
 	struct flowi fl;
 	int addr_type = 0;
-	int optlen;
 	int len;
 
 	/*
@@ -237,7 +265,7 @@
 	
 	addr_type = ipv6_addr_type(&hdr->daddr);
 
-	if (ipv6_chk_addr(&hdr->daddr, NULL, 0))
+	if (ipv6_chk_addr(&hdr->daddr, skb->dev, 0))
 		saddr = &hdr->daddr;
 
 	/*
@@ -275,8 +303,9 @@
 	/* 
 	 *	Never answer to a ICMP packet.
 	 */
-	if (is_icmp(hdr, (u8*)skb->tail - (u8*)hdr)) {
-		printk(KERN_DEBUG "icmpv6_send: no reply to icmp\n"); 
+	if (is_ineligible(hdr, (u8*)skb->tail - (u8*)hdr)) {
+		if (net_ratelimit())
+			printk(KERN_DEBUG "icmpv6_send: no reply to icmp error/fragment\n"); 
 		return;
 	}
 
@@ -303,34 +332,22 @@
 	msg.data = skb->nh.raw;
 	msg.csum = 0;
 	msg.daddr = &hdr->saddr;
-        /*
-	if (skb->opt)
-		optlen = skb->opt->optlen;
-	else
-	*/
-
-	optlen = 0;
-
-	len = min(skb->tail - ((unsigned char *) hdr), 
-		  576 - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr)
-		  - optlen);
+
+	len = min((skb->tail - ((unsigned char *) hdr)) + sizeof(struct icmp6hdr), 
+		  IPV6_MIN_MTU - sizeof(struct icmp6hdr));
 
 	if (len < 0) {
 		printk(KERN_DEBUG "icmp: len problem\n");
 		return;
 	}
 
-	len += sizeof(struct icmp6hdr);
-
 	msg.len = len;
 
 	ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
 		       MSG_DONTWAIT);
-
-	/* Oops! We must purge cached dst, otherwise
-	   all the following ICMP messages will go there :) --ANK
-	 */
-	dst_release(xchg(&sk->dst_cache, NULL));
+	if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
+		(&icmpv6_statistics.Icmp6OutDestUnreachs)[type-1]++;
+	icmpv6_statistics.Icmp6OutMsgs++;
 }
 
 static void icmpv6_echo_reply(struct sk_buff *skb)
@@ -374,38 +391,41 @@
 
 	ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
 		       MSG_DONTWAIT);
-
-	/* Oops! We must purge cached dst, otherwise
-	   all the following ICMP messages will go there :) --ANK
-	 */
-	dst_release(xchg(&sk->dst_cache, NULL));
+	icmpv6_statistics.Icmp6OutEchoReplies++;
+	icmpv6_statistics.Icmp6OutMsgs++;
 }
 
 static void icmpv6_notify(struct sk_buff *skb,
-			  int type, int code, unsigned char *buff, int len,
-			  struct in6_addr *saddr, struct in6_addr *daddr, 
-			  struct inet6_protocol *protocol)
+			  int type, int code, unsigned char *buff, int len)
 {
+	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
 	struct ipv6hdr *hdr = (struct ipv6hdr *) buff;
 	struct inet6_protocol *ipprot;
 	struct sock *sk;
-	struct ipv6_opt_hdr *pb;
+	u8 *pb;
 	__u32 info = 0;
 	int hash;
 	u8 nexthdr;
 
 	nexthdr = hdr->nexthdr;
 
-	pb = (struct ipv6_opt_hdr *) (hdr + 1);
 	len -= sizeof(struct ipv6hdr);
 	if (len < 0)
 		return;
 
 	/* now skip over extension headers */
-	pb = ipv6_skip_exthdr(pb, &nexthdr, len);
+	pb = ipv6_skip_exthdr((struct ipv6_opt_hdr *) (hdr + 1), &nexthdr, len);
 	if (!pb)
 		return;
 
+	/* BUGGG_FUTURE: we should try to parse exthdrs in this packet.
+	   Without this we will not able f.e. to make source routed
+	   pmtu discovery.
+	   Corresponding argument (opt) to notifiers is already added.
+	   --ANK (980726)
+	 */
+
 	hash = nexthdr & (MAX_INET_PROTOS - 1);
 
 	for (ipprot = (struct inet6_protocol *) inet6_protos[hash]; 
@@ -414,9 +434,8 @@
 		if (ipprot->protocol != nexthdr)
 			continue;
 
-		if (ipprot->err_handler) 
-			ipprot->err_handler(skb, type, code, (u8*)pb, info,
-					    saddr, daddr, ipprot);
+		if (ipprot->err_handler)
+			ipprot->err_handler(skb, hdr, NULL, type, code, pb, info);
 		return;
 	}
 
@@ -428,7 +447,7 @@
 		return;
 
 	while((sk = raw_v6_lookup(sk, nexthdr, daddr, saddr))) {
-		rawv6_err(sk, type, code, (char*)pb, saddr, daddr);
+		rawv6_err(sk, skb, hdr, NULL, type, code, pb, info);
 		sk = sk->next;
 	}
 }
@@ -437,14 +456,17 @@
  *	Handle icmp messages
  */
 
-int icmpv6_rcv(struct sk_buff *skb, struct device *dev,
-	       struct in6_addr *saddr, struct in6_addr *daddr,
-	       struct ipv6_options *opt, unsigned short len,
-	       int redo, struct inet6_protocol *protocol)
+int icmpv6_rcv(struct sk_buff *skb, unsigned long len)
 {
+	struct device *dev = skb->dev;
+	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
 	struct ipv6hdr *orig_hdr;
 	struct icmp6hdr *hdr = (struct icmp6hdr *) skb->h.raw;
 	int ulen;
+	int type;
+
+	icmpv6_statistics.Icmp6InMsgs++;
 
 	/* Perform checksum. */
 	switch (skb->ip_summed) {	
@@ -480,8 +502,15 @@
 	 *	length of original packet carried in skb
 	 */
 	ulen = skb->tail - (unsigned char *) (hdr + 1);
-	
-	switch (hdr->icmp6_type) {
+
+	type = hdr->icmp6_type;
+
+	if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
+		(&icmpv6_statistics.Icmp6InDestUnreachs)[type-ICMPV6_DEST_UNREACH]++;
+	else if (type >= ICMPV6_ECHO_REQUEST && type <= NDISC_REDIRECT)
+		(&icmpv6_statistics.Icmp6InEchos)[type-ICMPV6_ECHO_REQUEST]++;
+
+	switch (type) {
 
 	case ICMPV6_ECHO_REQUEST:
 		icmpv6_echo_reply(skb);
@@ -492,9 +521,14 @@
 		break;
 
 	case ICMPV6_PKT_TOOBIG:
+		/* BUGGG_FUTURE: if packet contains rthdr, we cannot update
+		   standard destination cache. Seems, only "advanced"
+		   destination cache will allow to solve this problem
+		   --ANK (980726)
+		 */
 		orig_hdr = (struct ipv6hdr *) (hdr + 1);
 		if (ulen >= sizeof(struct ipv6hdr))
-			rt6_pmtu_discovery(&orig_hdr->daddr, dev,
+			rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev,
 					   ntohl(hdr->icmp6_mtu));
 
 		/*
@@ -504,10 +538,8 @@
 	case ICMPV6_DEST_UNREACH:
 	case ICMPV6_TIME_EXCEED:
 	case ICMPV6_PARAMPROB:
-
-		icmpv6_notify(skb, hdr->icmp6_type, hdr->icmp6_code,
-			      (char *) (hdr + 1), ulen,
-			      saddr, daddr, protocol);
+		icmpv6_notify(skb, type, hdr->icmp6_code,
+			      (char *) (hdr + 1), ulen);
 		break;
 
 	case NDISC_ROUTER_SOLICITATION:
@@ -515,7 +547,7 @@
 	case NDISC_NEIGHBOUR_SOLICITATION:
 	case NDISC_NEIGHBOUR_ADVERTISEMENT:
 	case NDISC_REDIRECT:
-		ndisc_rcv(skb, dev, saddr, daddr, opt, len);		
+		ndisc_rcv(skb, len);
 		break;
 
 	case ICMPV6_MGM_QUERY:
@@ -530,23 +562,26 @@
 		break;
 
 	default:
-		printk(KERN_DEBUG "icmpv6: msg of unkown type\n");
+		if (net_ratelimit())
+			printk(KERN_DEBUG "icmpv6: msg of unkown type\n");
 		
 		/* informational */
-		if (hdr->icmp6_type & 0x80)
-			goto discard_it;
+		if (type & 0x80)
+			break;
 
 		/* 
 		 * error of unkown type. 
 		 * must pass to upper level 
 		 */
 
-		icmpv6_notify(skb, hdr->icmp6_type, hdr->icmp6_code,
-			      (char *) (hdr + 1), ulen,
-			      saddr, daddr, protocol);	
+		icmpv6_notify(skb, type, hdr->icmp6_code,
+			      (char *) (hdr + 1), ulen);
 	};
+	kfree_skb(skb);
+	return 0;
 
 discard_it:
+	icmpv6_statistics.Icmp6InErrors++;
 	kfree_skb(skb);
 	return 0;
 }
@@ -597,7 +632,7 @@
 } tab_unreach[] = {
 	{ ENETUNREACH,	0},	/* NOROUTE		*/
 	{ EACCES,	1},	/* ADM_PROHIBITED	*/
-	{ EOPNOTSUPP,	1},	/* NOT_NEIGHBOUR	*/
+	{ 0,		0},	/* Was NOT_NEIGHBOUR, now reserved */
 	{ EHOSTUNREACH,	0},	/* ADDR_UNREACH		*/
 	{ ECONNREFUSED,	1},	/* PORT_UNREACH		*/
 };
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/ip6_fib.c linux/net/ipv6/ip6_fib.c
--- v2.1.119/linux/net/ipv6/ip6_fib.c	Thu May 14 19:47:45 1998
+++ linux/net/ipv6/ip6_fib.c	Thu Aug 27 19:33:09 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_fib.c,v 1.14 1998/05/07 15:43:03 davem Exp $
+ *	$Id: ip6_fib.c,v 1.15 1998/08/26 12:04:55 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -32,10 +32,52 @@
 #include <net/ip6_fib.h>
 #include <net/ip6_route.h>
 
-#define RT_DEBUG 2
+#define RT6_DEBUG 2
+#undef CONFIG_IPV6_SUBTREES
+
+#if RT6_DEBUG >= 1
+#define BUG_TRAP(x) ({ if (!(x)) { printk("Assertion (" #x ") failed at " __FILE__ "(%d):" __FUNCTION__ "\n", __LINE__); } })
+#else
+#define BUG_TRAP(x) do { ; } while (0)
+#endif
+
+#if RT6_DEBUG >= 3
+#define RT6_TRACE(x...) printk(KERN_DEBUG x)
+#else
+#define RT6_TRACE(x...) do { ; } while (0)
+#endif
 
 struct rt6_statistics	rt6_stats;
 
+enum fib_walk_state_t
+{
+#ifdef CONFIG_IPV6_SUBTREES
+	FWS_S,
+#endif
+	FWS_L,
+	FWS_R,
+	FWS_C,
+	FWS_U
+};
+
+struct fib6_cleaner_t
+{
+	struct fib6_walker_t w;
+	int (*func)(struct rt6_info *, void *arg);
+	void *arg;
+};
+
+#ifdef CONFIG_IPV6_SUBTREES
+#define FWS_INIT FWS_S
+#define SUBTREE(fn) ((fn)->subtree)
+#else
+#define FWS_INIT FWS_L
+#define SUBTREE(fn) NULL
+#endif
+
+static void fib6_prune_clones(struct fib6_node *fn, struct rt6_info *rt);
+static void fib6_repair_tree(struct fib6_node *fn);
+
 /*
  *	A routing update causes an increase of the serial number on the
  *	afected subtree. This allows for cached routes to be asynchronously
@@ -48,10 +90,24 @@
 static struct timer_list ip6_fib_timer = {
 	NULL, NULL,
 	0,
-	0,
+	~0UL,
 	fib6_run_gc
 };
 
+static struct fib6_walker_t fib6_walker_list = {
+	&fib6_walker_list, &fib6_walker_list, 
+};
+
+#define FOR_WALKERS(w) for ((w)=fib6_walker_list.next; (w) != &fib6_walker_list; (w)=(w)->next)
+
+static __inline__ u32 fib6_new_sernum(void)
+{
+	u32 n = ++rt_sernum;
+	if (n == 0)
+		n = ++rt_sernum;
+	return n;
+}
+
 /*
  *	Auxiliary address test functions for the radix tree.
  *
@@ -70,7 +126,7 @@
 	int pdw;
 	int pbi;
 
-	pdw = prefixlen >> 0x05;  /* num of whole __u32 in prefix */
+	pdw = prefixlen >> 5;	  /* num of whole __u32 in prefix */
 	pbi = prefixlen &  0x1f;  /* num of bits in incomplete u32 in prefix */
 
 	if (pdw)
@@ -78,15 +134,11 @@
 			return 0;
 
 	if (pbi) {
-		__u32 w1, w2;
 		__u32 mask;
 
-		w1 = a1[pdw];
-		w2 = a2[pdw];
-
-		mask = htonl((0xffffffff) << (0x20 - pbi));
+		mask = htonl((0xffffffff) << (32 - pbi));
 
-		if ((w1 ^ w2) & mask)
+		if ((a1[pdw] ^ a2[pdw]) & mask)
 			return 0;
 	}
 
@@ -99,24 +151,11 @@
 
 static __inline__ int addr_bit_set(void *token, int fn_bit)
 {
-	int dw;
-	__u32 b1;
-	__u32 mask;
-	int bit = fn_bit;
 	__u32 *addr = token;
 
-	dw = bit >> 0x05;
-
-	b1 = addr[dw];
-	
-	bit = ~bit;
-	bit &= 0x1f;
-	mask = htonl(1 << bit);
-	return (b1 & mask);
+	return htonl(1 << ((~fn_bit)&0x1F)) & addr[fn_bit>>5];
 }
 
-
-
 /*
  *	find the first different bit between two addresses
  *	length of address must be a multiple of 32bits
@@ -131,42 +170,47 @@
 	addrlen >>= 2;
 
 	for (i = 0; i < addrlen; i++) {
-		__u32 b1, b2;
 		__u32 xb;
 
-		b1 = a1[i];
-		b2 = a2[i];
-
-		xb = b1 ^ b2;
+		xb = a1[i] ^ a2[i];
 
 		if (xb) {
-			int res = 0;
-			int j=31;
+			int j = 31;
 
 			xb = ntohl(xb);
 
-			while (test_bit(j, &xb) == 0) {
-				res++;
+			while (test_bit(j, &xb) == 0)
 				j--;
-			}
 
-			return (i * 32 + res);
+			return (i * 32 + 31 - j);
 		}
 	}
 
 	/*
 	 *	we should *never* get to this point since that 
 	 *	would mean the addrs are equal
+	 *
+	 *	However, we do get to it 8) And exacly, when
+	 *	addresses are equal 8)
+	 *
+	 *	ip route add 1111::/128 via ...
+	 *	ip route add 1111::/64 via ...
+	 *	and we are here.
+	 *
+	 *	Ideally, this function should stop comparison
+	 *	at prefix length. It does not, but it is still OK,
+	 *	if returned value is greater than prefix length.
+	 *					--ANK (980803)
 	 */
 
-	return -1;
+	return addrlen<<5;
 }
 
 static __inline__ struct fib6_node * node_alloc(void)
 {
 	struct fib6_node *fn;
 
-	if ((fn = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC))) {
+	if ((fn = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC)) != NULL) {
 		memset(fn, 0, sizeof(struct fib6_node));
 		rt6_stats.fib_nodes++;
 	}
@@ -180,13 +224,10 @@
 	kfree(fn);
 }
 
-extern __inline__ void rt6_release(struct rt6_info *rt)
+static __inline__ void rt6_release(struct rt6_info *rt)
 {
-	struct dst_entry *dst = (struct dst_entry *) rt;
-	if (atomic_dec_and_test(&dst->refcnt)) {
-		rt->rt6i_node = NULL;
-		dst_free(dst);
-	}
+	if (atomic_dec_and_test(&rt->rt6i_ref))
+		dst_free(&rt->u.dst);
 }
 
 
@@ -200,18 +241,16 @@
 
 static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr,
 				     int addrlen, int plen,
-				     unsigned long offset,
-				     struct rt6_info *rt)
-				     
+				     int offset)
 {
-	struct fib6_node *fn;
+	struct fib6_node *fn, *in, *ln;
 	struct fib6_node *pn = NULL;
-	struct fib6_node *in;
-	struct fib6_node *ln;
 	struct rt6key *key;
-	__u32	bit;
-	__u32	dir = 0;
-	__u32	sernum = ++rt_sernum;
+	int	bit;
+       	int	dir = 0;
+	__u32	sernum = fib6_new_sernum();
+
+	RT6_TRACE("fib6_add_1\n");
 
 	/* insert node in tree */
 
@@ -220,146 +259,143 @@
 	if (plen == 0)
 		return fn;
 
-	for (;;) {
-		if (fn == NULL) {
-			ln = node_alloc();
-
-			if (ln == NULL)
-				return NULL;
-			ln->fn_bit = plen;
-			
-			ln->parent = pn;
-			ln->fn_sernum = sernum;
-			rt->rt6i_node = ln;
-
-			if (dir)
-				pn->right = ln;
-			else
-				pn->left  = ln;
-
-			return ln;
-		}
-
+	do {
 		key = (struct rt6key *)((u8 *)fn->leaf + offset);
 
 		/*
 		 *	Prefix match
 		 */
-		if (addr_match(&key->addr, addr, fn->fn_bit)) {
+		if (plen < fn->fn_bit ||
+		    !addr_match(&key->addr, addr, fn->fn_bit))
+			goto insert_above;
 		
-			/*
-			 *	Exact match ?
-			 */
+		/*
+		 *	Exact match ?
+		 */
 			 
-			if (plen == fn->fn_bit) {
-				/* clean up an intermediate node */
-				if ((fn->fn_flags & RTN_RTINFO) == 0) {
-					rt6_release(fn->leaf);
-					fn->leaf = NULL;
-				}
+		if (plen == fn->fn_bit) {
+			/* clean up an intermediate node */
+			if ((fn->fn_flags & RTN_RTINFO) == 0) {
+				rt6_release(fn->leaf);
+				fn->leaf = NULL;
+			}
 			
-				fn->fn_sernum = sernum;
+			fn->fn_sernum = sernum;
 				
-				return fn;
-			}
-
-			/*
-			 *	We have more bits to go
-			 */
-			 
-			if (plen > fn->fn_bit) {
-				/* Walk down on tree. */
-				fn->fn_sernum = sernum;
-				dir = addr_bit_set(addr, fn->fn_bit);
-				pn = fn;
-				fn = dir ? fn->right: fn->left;
-
-				/*
-				 *	Round we go. Note if fn has become
-				 *	NULL then dir is set and fn is handled
-				 *	top of loop.
-				 */
-				continue;
-			}
+			return fn;
 		}
 
 		/*
-		 * split since we don't have a common prefix anymore or 
-		 * we have a less significant route.
-		 * we've to insert an intermediate node on the list
-		 * this new node will point to the one we need to create
-		 * and the current
+		 *	We have more bits to go
 		 */
+			 
+		/* Try to walk down on tree. */
+		fn->fn_sernum = sernum;
+		dir = addr_bit_set(addr, fn->fn_bit);
+		pn = fn;
+		fn = dir ? fn->right: fn->left;
+	} while (fn);
 
-		pn = fn->parent;
+	/*
+	 *	We wlaked to the bottom of tree.
+	 *	Create new leaf node without children.
+	 */
+
+	ln = node_alloc();
+
+	if (ln == NULL)
+		return NULL;
+	ln->fn_bit = plen;
+			
+	ln->parent = pn;
+	ln->fn_sernum = sernum;
+
+	if (dir)
+		pn->right = ln;
+	else
+		pn->left  = ln;
+
+	return ln;
+
+
+insert_above:
+	/*
+	 * split since we don't have a common prefix anymore or 
+	 * we have a less significant route.
+	 * we've to insert an intermediate node on the list
+	 * this new node will point to the one we need to create
+	 * and the current
+	 */
+
+	pn = fn->parent;
 
-		/* find 1st bit in difference between the 2 addrs */
-		bit = addr_diff(addr, &key->addr, addrlen);
+	/* find 1st bit in difference between the 2 addrs.
+
+	   See comment in addr_diff: bit may be an invalid value,
+	   but if it is >= plen, the value is ignored in any case.
+	 */
+	
+	bit = addr_diff(addr, &key->addr, addrlen);
 
+	/* 
+	 *		(intermediate)[in]	
+	 *	          /	   \
+	 *	(new leaf node)[ln] (old node)[fn]
+	 */
+	if (plen > bit) {
+		in = node_alloc();
+		ln = node_alloc();
+		
+		if (in == NULL || ln == NULL) {
+			if (in)
+				node_free(in);
+			if (ln)
+				node_free(ln);
+			return NULL;
+		}
 
 		/* 
-		 *		(intermediate)	
-		 *	          /	   \
-		 *	(new leaf node)    (old node)
+		 * new intermediate node. 
+		 * RTN_RTINFO will
+		 * be off since that an address that chooses one of
+		 * the branches would not match less specific routes
+		 * in the other branch
 		 */
-		if (plen > bit) {
-			in = node_alloc();
-		
-			if (in == NULL)
-				return NULL;
 
-			/* 
-			 * new intermediate node. 
-			 * RTN_RTINFO will
-			 * be off since that an address that chooses one of
-			 * the branches would not match less specific routes
-			 * int the other branch
-			 */
+		in->fn_bit = bit;
 
-			in->fn_bit = bit;
+		in->parent = pn;
+		in->leaf = fn->leaf;
+		atomic_inc(&in->leaf->rt6i_ref);
 
-			in->parent = pn;
-			in->leaf = rt;
+		in->fn_sernum = sernum;
 
-			in->fn_sernum = sernum;
-			atomic_inc(&rt->rt6i_ref);
+		/* update parent pointer */
+		if (dir)
+			pn->right = in;
+		else
+			pn->left  = in;
 
-			/* leaf node */
-			ln = node_alloc();
+		ln->fn_bit = plen;
 
-			if (ln == NULL) {
-				node_free(in);
-				return NULL;
-			}
+		ln->parent = in;
+		fn->parent = in;
 
-			/* update parent pointer */
-			if (dir)
-				pn->right = in;
-			else
-				pn->left  = in;
-
-			ln->fn_bit = plen;
-
-			ln->parent = in;
-			fn->parent = in;
-
-			ln->fn_sernum = sernum;
-
-			if (addr_bit_set(addr, bit)) {
-				in->right = ln;
-				in->left  = fn;
-			} else {
-				in->left  = ln;
-				in->right = fn;
-			}
+		ln->fn_sernum = sernum;
 
-			return ln;
+		if (addr_bit_set(addr, bit)) {
+			in->right = ln;
+			in->left  = fn;
+		} else {
+			in->left  = ln;
+			in->right = fn;
 		}
+	} else { /* plen <= bit */
 
 		/* 
-		 *		(new leaf node)
+		 *		(new leaf node)[ln]
 		 *	          /	   \
-		 *	     (old node)    NULL
+		 *	     (old node)[fn] NULL
 		 */
 
 		ln = node_alloc();
@@ -377,7 +413,6 @@
 			pn->right = ln;
 		else
 			pn->left  = ln;
-		
 
 		if (addr_bit_set(&key->addr, plen))
 			ln->right = fn;
@@ -385,11 +420,8 @@
 			ln->left  = fn;
 
 		fn->parent = ln;
-
-		return ln;
 	}
-
-	return NULL;
+	return ln;
 }
 
 /*
@@ -401,7 +433,6 @@
 	struct rt6_info *iter = NULL;
 	struct rt6_info **ins;
 
-	rt->rt6i_node = fn;
 	ins = &fn->leaf;
 
 	for (iter = fn->leaf; iter; iter=iter->u.next) {
@@ -423,7 +454,7 @@
 				iter->rt6i_expires = rt->rt6i_expires;
 				if (!(rt->rt6i_flags&RTF_EXPIRES)) {
 					iter->rt6i_flags &= ~RTF_EXPIRES;
-					iter->rt6i_expires = rt->rt6i_expires;
+					iter->rt6i_expires = 0;
 				}
 				return -EEXIST;
 			}
@@ -439,8 +470,9 @@
 	 *	insert node
 	 */
 
-	*ins = rt;
 	rt->u.next = iter;
+	*ins = rt;
+	rt->rt6i_node = fn;
 	atomic_inc(&rt->rt6i_ref);
 #ifdef CONFIG_RTNETLINK
 	inet6_rt_notify(RTM_NEWROUTE, rt);
@@ -457,8 +489,8 @@
 
 static __inline__ void fib6_start_gc(struct rt6_info *rt)
 {
-	if ((ip6_fib_timer.expires == 0) &&
-	    (rt->rt6i_flags & (RTF_ADDRCONF | RTF_CACHE))) {
+	if (ip6_fib_timer.expires == 0 &&
+	    (rt->rt6i_flags & (RTF_EXPIRES|RTF_CACHE))) {
 		del_timer(&ip6_fib_timer);
 		ip6_fib_timer.expires = jiffies + ip6_rt_gc_interval;
 		add_timer(&ip6_fib_timer);
@@ -475,67 +507,97 @@
 {
 	struct fib6_node *fn;
 	int err = -ENOMEM;
-	unsigned long offset;
-	
-	offset = (u8*) &rt->rt6i_dst - (u8*) rt;
+
 	fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr),
-			rt->rt6i_dst.plen, offset, rt);
+			rt->rt6i_dst.plen, (u8*) &rt->rt6i_dst - (u8*) rt);
 
-	if (fn == NULL) {
-#if RT_DEBUG >= 2
-		printk(KERN_DEBUG "fib6_add: fn == NULL\n");
-#endif
-		goto out;
-	}
+	if (fn == NULL)
+		return -ENOMEM;
 
+#ifdef CONFIG_IPV6_SUBTREES
 	if (rt->rt6i_src.plen) {
 		struct fib6_node *sn;
 
-#if RT_DEBUG >= 2
-		printk(KERN_DEBUG "fib6_add: src.len > 0\n");
-#endif
-
 		if (fn->subtree == NULL) {
 			struct fib6_node *sfn;
 
-			if (fn->leaf == NULL) {
-				fn->leaf = rt;
-				atomic_inc(&rt->rt6i_ref);
-			}
+			/*
+			 * Create subtree.
+			 *
+			 *		fn[main tree]
+			 *		|
+			 *		sfn[subtree root]
+			 *		   \
+			 *		    sn[new leaf node]
+			 */
 
+			/* Create subtree root node */
 			sfn = node_alloc();
-
 			if (sfn == NULL)
-				goto out;
+				goto st_failure;
 
-			sfn->parent = fn;
 			sfn->leaf = &ip6_null_entry;
+			atomic_inc(&ip6_null_entry.rt6i_ref);
 			sfn->fn_flags = RTN_ROOT;
-			sfn->fn_sernum = ++rt_sernum;
+			sfn->fn_sernum = fib6_new_sernum();
 
-			fn->subtree = sfn;
-		}
+			/* Now add the first leaf node to new subtree */
 
-		offset = (u8*) &rt->rt6i_src - (u8*) rt;
+			sn = fib6_add_1(sfn, &rt->rt6i_src.addr,
+					sizeof(struct in6_addr), rt->rt6i_src.plen,
+					(u8*) &rt->rt6i_src - (u8*) rt);
+
+			if (sn == NULL) {
+				/* If it is failed, discard just allocated
+				   root, and then (in st_failure) stale node
+				   in main tree.
+				 */
+				node_free(sfn);
+				goto st_failure;
+			}
 
-		sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,
-				sizeof(struct in6_addr), rt->rt6i_src.plen,
-				offset, rt);
+			/* Now link new subtree to main tree */
+			sfn->parent = fn;
+			fn->subtree = sfn;
+			if (fn->leaf == NULL) {
+				fn->leaf = rt;
+				atomic_inc(&rt->rt6i_ref);
+			}
+		} else {
+			sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,
+					sizeof(struct in6_addr), rt->rt6i_src.plen,
+					(u8*) &rt->rt6i_src - (u8*) rt);
 
-		if (sn == NULL)
-			goto out;
+			if (sn == NULL)
+				goto st_failure;
+		}
 
 		fn = sn;
 	}
+#endif
 
 	err = fib6_add_rt2node(fn, rt);
 
-	if (err == 0)
+	if (err == 0) {
 		fib6_start_gc(rt);
-out:
+		if (!(rt->rt6i_flags&RTF_CACHE))
+			fib6_prune_clones(fn, rt);
+	}
+
 	if (err)
 		dst_free(&rt->u.dst);
 	return err;
+
+#ifdef CONFIG_IPV6_SUBTREES
+	/* Subtree creation failed, probably main tree node
+	   is orphan. If it is, shot it.
+	 */
+st_failure:
+	if (fn && !(fn->fn_flags&RTN_RTINFO|RTN_ROOT))
+		fib_repair_tree(fn);
+	dst_free(&rt->u.dst);
+	return err;
+#endif
 }
 
 /*
@@ -544,7 +606,7 @@
  */
 
 struct lookup_args {
-	unsigned long	offset;		/* key offset on rt6_info	*/
+	int		offset;		/* key offset on rt6_info	*/
 	struct in6_addr	*addr;		/* search key			*/
 };
 
@@ -576,6 +638,7 @@
 	}
 
 	while ((fn->fn_flags & RTN_ROOT) == 0) {
+#ifdef CONFIG_IPV6_SUBTREES
 		if (fn->subtree) {
 			struct fib6_node *st;
 			struct lookup_args *narg;
@@ -591,6 +654,7 @@
 				}
 			}
 		}
+#endif
 
 		if (fn->fn_flags & RTN_RTINFO) {
 			struct rt6key *key;
@@ -618,8 +682,10 @@
 	args[0].offset = (u8*) &rt->rt6i_dst - (u8*) rt;
 	args[0].addr = daddr;
 
+#ifdef CONFIG_IPV6_SUBTREES
 	args[1].offset = (u8*) &rt->rt6i_src - (u8*) rt;
 	args[1].addr = saddr;
+#endif
 
 	fn = fib6_lookup_1(root, args);
 
@@ -630,12 +696,79 @@
 }
 
 /*
+ *	Get node with sepciafied destination prefix (and source prefix,
+ *	if subtrees are used)
+ */
+
+
+static struct fib6_node * fib6_locate_1(struct fib6_node *root,
+					struct in6_addr *addr,
+					int plen, int offset)
+{
+	struct fib6_node *fn;
+
+	for (fn = root; fn ; ) {
+		struct rt6key *key = (struct rt6key *)((u8 *)fn->leaf + offset);
+
+		/*
+		 *	Prefix match
+		 */
+		if (plen < fn->fn_bit ||
+		    !addr_match(&key->addr, addr, fn->fn_bit))
+			return NULL;
+
+		if (plen == fn->fn_bit)
+			return fn;
+
+		/*
+		 *	We have more bits to go
+		 */
+		if (addr_bit_set(addr, fn->fn_bit))
+			fn = fn->right;
+		else
+			fn = fn->left;
+	}
+	return NULL;
+}
+
+struct fib6_node * fib6_locate(struct fib6_node *root,
+			       struct in6_addr *daddr, int dst_len,
+			       struct in6_addr *saddr, int src_len)
+{
+	struct rt6_info *rt = NULL;
+	struct fib6_node *fn;
+
+	fn = fib6_locate_1(root, daddr, dst_len,
+			   (u8*) &rt->rt6i_dst - (u8*) rt);
+
+#ifdef CONFIG_IPV6_SUBTREES
+	if (src_len) {
+		BUG_TRAP(saddr!=NULL);
+		if (fn == NULL)
+			fn = fn->subtree;
+		if (fn)
+			fn = fib6_locate_1(fn, saddr, src_len,
+					   (u8*) &rt->rt6i_src - (u8*) rt);
+	}
+#endif
+
+	if (fn && fn->fn_flags&RTN_RTINFO)
+		return fn;
+
+	return NULL;
+}
+
+
+/*
  *	Deletion
  *
  */
 
 static struct rt6_info * fib6_find_prefix(struct fib6_node *fn)
 {
+	if (fn->fn_flags&RTN_ROOT)
+		return &ip6_null_entry;
+
 	while(fn) {
 		if(fn->left)
 			return fn->left->leaf;
@@ -643,7 +776,7 @@
 		if(fn->right)
 			return fn->right->leaf;
 
-		fn = fn->subtree;
+		fn = SUBTREE(fn);
 	}
 	return NULL;
 }
@@ -653,428 +786,414 @@
  *	is the node we want to try and remove.
  */
 
-static void fib6_del_2(struct fib6_node *fn)
+static void fib6_repair_tree(struct fib6_node *fn)
 {
-	struct rt6_info *rt;
+	int children;
+	int nstate;
+	struct fib6_node *child, *pn;
+	struct fib6_walker_t *w;
+	int iter = 0;
 
-	fn->fn_flags &= ~RTN_RTINFO;
-	rt6_stats.fib_route_nodes--;
-
-	/*
-	 *	Can't delete a root node
-	 */
-	 
-	if (fn->fn_flags & RTN_TL_ROOT)
-		return;
+	for (;;) {
+		RT6_TRACE("fixing tree: plen=%d iter=%d\n", fn->fn_bit, iter);
+		iter++;
 
-	do {
-		struct fib6_node *pn, *child;
-		int children = 0;
+		BUG_TRAP(!(fn->fn_flags&RTN_RTINFO));
+		BUG_TRAP(!(fn->fn_flags&RTN_TL_ROOT));
+		BUG_TRAP(fn->leaf==NULL);
 
+		children = 0;
 		child = NULL;
+		if (fn->right) child = fn->right, children |= 1;
+		if (fn->left) child = fn->left, children |= 2;
 
-		/*
-		 *	We have a child to left
-		 */
-		 
-		if (fn->left) {
-			children++;
-			child = fn->left;
-		}
-
-		/*
-		 *	To right
-		 */
-		 
-		if (fn->right) {
-			children++;
-			child = fn->right;
-		}
-
-		/*
-		 *	We can't tidy a case of two children.
-		 */
-		if (children > 1) {
-			if (fn->leaf == NULL)
-				goto split_repair;
-			break;
+		if (children == 3 || SUBTREE(fn) 
+#ifdef CONFIG_IPV6_SUBTREES
+		    /* Subtree root (i.e. fn) may have one child */
+		    || (children && fn->fn_flags&RTN_ROOT)
+#endif
+		    ) {
+			fn->leaf = fib6_find_prefix(fn);
+#if RT6_DEBUG >= 2
+			if (fn->leaf==NULL) {
+				BUG_TRAP(fn->leaf);
+				fn->leaf = &ip6_null_entry;
+			}
+#endif
+			atomic_inc(&fn->leaf->rt6i_ref);
+			return;
 		}
 
-		if (fn->fn_flags & RTN_RTINFO)
-			break;
-
-		/*
-		 *	The node we plan to tidy has an stree. Talk about
-		 *	making life hard.
-		 */
-		 
-		if (fn->subtree)
-			goto stree_node;
-
-		/*
-		 *	Up we go
-		 */
-		 
 		pn = fn->parent;
-
-		/*
-		 *	Not a ROOT - we can tidy
-		 */
-		 
-		if ((fn->fn_flags & RTN_ROOT) == 0) {
-			/*
-			 *	Make our child our parents child
-			 */
-			if (pn->left == fn)
-				pn->left = child;
-			else
-				pn->right = child;
-
-			/*
-			 *	Reparent the child
-			 */
+#ifdef CONFIG_IPV6_SUBTREES
+		if (SUBTREE(pn) == fn) {
+			BUG_TRAP(fn->fn_flags&RTN_ROOT);
+			SUBTREE(pn) = NULL;
+			nstate = FWS_L;
+		} else {
+			BUG_TRAP(!(fn->fn_flags&RTN_ROOT));
+#endif
+			if (pn->right == fn) pn->right = child;
+			else if (pn->left == fn) pn->left = child;
+#if RT6_DEBUG >= 2
+			else BUG_TRAP(0);
+#endif
 			if (child)
 				child->parent = pn;
+			nstate = FWS_R;
+#ifdef CONFIG_IPV6_SUBTREES
+		}
+#endif
 
-			/*
-			 *	Discard leaf entries
-			 */
-			if (fn->leaf)
-				rt6_release(fn->leaf);
-		} else {
-			if (children)
-				break;
-			/*
-			 *	No children so no subtree
-			 */
-
-			pn->subtree = NULL;
+		FOR_WALKERS(w) {
+			if (child == NULL) {
+				if (w->root == fn) {
+					w->root = w->node = NULL;
+					RT6_TRACE("W %p adjusted by delroot 1\n", w);
+				} else if (w->node == fn) {
+					RT6_TRACE("W %p adjusted by delnode 1, s=%d/%d\n", w, w->state, nstate);
+					w->node = pn;
+					w->state = nstate;
+				}
+			} else {
+				if (w->root == fn) {
+					w->root = child;
+					RT6_TRACE("W %p adjusted by delroot 2\n", w);
+				}
+				if (w->node == fn) {
+					w->node = child;
+					if (children&2) {
+						RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);
+						w->state = w->state>=FWS_R ? FWS_U : FWS_INIT;
+					} else {
+						RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);
+						w->state = w->state>=FWS_C ? FWS_U : FWS_INIT;
+					}
+				}
+			}
 		}
 
-		/*
-		 *	We are discarding 
-		 */
 		node_free(fn);
-		
-		/*
-		 *	Our merge of entries might propogate further
-		 *	up the tree, so move up a level and retry.
-		 */
-		 
-		fn = pn;
-
-	} while (!(fn->fn_flags & RTN_TL_ROOT));
-
-	return;
-
-stree_node:
-
-	rt6_release(fn->leaf);
-
-split_repair:
-	rt = fib6_find_prefix(fn);
-
-	if (rt == NULL)
-		panic("fib6_del_2: inconsistent tree\n");
+		if (pn->fn_flags&RTN_RTINFO || SUBTREE(pn))
+			return;
 
-	atomic_inc(&rt->rt6i_ref);
-	fn->leaf = rt;
+		rt6_release(pn->leaf);
+		pn->leaf = NULL;
+		fn = pn;
+	}
 }
 
-/*
- *	Remove our entry in the tree. This throws away the route entry
- *	from the list of entries attached to this fib node. It doesn't
- *	expunge from the tree.
- */
-
-static struct fib6_node * fib6_del_1(struct rt6_info *rt)
+static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp)
 {
-	struct fib6_node *fn;
-	
-	fn = rt->rt6i_node;
+	struct fib6_walker_t *w;
+	struct rt6_info *rt = *rtp;
 
-	/* We need a fib node! */
-	if (fn) {
-		struct rt6_info **back;
-		struct rt6_info *lf;
-
-		back = &fn->leaf;
-		
-		/*
-		 *	Walk the leaf entries looking for ourself
-		 */
-		 
-		for(lf = fn->leaf; lf; lf=lf->u.next) {
-			if (rt == lf) {
-				/*
-				 *	Delete this entry.
-				 */
+	RT6_TRACE("fib6_del_route\n");
 
-				*back = lf->u.next;
-#ifdef CONFIG_RTNETLINK
-				inet6_rt_notify(RTM_DELROUTE, lf);
-#endif			
-				rt6_release(lf);
-				rt6_stats.fib_rt_entries--;
-				return fn;
-			}
-			back = &lf->u.next;
+	if (!(rt->rt6i_flags&RTF_CACHE))
+		fib6_prune_clones(fn, rt);
+
+	/* Unlink it */
+	*rtp = rt->u.next;
+	rt->rt6i_node = NULL;
+	rt6_stats.fib_rt_entries--;
+
+	/* Adjust walkers */
+	FOR_WALKERS(w) {
+		if (w->state == FWS_C && w->leaf == rt) {
+			RT6_TRACE("walker %p adjusted by delroute\n", w);
+			w->leaf = rt->u.next;
+			if (w->leaf == NULL)
+				w->state = FWS_U;
 		}
 	}
 
-	return NULL;
+	rt->u.next = NULL;
+
+	/* If it was last route, expunge its radix tree node */
+	if (fn->leaf == NULL) {
+		fn->fn_flags &= ~RTN_RTINFO;
+		rt6_stats.fib_route_nodes--;
+		fib6_repair_tree(fn);
+	}
+
+#ifdef CONFIG_RTNETLINK
+	inet6_rt_notify(RTM_DELROUTE, rt);
+#endif
+	rt6_release(rt);
 }
 
 int fib6_del(struct rt6_info *rt)
 {
-	struct fib6_node *fn;
+	struct fib6_node *fn = rt->rt6i_node;
+	struct rt6_info **rtp;
 
-	fn = fib6_del_1(rt);
-
-	if (fn == NULL)
+#if RT6_DEBUG >= 2
+	if (rt->u.dst.obsolete>0) {
+		BUG_TRAP(rt->u.dst.obsolete>0);
+		return -EFAULT;
+	}
+#endif
+	if (fn == NULL || rt == &ip6_null_entry)
 		return -ENOENT;
 
-	if (fn->leaf == NULL)
-		fib6_del_2(fn);
+	BUG_TRAP(fn->fn_flags&RTN_RTINFO);
 
-	return 0;
+	/*
+	 *	Walk the leaf entries looking for ourself
+	 */
+
+	for (rtp = &fn->leaf; *rtp; rtp = &(*rtp)->u.next) {
+		if (*rtp == rt) {
+			fib6_del_route(fn, rtp);
+			return 0;
+		}
+	}
+	return -ENOENT;
 }
 
 /*
- *	Tree transversal function
+ *	Tree transversal function.
+ *
+ *	Certainly, it is not interrupt safe.
+ *	However, it is internally reenterable wrt itself and fib6_add/fib6_del.
+ *	It means, that we can modify tree during walking
+ *	and use this function for garbage collection, clone pruning,
+ *	cleaning tree when a device goes down etc. etc.	
  *
- *	Wau... It is NOT REENTERABLE!!!!!!! It is cathastrophe. --ANK
+ *	It guarantees that every node will be traversed,
+ *	and that it will be traversed only once.
+ *
+ *	Callback function w->func may return:
+ *	0 -> continue walking.
+ *	positive value -> walking is suspended (used by tree dumps,
+ *	and probably by gc, if it will be split to several slices)
+ *	negative value -> terminate walking.
+ *
+ *	The function itself returns:
+ *	0   -> walk is complete.
+ *	>0  -> walk is incomplete (i.e. suspended)
+ *	<0  -> walk is terminated by an error.
  */
 
-int fib6_walk_count;
-
-void fib6_walk_tree(struct fib6_node *root, f_pnode func, void *arg,
-		    int filter)
+int fib6_walk_continue(struct fib6_walker_t *w)
 {
-	struct fib6_node *fn;
+	struct fib6_node *fn, *pn;
 
-	fn = root;
+	for (;;) {
+		fn = w->node;
+		if (fn == NULL)
+			return 0;
 
-	fib6_walk_count++;
-	
-	do {
-		if (!(fn->fn_flags & RTN_TAG)) {
-			fn->fn_flags |= RTN_TAG;
-			
+		if (w->prune && fn != w->root &&
+		    fn->fn_flags&RTN_RTINFO && w->state < FWS_C) {
+			w->state = FWS_C;
+			w->leaf = fn->leaf;
+		}
+		switch (w->state) {
+#ifdef CONFIG_IPV6_SUBTREES
+		case FWS_S:
+			if (SUBTREE(fn)) {
+				w->node = SUBTREE(fn);
+				continue;
+			}
+			w->state = FWS_L;
+#endif	
+		case FWS_L:
 			if (fn->left) {
-				fn = fn->left;
+				w->node = fn->left;
+				w->state = FWS_INIT;
 				continue;
 			}
-		}
-
-		fn->fn_flags &= ~RTN_TAG;
-
-		if (fn->right) {
-			fn = fn->right;
-			continue;
-		}
-		
-		do {
-			struct fib6_node *node;
-			
-			if (fn->fn_flags & RTN_ROOT)
-				break;
-			node = fn;
-			fn = fn->parent;
-			
-			if (!(node->fn_flags & RTN_TAG)) {
-				if (node->subtree) {
-					fib6_walk_tree(node->subtree, func,
-						       arg, filter);
-				}
-
-				if (!filter ||
-				    (node->fn_flags & RTN_RTINFO))
-					(*func)(node, arg);
+			w->state = FWS_R;
+		case FWS_R:
+			if (fn->right) {
+				w->node = fn->right;
+				w->state = FWS_INIT;
+				continue;
 			}
-			
-		} while (!(fn->fn_flags & RTN_TAG));
-
-	} while (!(fn->fn_flags & RTN_ROOT) || (fn->fn_flags & RTN_TAG));
-
-	fib6_walk_count--;
+			w->state = FWS_C;
+			w->leaf = fn->leaf;
+		case FWS_C:
+			if (w->leaf && fn->fn_flags&RTN_RTINFO) {
+				int err = w->func(w);
+				if (err)
+					return err;
+				continue;
+			}
+			w->state = FWS_U;
+		case FWS_U:
+			if (fn == w->root)
+				return 0;
+			pn = fn->parent;
+			w->node = pn;
+#ifdef CONFIG_IPV6_SUBTREES
+			if (SUBTREE(pn) == fn) {
+				BUG_TRAP(fn->fn_flags&RTN_ROOT);
+				w->state = FWS_L;
+				continue;
+			}
+#endif
+			if (pn->left == fn) {
+				w->state = FWS_R;
+				continue;
+			}
+			if (pn->right == fn) {
+				w->state = FWS_C;
+				w->leaf = w->node->leaf;
+				continue;
+			}
+#if RT6_DEBUG >= 2
+			BUG_TRAP(0);
+#endif
+		}
+	}
 }
 
-/*
- *	Garbage collection
- */
-
-static int fib6_gc_node(struct fib6_node *fn, int timeout)
+int fib6_walk(struct fib6_walker_t *w)
 {
-	struct rt6_info *rt, **back;
-	int more = 0;
-	unsigned long now = jiffies;
-
-	back = &fn->leaf;
+	int res;
 
-	for (rt = fn->leaf; rt;) {
-		if ((rt->rt6i_flags & RTF_CACHE) && atomic_read(&rt->rt6i_use) == 0) {
-			if ((long)(now - rt->rt6i_tstamp) >= timeout) {
-				struct rt6_info *old;
+	w->state = FWS_INIT;
+	w->node = w->root;
 
-				old = rt;
-
-				rt = rt->u.next;
+	fib6_walker_link(w);
+	res = fib6_walk_continue(w);
+	if (res <= 0)
+		fib6_walker_unlink(w);
+	return res;
+}
 
-				*back = rt;
+static int fib6_clean_node(struct fib6_walker_t *w)
+{
+	int res;
+	struct rt6_info *rt;
+	struct fib6_cleaner_t *c = (struct fib6_cleaner_t*)w;
 
-				old->rt6i_node = NULL;
-#ifdef CONFIG_RTNETLINK
-				inet6_rt_notify(RTM_DELROUTE, old);
+	for (rt = w->leaf; rt; rt = rt->u.next) {
+		res = c->func(rt, c->arg);
+		if (res < 0) {
+			w->leaf = rt;
+			res = fib6_del(rt);
+			if (res) {
+#if RT6_DEBUG >= 2
+				printk(KERN_DEBUG "fib6_clean_node: del failed: rt=%p@%p err=%d\n", rt, rt->rt6i_node, res);
 #endif
-				old->u.dst.obsolete = 1;
-				rt6_release(old);
-				rt6_stats.fib_rt_entries--;
 				continue;
 			}
-			more++;
+			return 0;
 		}
+		BUG_TRAP(res==0);
+	}
+	w->leaf = rt;
+	return 0;
+}
 
-		/*
-		 *	check addrconf expiration here.
-		 *
-		 *	BUGGGG Crossing fingers and ...
-		 *	Seems, radix tree walking is absolutely broken,
-		 *	but we will try in any case --ANK
-		 */
-		if ((rt->rt6i_flags&RTF_EXPIRES) && rt->rt6i_expires
-		    && (long)(now - rt->rt6i_expires) > 0) {
-			struct rt6_info *old;
+/*
+ *	Convenient frontend to tree walker.
+ *	
+ *	func is called on each route.
+ *		It may return -1 -> delete this route.
+ *		              0  -> continue walking
+ *
+ *	prune==1 -> only immediate children of node (certainly,
+ *	ignoring pure split nodes) will be scanned.
+ */
 
-			old = rt;
-			rt = rt->u.next;
+void fib6_clean_tree(struct fib6_node *root,
+		     int (*func)(struct rt6_info *, void *arg),
+		     int prune, void *arg)
+{
+	struct fib6_cleaner_t c;
 
-			*back = rt;
+	c.w.root = root;
+	c.w.func = fib6_clean_node;
+	c.w.prune = prune;
+	c.func = func;
+	c.arg = arg;
 
-			old->rt6i_node = NULL;
-#ifdef CONFIG_RTNETLINK
-			inet6_rt_notify(RTM_DELROUTE, old);
-#endif
-			old->u.dst.obsolete = 1;
-			rt6_release(old);
-			rt6_stats.fib_rt_entries--;
-			continue;
-		}
-		back = &rt->u.next;
-		rt = rt->u.next;
+	start_bh_atomic();
+	fib6_walk(&c.w);
+	end_bh_atomic();
+}
+
+static int fib6_prune_clone(struct rt6_info *rt, void *arg)
+{
+	if (rt->rt6i_flags & RTF_CACHE) {
+		RT6_TRACE("pruning clone %p\n", rt);
+		return -1;
 	}
 
-	return more;
+	return 0;
 }
 
-struct fib6_gc_args {
-	unsigned long	timeout;
-	int		more;
-};
+static void fib6_prune_clones(struct fib6_node *fn, struct rt6_info *rt)
+{
+	fib6_clean_tree(fn, fib6_prune_clone, 1, rt);
+}
+
+/*
+ *	Garbage collection
+ */
 
-static void fib6_garbage_collect(struct fib6_node *fn, void *p_arg)
+static struct fib6_gc_args
 {
-	struct fib6_gc_args * args = (struct fib6_gc_args *) p_arg;
+	int			timeout;
+	int			more;
+} gc_args;
 
-	if (fn->fn_flags & RTN_RTINFO) {
-		int more;
+static int fib6_age(struct rt6_info *rt, void *arg)
+{
+	unsigned long now = jiffies;
 
-		more = fib6_gc_node(fn, args->timeout);
+	/* Age clones. Note, that clones are aged out
+	   only if they are not in use now.
+	 */
 
-		if (fn->leaf) {
-			args->more += more;
-			return;
+	if (rt->rt6i_flags & RTF_CACHE) {
+		if (atomic_read(&rt->u.dst.use) == 0 &&
+		    (long)(now - rt->u.dst.lastuse) >= gc_args.timeout) {
+			RT6_TRACE("aging clone %p\n", rt);
+			return -1;
 		}
-
-		rt6_stats.fib_route_nodes--;
-		fn->fn_flags &= ~RTN_RTINFO;
+		gc_args.more++;
+		return 0;
 	}
 
 	/*
-	 *	tree nodes (with no routing information)
+	 *	check addrconf expiration here.
+	 *	They are expired even if they are in use.
 	 */
 
-	if (!fn->subtree && !(fn->fn_flags & RTN_TL_ROOT)) {
-		int children = 0;
-		struct fib6_node *chld = NULL;
-
-		if (fn->left) {
-			children++;
-			chld = fn->left;
-		}
-			
-		if (fn->right) {
-			children++;
-			chld = fn->right;
-		}
-		
-		if ((fn->fn_flags & RTN_ROOT)) {
-			if (children == 0) {
-				struct fib6_node *pn;
-
-				pn = fn->parent;
-				pn->subtree = NULL;
-
-				node_free(fn);
-			}
-			return;
-		}
-
-		if (children <= 1) {
-			struct fib6_node *pn = fn->parent;
-			
-			if (pn->left == fn)
-				pn->left = chld;
-			else
-				pn->right = chld;
-			
-			if (chld)
-				chld->parent = pn;
-			
-			if (fn->leaf)
-				rt6_release(fn->leaf);
-
-			node_free(fn);
-
-			return;
+	if (rt->rt6i_flags&RTF_EXPIRES && rt->rt6i_expires) {
+		if ((long)(now - rt->rt6i_expires) > 0) {
+			RT6_TRACE("expiring %p\n", rt);
+			return -1;
 		}
+		gc_args.more++;
+		return 0;
 	}
 
-	if (fn->leaf == NULL) {
-		struct rt6_info *nrt;
-		
-		nrt = fib6_find_prefix(fn);
-
-		if (nrt == NULL)
-			panic("fib6: inconsistent tree\n");
-
-		atomic_inc(&nrt->rt6i_ref);
-		fn->leaf = nrt;
-	}
+	return 0;
 }
 
 void fib6_run_gc(unsigned long dummy)
 {
-	struct fib6_gc_args arg = {
-		ip6_rt_gc_timeout,
-		0
-	};
+	if (dummy != ~0UL)
+		gc_args.timeout = (int)dummy;
+	else
+		gc_args.timeout = ip6_rt_gc_interval;
 
-	del_timer(&ip6_fib_timer);
+	gc_args.more = 0;
 
-	if (dummy)
-		arg.timeout = dummy;
+	fib6_clean_tree(&ip6_routing_table, fib6_age, 0, NULL);
 
-	if (fib6_walk_count == 0)
-		fib6_walk_tree(&ip6_routing_table, fib6_garbage_collect, &arg, 0);
-	else
-		arg.more = 1;
+	del_timer(&ip6_fib_timer);
 
-	if (arg.more) {
+	ip6_fib_timer.expires = 0;
+	if (gc_args.more) {
 		ip6_fib_timer.expires = jiffies + ip6_rt_gc_interval;
 		add_timer(&ip6_fib_timer);
-	} else {
-		ip6_fib_timer.expires = 0;
 	}
 }
 
@@ -1084,3 +1203,5 @@
 	del_timer(&ip6_fib_timer);
 }
 #endif
+
+
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/ip6_fw.c linux/net/ipv6/ip6_fw.c
--- v2.1.119/linux/net/ipv6/ip6_fw.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv6/ip6_fw.c	Thu Aug 27 19:33:09 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_fw.c,v 1.9 1998/02/12 07:43:42 davem Exp $
+ *	$Id: ip6_fw.c,v 1.10 1998/08/26 12:04:57 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -300,14 +300,19 @@
 	rl->info.uli_u.data = msg->u.data;
 
 	rtmsg.rtmsg_flags = RTF_NONEXTHOP|RTF_POLICY;
-	rt = ip6_route_add(&rtmsg, &err);
+	err = ip6_route_add(&rtmsg);
 
-	/* BUGGGG! rt can point to nowhere. */
-	if (rt == NULL) {
+	if (err) {
 		ip6_fwrule_free(rl);
-		return -ENOMEM;
+		return err;
 	}
 
+	/* The rest will not work for now. --ABK (989725) */
+
+#ifndef notdef
+	ip6_fwrule_free(rl);
+	return -EPERM;
+#else
 	rt->u.dst.error = -EPERM;
 
 	if (msg->policy == IP6_FW_ACCEPT) {
@@ -327,6 +332,7 @@
 	rt->rt6i_flowr = flow_clone((struct flow_rule *)rl);
 
 	return 0;
+#endif
 }
 
 static int ip6_fw_msgrcv(int unit, struct sk_buff *skb)
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/ip6_input.c linux/net/ipv6/ip6_input.c
--- v2.1.119/linux/net/ipv6/ip6_input.c	Tue Jul 28 14:21:10 1998
+++ linux/net/ipv6/ip6_input.c	Thu Aug 27 19:33:09 1998
@@ -6,7 +6,7 @@
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *	Ian P. Morris		<I.P.Morris@soton.ac.uk>
  *
- *	$Id: ip6_input.c,v 1.10 1998/07/15 05:05:34 davem Exp $
+ *	$Id: ip6_input.c,v 1.11 1998/08/26 12:04:59 davem Exp $
  *
  *	Based in linux/net/ipv4/ip_input.c
  *
@@ -37,144 +37,21 @@
 #include <net/ip6_route.h>
 #include <net/addrconf.h>
 
-static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev,
-			 __u8 *nhptr, struct ipv6_options *opt);
-
-struct hdrtype_proc {
-	u8	type;
-	int	(*func) (struct sk_buff **, struct device *dev, __u8 *ptr,
-			 struct ipv6_options *opt);
-} hdrproc_lst[] = {
-
-  /*
-	TODO
-
-	{NEXTHDR_HOP,		ipv6_hop_by_hop}
-	{NEXTHDR_ROUTING,	ipv6_routing_header},
-   */
-	{NEXTHDR_FRAGMENT,	ipv6_reassembly},
-  
-	{NEXTHDR_DEST,		ipv6_dest_opt},
-   /*	
-	{NEXTHDR_AUTH,		ipv6_auth_hdr},
-	{NEXTHDR_ESP,		ipv6_esp_hdr},
-    */
-	{NEXTHDR_MAX,		NULL}
-};
-
-/* New header structures */
-
-
-struct tlvtype_proc {
-	u8	type;
-	int	(*func) (struct sk_buff *, struct device *dev, __u8 *ptr,
-			 struct ipv6_options *opt);
-	/*
-	 *	these functions do NOT update skb->h.raw
-	 */
-
-} tlvprocdestopt_lst[] = {
-	{255,			NULL}
-};
-
-int ip6_dstopt_unknown(struct sk_buff *skb, struct ipv6_tlvtype *hdr)
-{
-	struct in6_addr *daddr;
-	int pos;
-
-	/*
-	 *	unkown destination option type
-	 */
-	
-	pos = (__u8 *) hdr - (__u8 *) skb->nh.raw;
-	
-	/* I think this is correct please check - IPM */
-
-	switch ((hdr->type & 0xC0) >> 6) {
-	case 0: /* ignore */
-		skb->h.raw += hdr->len+2;
-		return 1;
-		
-	case 1: /* drop packet */
-		break;
-
-	case 2: /* send ICMP PARM PROB regardless and drop packet */
-		icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_UNK_OPTION,
-			    pos, skb->dev);
-		break;
-		
-	case 3: /* Send ICMP if not a multicast address and drop packet */
-		daddr = &skb->nh.ipv6h->daddr;
-		if (!(ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST))
-			icmpv6_send(skb, ICMPV6_PARAMPROB,
-				    ICMPV6_UNK_OPTION, pos, skb->dev);
-	};
-	
-	kfree_skb(skb);
-	return 0;
-}
-
-static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb,
-			 struct device *dev, __u8 *nhptr,
-			 struct ipv6_options *opt, void *lastopt)
-{
-	struct ipv6_tlvtype *hdr;
-	struct tlvtype_proc *curr;
-
-	while ((hdr=(struct ipv6_tlvtype *)skb->h.raw) != lastopt) {
-		switch (hdr->type) {
-		case 0: /* TLV encoded Pad1 */
-			skb->h.raw++;
-			break;
-
-		case 1: /* TLV encoded PadN */
-			skb->h.raw += hdr->len+2;
-			break;
-
-		default: /* Other TLV code so scan list */
-			for (curr=procs; curr->type != 255; curr++) {
-				if (curr->type == (hdr->type)) {
-					curr->func(skb, dev, nhptr, opt);
-					skb->h.raw += hdr->len+2;
-					break;
-				}
-			}
-			if (curr->type==255) {
-				if (ip6_dstopt_unknown(skb, hdr) == 0)
-					return 0;
-			}
-			break;
-		}
-	}
-	return 1;
-}
-
-static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev,
-			 __u8 *nhptr, struct ipv6_options *opt)
-{
-	struct sk_buff *skb=*skb_ptr;
-	struct ipv6_destopt_hdr *hdr = (struct ipv6_destopt_hdr *) skb->h.raw;
-	int res = 0;
-	void *lastopt=skb->h.raw+hdr->hdrlen+sizeof(struct ipv6_destopt_hdr);
-
-	skb->h.raw += sizeof(struct ipv6_destopt_hdr);
-	if (ip6_parse_tlv(tlvprocdestopt_lst, skb, dev, nhptr, opt, lastopt))
-		res = hdr->nexthdr;
-	skb->h.raw+=hdr->hdrlen;
-
-	return res;
-}
-
 
 int ipv6_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
 	struct ipv6hdr *hdr;
-	int pkt_len;
+	u32 		pkt_len;
 
-	if (skb->pkt_type == PACKET_OTHERHOST) {
-		kfree_skb(skb);
-		return 0;
-	}
+	if (skb->pkt_type == PACKET_OTHERHOST)
+		goto drop;
+
+	ipv6_statistics.Ip6InReceives++;
+
+	/* Store incoming device index. When the packet will
+	   be queued, we cannot refer to skb->dev anymore.
+	 */
+	((struct inet6_skb_parm *)skb->cb)->iif = dev->ifindex;
 
 	hdr = skb->nh.ipv6h;
 
@@ -183,16 +60,31 @@
 
 	pkt_len = ntohs(hdr->payload_len);
 
-	if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
-		goto err;
+	/* pkt_len may be zero if Jumbo payload option is present */
+	if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
+		if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
+			goto truncated;
+		skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+	}
+
+	if (hdr->nexthdr == NEXTHDR_HOP) {
+		skb->h.raw = (u8*)(hdr+1);
+		if (!ipv6_parse_hopopts(skb, &hdr->nexthdr)) {
+			ipv6_statistics.Ip6InHdrErrors++;
+			return 0;
+		}
+	}
 
-	skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+	if (skb->dst == NULL)
+		ip6_route_input(skb);
 
-	ip6_route_input(skb);
-	
-	return 0;
+	return skb->dst->input(skb);
+
+truncated:
+	ipv6_statistics.Ip6InTruncatedPkts++;
 err:
 	ipv6_statistics.Ip6InHdrErrors++;
+drop:
 	kfree_skb(skb);
 	return 0;
 }
@@ -217,8 +109,7 @@
  *	without calling rawv6.c)
  */
 static struct sock * ipv6_raw_deliver(struct sk_buff *skb,
-				      struct ipv6_options *opt,
-				      int nexthdr, int len)
+				      int nexthdr, unsigned long len)
 {
 	struct in6_addr *saddr;
 	struct in6_addr *daddr;
@@ -253,8 +144,8 @@
 				continue;
 
 			buff = skb_clone(skb, GFP_ATOMIC);
-			buff->sk = sk2;
-			rawv6_rcv(buff, skb->dev, saddr, daddr, opt, len);
+			if (buff)
+				rawv6_rcv(sk2, buff, len);
 		}
 	}
 
@@ -270,10 +161,8 @@
 
 int ip6_input(struct sk_buff *skb)
 {
-	struct ipv6_options *opt = (struct ipv6_options *) skb->cb;
 	struct ipv6hdr *hdr = skb->nh.ipv6h;
 	struct inet6_protocol *ipprot;
-	struct hdrtype_proc *hdrt;
 	struct sock *raw_sk;
 	__u8 *nhptr;
 	int nexthdr;
@@ -281,7 +170,7 @@
 	u8 hash;
 	int len;
 	
-	skb->h.raw += sizeof(struct ipv6hdr);
+	skb->h.raw = skb->nh.raw + sizeof(struct ipv6hdr);
 
 	/*
 	 *	Parse extension headers
@@ -290,64 +179,55 @@
 	nexthdr = hdr->nexthdr;
 	nhptr = &hdr->nexthdr;
 
-	/*
-	 *	check for extension headers
-	 */
-
-st_loop:
+	/* Skip  hop-by-hop options, they are already parsed. */
+	if (nexthdr == NEXTHDR_HOP) {
+		nhptr = (u8*)(hdr+1);
+		nexthdr = *nhptr;
+		skb->h.raw += (nhptr[1]+1)<<3;
+	}
 
-	for (hdrt=hdrproc_lst; hdrt->type != NEXTHDR_MAX; hdrt++) {
-		if (hdrt->type == nexthdr) {
-			if ((nexthdr = hdrt->func(&skb, skb->dev, nhptr, opt))) {
-				nhptr = skb->h.raw;
-				hdr = skb->nh.ipv6h;
-				goto st_loop;
-			}
+	/* This check is sort of optimization.
+	   It would be stupid to detect for optional headers,
+	   which are missing with probability of 200%
+	 */
+	if (nexthdr != IPPROTO_TCP && nexthdr != IPPROTO_UDP) {
+		nhptr = ipv6_parse_exthdrs(&skb, nhptr);
+		if (nhptr == NULL)
 			return 0;
-		}
+		nexthdr = *nhptr;
+		hdr = skb->nh.ipv6h;
 	}
-
 	len = skb->tail - skb->h.raw;
 
-	raw_sk = ipv6_raw_deliver(skb, opt, nexthdr, len);
+	raw_sk = ipv6_raw_deliver(skb, nexthdr, len);
 
 	hash = nexthdr & (MAX_INET_PROTOS - 1);
 	for (ipprot = (struct inet6_protocol *) inet6_protos[hash]; 
 	     ipprot != NULL; 
 	     ipprot = (struct inet6_protocol *) ipprot->next) {
 		struct sk_buff *buff = skb;
-		
+
 		if (ipprot->protocol != nexthdr)
 			continue;
-		
+
 		if (ipprot->copy || raw_sk)
 			buff = skb_clone(skb, GFP_ATOMIC);
-		
-		
-		ipprot->handler(buff, skb->dev, &hdr->saddr, &hdr->daddr,
-				opt, len, 0, ipprot);
+
+		ipprot->handler(buff, len);
 		found = 1;
 	}
-	
+
 	if (raw_sk) {
-		skb->sk = raw_sk;
-		rawv6_rcv(skb, skb->dev, &hdr->saddr, &hdr->daddr, opt, len);
+		rawv6_rcv(raw_sk, skb, len);
 		found = 1;
 	}
-	
+
 	/*
 	 *	not found: send ICMP parameter problem back
 	 */
-	
 	if (!found) {
-		unsigned long offset;
-#if IP6_DEBUG >= 2
-		printk(KERN_DEBUG "proto not found %d\n", nexthdr);
-#endif
-		offset = nhptr - (u8*) hdr;
-		icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_UNK_NEXTHDR,
-			    offset, skb->dev);
-		kfree_skb(skb);
+		ipv6_statistics.Ip6InUnknownProtos++;
+		icmpv6_param_prob(skb, ICMPV6_UNK_NEXTHDR, nhptr);
 	}
 
 	return 0;
@@ -358,6 +238,8 @@
 	struct ipv6hdr *hdr;	
 	int deliver = 0;
 	int discard = 1;
+
+	ipv6_statistics.Ip6InMcastPkts++;
 
 	hdr = skb->nh.ipv6h;
 	if (ipv6_chk_mcast_addr(skb->dev, &hdr->daddr))
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/ip6_output.c linux/net/ipv6/ip6_output.c
--- v2.1.119/linux/net/ipv6/ip6_output.c	Tue Jul 28 14:21:10 1998
+++ linux/net/ipv6/ip6_output.c	Thu Aug 27 19:33:09 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_output.c,v 1.13 1998/07/15 05:05:38 davem Exp $
+ *	$Id: ip6_output.c,v 1.14 1998/08/26 12:05:01 davem Exp $
  *
  *	Based on linux/net/ipv4/ip_output.c
  *
@@ -13,6 +13,14 @@
  *      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.
+ *
+ *	Changes:
+ *	A.N.Kuznetsov	:	airthmetics in fragmentation.
+ *				extension headers are implemented.
+ *				route changes now work.
+ *				ip6_forward does not confuse sniffers.
+ *				etc.
+ *				
  */
 
 #include <linux/errno.h>
@@ -33,6 +41,7 @@
 #include <net/ip6_route.h>
 #include <net/addrconf.h>
 #include <net/rawv6.h>
+#include <net/icmp.h>
 
 static u32	ipv6_fragmentation_id = 1;
 
@@ -59,6 +68,8 @@
 				return 0;
 			}
 		}
+
+		ipv6_statistics.Ip6OutMcastPkts++;
 	}
 
 	if (hh) {
@@ -85,17 +96,40 @@
  */
 
 int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
-	     struct ipv6_options *opt)
+	     struct ipv6_txoptions *opt)
 {
 	struct ipv6_pinfo * np = sk ? &sk->net_pinfo.af_inet6 : NULL;
+	struct in6_addr *first_hop = fl->nl_u.ip6_u.daddr;
 	struct dst_entry *dst = skb->dst;
 	struct ipv6hdr *hdr;
-	int seg_len;
+	u8  proto = fl->proto;
+	int seg_len = skb->len;
 	int hlimit;
 
-	/* Do something with IPv6 options headers here. */
+	if (opt) {
+		int head_room;
 
-	seg_len = skb->len;
+		/* First: exthdrs may take lots of space (~8K for now)
+		   MAX_HEADER is not enough.
+		 */
+		head_room = opt->opt_nflen + opt->opt_flen;
+		seg_len += head_room;
+		head_room += sizeof(struct ipv6hdr) + ((dst->dev->hard_header_len + 15)&~15);
+
+		if (skb_headroom(skb) < head_room) {
+			struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
+			kfree(skb);
+			skb = skb2;
+			if (skb == NULL)
+				return -ENOBUFS;
+			if (sk)
+				skb_set_owner_w(skb, sk);
+		}
+		if (opt->opt_flen)
+			ipv6_push_frag_opts(skb, opt, &proto);
+		if (opt->opt_nflen)
+			ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop);
+	}
 
 	hdr = skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, sizeof(struct ipv6hdr));
 
@@ -117,16 +151,22 @@
 		hlimit = ((struct rt6_info*)dst)->rt6i_hoplimit;
 
 	hdr->payload_len = htons(seg_len);
-	hdr->nexthdr = fl->proto;
+	hdr->nexthdr = proto;
 	hdr->hop_limit = hlimit;
 
 	ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
-	ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);
+	ipv6_addr_copy(&hdr->daddr, first_hop);
 
-	ipv6_statistics.Ip6OutRequests++;
-	dst->output(skb);
+	if (skb->len <= dst->pmtu) {
+		ipv6_statistics.Ip6OutRequests++;
+		dst->output(skb);
+		return 0;
+	}
 
-	return 0;
+	printk(KERN_DEBUG "IPv6: sending pkt_too_big to self\n");
+	icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev);
+	kfree_skb(skb);
+	return -EMSGSIZE;
 }
 
 /*
@@ -166,8 +206,8 @@
 	return 0;
 }
 
-static void ip6_bld_1(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
-		      int hlimit, unsigned short pktlength)
+static struct ipv6hdr * ip6_bld_1(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
+				  int hlimit, unsigned pktlength)
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct ipv6hdr *hdr;
@@ -177,43 +217,56 @@
 	
 	hdr->version = 6;
 	hdr->priority = np->priority;
-	
 	memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
 	
 	hdr->payload_len = htons(pktlength - sizeof(struct ipv6hdr));
-
-	/*
-	 *	FIXME: hop limit has default UNI/MCAST and
-	 *	msgctl settings
-	 */
 	hdr->hop_limit = hlimit;
+	hdr->nexthdr = fl->proto;
 
 	ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
-	ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);	
+	ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);
+	return hdr;
+}
+
+static __inline__ u8 * ipv6_build_fraghdr(struct sk_buff *skb, u8* prev_hdr, unsigned offset)
+{
+	struct frag_hdr *fhdr;
+
+	fhdr = (struct frag_hdr *) skb_put(skb, sizeof(struct frag_hdr));
+
+	fhdr->nexthdr  = *prev_hdr;
+	*prev_hdr = NEXTHDR_FRAGMENT;
+	prev_hdr = &fhdr->nexthdr;
+
+	fhdr->reserved = 0;
+	fhdr->frag_off = htons(offset);
+	fhdr->identification = ipv6_fragmentation_id++;
+	return &fhdr->nexthdr;
 }
 
 static int ip6_frag_xmit(struct sock *sk, inet_getfrag_t getfrag,
 			 const void *data, struct dst_entry *dst,
-			 struct flowi *fl, struct ipv6_options *opt,
-			 int hlimit, int flags, unsigned length)
+			 struct flowi *fl, struct ipv6_txoptions *opt,
+			 struct in6_addr *final_dst,
+			 int hlimit, int flags, unsigned length, int mtu)
 {
-	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct ipv6hdr *hdr;
 	struct sk_buff *last_skb;
-	struct frag_hdr *fhdr;
+	u8 *prev_hdr;
 	int unfrag_len;
-	int payl_len;
 	int frag_len;
 	int last_len;
 	int nfrags;
 	int fhdr_dist;
+	int frag_off;
+	int data_off;
 	int err;
 
 	/*
 	 *	Fragmentation
 	 *
 	 *	Extension header order:
-	 *	Hop-by-hop -> Routing -> Fragment -> rest (...)
+	 *	Hop-by-hop -> Dest0 -> Routing -> Fragment -> Auth -> Dest1 -> rest (...)
 	 *	
 	 *	We must build the non-fragmented part that
 	 *	will be in every packet... this also means
@@ -222,11 +275,11 @@
 	 */
 
 	unfrag_len = sizeof(struct ipv6hdr) + sizeof(struct frag_hdr);
-	payl_len = length;
+	last_len = length;
 
 	if (opt) {
 		unfrag_len += opt->opt_nflen;
-		payl_len += opt->opt_flen;
+		last_len += opt->opt_flen;
 	}
 
 	/*
@@ -235,9 +288,13 @@
 	 *	"integer multiple of 8 octects".
 	 */
 
-	frag_len = (dst->pmtu - unfrag_len) & ~0x7;
+	frag_len = (mtu - unfrag_len) & ~0x7;
 
-	nfrags = payl_len / frag_len;
+	/* Unfragmentable part exceeds mtu. */
+	if (frag_len <= 0)
+		return -EMSGSIZE;
+
+	nfrags = last_len / frag_len;
 
 	/*
 	 *	We must send from end to start because of 
@@ -250,13 +307,25 @@
 	 *	might be a good idea.
 	 */
 
-	last_len = payl_len - (nfrags * frag_len);
+	frag_off = nfrags * frag_len;
+	last_len -= frag_off;
 
 	if (last_len == 0) {
 		last_len = frag_len;
+		frag_off -= frag_len;
 		nfrags--;
 	}
-		
+	data_off = frag_off;
+
+	/* And it is implementation problem: for now we assume, that
+	   all the exthdrs will fit to the first fragment.
+	 */
+	if (opt) {
+		if (frag_len < opt->opt_flen)
+			return -EMSGSIZE;
+		data_off = frag_off - opt->opt_flen;
+	}
+
 	last_skb = sock_alloc_send_skb(sk, unfrag_len + frag_len +
 				       dst->dev->hard_header_len + 15,
 				       0, flags & MSG_DONTWAIT, &err);
@@ -267,41 +336,17 @@
 	last_skb->dst = dst_clone(dst);
 
 	skb_reserve(last_skb, (dst->dev->hard_header_len + 15) & ~15);
-	
-	hdr = (struct ipv6hdr *) skb_put(last_skb, sizeof(struct ipv6hdr));
-	last_skb->nh.ipv6h = hdr;
-
-	hdr->version = 6;
-	hdr->priority = np->priority;
-	
-	memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
-	hdr->payload_len = htons(unfrag_len + frag_len - sizeof(struct ipv6hdr));
 
-	hdr->hop_limit = hlimit;
-
-	hdr->nexthdr = NEXTHDR_FRAGMENT;
+	hdr = ip6_bld_1(sk, last_skb, fl, hlimit, frag_len+unfrag_len);
+	prev_hdr = &hdr->nexthdr;
 
-	ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
-	ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);
+	if (opt && opt->opt_nflen)
+		prev_hdr = ipv6_build_nfrag_opts(last_skb, prev_hdr, opt, final_dst, 0);
 
-#if 0
-	if (opt && opt->srcrt) {
-		hdr->nexthdr = ipv6opt_bld_rthdr(last_skb, opt, daddr,
-						 NEXTHDR_FRAGMENT);
-	}
-#endif
+	prev_hdr = ipv6_build_fraghdr(last_skb, prev_hdr, frag_off);
+	fhdr_dist = prev_hdr - last_skb->data;
 
-	fhdr = (struct frag_hdr *) skb_put(last_skb, sizeof(struct frag_hdr));
-	memset(fhdr, 0, sizeof(struct frag_hdr));
-
-	fhdr->nexthdr  = fl->proto;		
-	fhdr->frag_off = ntohs(nfrags * frag_len);
-	fhdr->identification = ipv6_fragmentation_id++;
-
-	fhdr_dist = (unsigned char *) fhdr - last_skb->data;
-
-	err = getfrag(data, &hdr->saddr, last_skb->tail, nfrags * frag_len,
-		      last_len);
+	err = getfrag(data, &hdr->saddr, last_skb->tail, data_off, last_len);
 
 	if (!err) {
 		while (nfrags--) {
@@ -309,58 +354,60 @@
 			
 			struct frag_hdr *fhdr2;
 				
-#if 0
-			printk(KERN_DEBUG "sending frag %d\n", nfrags);
-#endif
 			skb = skb_copy(last_skb, sk->allocation);
 
-			if (skb == NULL)
+			if (skb == NULL) {
+				ipv6_statistics.Ip6FragFails++;
+				kfree_skb(last_skb);
 				return -ENOMEM;
+			}
 			
+			frag_off -= frag_len;
+			data_off -= frag_len;
+
 			fhdr2 = (struct frag_hdr *) (skb->data + fhdr_dist);
 
 			/* more flag on */
-			fhdr2->frag_off = ntohs(nfrags * frag_len + 1);
+			fhdr2->frag_off = htons(frag_off | 1);
 
-			/*
-			 *	FIXME:
-			 *	if (nfrags == 0)
-			 *	put rest of headers
-			 */
+			/* Write fragmentable exthdrs to the first chunk */
+			if (nfrags == 0 && opt && opt->opt_flen) {
+				ipv6_build_frag_opts(skb, &fhdr2->nexthdr, opt);
+				frag_len -= opt->opt_flen;
+				data_off = 0;
+			}
 
 			err = getfrag(data, &hdr->saddr,skb_put(skb, frag_len),
-				      nfrags * frag_len, frag_len);
+				      data_off, frag_len);
 
 			if (err) {
 				kfree_skb(skb);
 				break;
 			}
 
+			ipv6_statistics.Ip6FragCreates++;
 			ipv6_statistics.Ip6OutRequests++;
 			dst->output(skb);
 		}
 	}
 
 	if (err) {
+		ipv6_statistics.Ip6FragFails++;
 		kfree_skb(last_skb);
 		return -EFAULT;
 	}
 
-#if 0
-	printk(KERN_DEBUG "sending last frag \n");
-#endif
-
-	hdr->payload_len = htons(unfrag_len + last_len - 
-				 sizeof(struct ipv6hdr));
+	hdr->payload_len = htons(unfrag_len + last_len - sizeof(struct ipv6hdr));
 
 	/*
 	 *	update last_skb to reflect the getfrag we did
 	 *	on start.
 	 */
-	
-	last_skb->tail += last_len;
-	last_skb->len += last_len;
 
+	skb_put(last_skb, last_len);
+
+	ipv6_statistics.Ip6FragCreates++;
+	ipv6_statistics.Ip6FragOKs++;
 	ipv6_statistics.Ip6OutRequests++;
 	dst->output(last_skb);
 
@@ -369,42 +416,71 @@
 
 int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
 		   struct flowi *fl, unsigned length,
-		   struct ipv6_options *opt, int hlimit, int flags)
+		   struct ipv6_txoptions *opt, int hlimit, int flags)
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct in6_addr *final_dst = NULL;
 	struct dst_entry *dst;
-	int pktlength;
 	int err = 0;
-	
+	unsigned int pktlength, jumbolen, mtu;
+
 	if (opt && opt->srcrt) {
 		struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
 		final_dst = fl->nl_u.ip6_u.daddr;
 		fl->nl_u.ip6_u.daddr = rt0->addr;
 	}
 
-	dst = NULL;
-
 	if (!fl->oif && ipv6_addr_is_multicast(fl->nl_u.ip6_u.daddr))
 		fl->oif = np->mcast_oif;
-	
-	if (sk->dst_cache)
+
+	dst = NULL;
+	if (sk->dst_cache) {
 		dst = dst_check(&sk->dst_cache, np->dst_cookie);
+		if (dst) {
+			struct rt6_info *rt = (struct rt6_info*)dst_clone(dst);
+
+			/* Yes, checking route validity in not connected
+			   case is not very simple. Take into account,
+			   that we do not support routing by source, TOS,
+			   and MSG_DONTROUTE 		--ANK (980726)
+
+			   1. If route was host route, check that
+			      cached destination is current.
+			      If it is network route, we still may
+			      check its validity using saved pointer
+			      to the last used address: daddr_cache.
+			      We do not want to save whole address now,
+			      (because main consumer of this service
+			       is tcp, which has not this problem),
+			      so that the last trick works only on connected
+			      sockets.
+			   2. oif also should be the same.
+			 */
+			if (((rt->rt6i_dst.plen != 128 ||
+			      ipv6_addr_cmp(fl->fl6_dst, &rt->rt6i_dst.addr))
+			     && (np->daddr_cache == NULL ||
+				 ipv6_addr_cmp(fl->fl6_dst, np->daddr_cache)))
+			    || (fl->oif && fl->oif != dst->dev->ifindex)) {
+				dst_release(dst);
+				dst = NULL;
+			}
+		}
+	}
 
 	if (dst == NULL)
 		dst = ip6_route_output(sk, fl);
 
 	if (dst->error) {
 		ipv6_statistics.Ip6OutNoRoutes++;
-		err = -ENETUNREACH;
-		goto out;
+		dst_release(dst);
+		return -ENETUNREACH;
 	}
 
 	if (fl->nl_u.ip6_u.saddr == NULL) {
 		struct inet6_ifaddr *ifa;
 		
 		ifa = ipv6_get_saddr(dst, fl->nl_u.ip6_u.daddr);
-		
+
 		if (ifa == NULL) {
 #if IP6_DEBUG >= 2
 			printk(KERN_DEBUG "ip6_build_xmit: "
@@ -415,7 +491,6 @@
 		}
 		fl->nl_u.ip6_u.saddr = &ifa->addr;
 	}
-	
 	pktlength = length;
 
 	if (hlimit < 0) {
@@ -427,29 +502,38 @@
 			hlimit = ((struct rt6_info*)dst)->rt6i_hoplimit;
 	}
 
+	jumbolen = 0;
+
 	if (!sk->ip_hdrincl) {
 		pktlength += sizeof(struct ipv6hdr);
 		if (opt)
 			pktlength += opt->opt_flen + opt->opt_nflen;
 
-		/* Due to conservative check made by caller,
-		   pktlength cannot overflow here.
-
-		   When (and if) jumbo option will be implemented
-		   we could try soemething sort of:
+		if (pktlength > 0xFFFF + sizeof(struct ipv6hdr)) {
+			/* Jumbo datagram.
+			   It is assumed, that in the case of sk->ip_hdrincl
+			   jumbo option is supplied by user.
+			 */
+			pktlength += 8;
+			jumbolen = pktlength - sizeof(struct ipv6hdr);
+		}
+	}
 
-		   if (pktlength < length) return -EMSGSIZE;
+	mtu = dst->pmtu;
 
-		*/
-	}
+	/* Critical arithmetic overflow check.
+	   FIXME: may gcc optimize it out? --ANK (980726)
+	 */
+	if (pktlength < length)
+		return -EMSGSIZE;
 
-	if (pktlength <= dst->pmtu) {
+	if (pktlength <= mtu) {
 		struct sk_buff *skb;
 		struct ipv6hdr *hdr;
-		struct device *dev;
+		struct device *dev = dst->dev;
 
 		skb = sock_alloc_send_skb(sk, pktlength + 15 +
-					  dst->dev->hard_header_len, 0,
+					  dev->hard_header_len, 0,
 					  flags & MSG_DONTWAIT, &err);
 
 		if (skb == NULL) {
@@ -457,7 +541,6 @@
 			goto out;
 		}
 
-		dev = dst->dev;
 		skb->dst = dst_clone(dst);
 
 		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
@@ -466,23 +549,22 @@
 		skb->nh.ipv6h = hdr;
 
 		if (!sk->ip_hdrincl) {
-			ip6_bld_1(sk, skb, fl, hlimit, pktlength);
-#if 0
-			if (opt && opt->srcrt) {
-				hdr->nexthdr = ipv6opt_bld_rthdr(skb, opt,
-								 final_dst,
-								 fl->proto);
+			ip6_bld_1(sk, skb, fl, hlimit,
+				  jumbolen ? sizeof(struct ipv6hdr) : pktlength);
+
+			if (opt || jumbolen) {
+				u8 *prev_hdr = &hdr->nexthdr;
+				prev_hdr = ipv6_build_nfrag_opts(skb, prev_hdr, opt, final_dst, jumbolen);
+				if (opt && opt->opt_flen)
+					ipv6_build_frag_opts(skb, prev_hdr, opt);
 			}
-			else
-#endif
-				hdr->nexthdr = fl->proto;
 		}
 
 		skb_put(skb, length);
 		err = getfrag(data, &hdr->saddr,
 			      ((char *) hdr) + (pktlength - length),
 			      0, length);
-		
+
 		if (!err) {
 			ipv6_statistics.Ip6OutRequests++;
 			dst->output(skb);
@@ -491,32 +573,18 @@
 			kfree_skb(skb);
 		}
 	} else {
-		if (sk->ip_hdrincl)
+		if (sk->ip_hdrincl || jumbolen)
 			return -EMSGSIZE;
 
-		/* pktlength includes IPv6 header, not included
-		   in IPv6 payload length.
-		   FIXME are non-fragmentable options included
-		   in packet after defragmentation? If not, we
-		   should subtract opt_nflen also. --ANK
-		 */
-		if (pktlength > 0xFFFF + sizeof(struct ipv6hdr))
-			return -EMSGSIZE;
-
-		err = ip6_frag_xmit(sk, getfrag, data, dst, fl, opt, hlimit,
-				    flags, length);
+		err = ip6_frag_xmit(sk, getfrag, data, dst, fl, opt, final_dst, hlimit,
+				    flags, length, mtu);
 	}
-	
+
 	/*
 	 *	cleanup
 	 */
-  out:
-	
-	if (sk->dst_cache)
-		ip6_dst_store(sk, dst);
-	else
-		dst_release(dst);
-
+out:
+	ip6_dst_store(sk, dst, fl->nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL);
 	return err;
 }
 
@@ -530,20 +598,15 @@
 		if (sk && ra->sel == sel) {
 			if (last) {
 				struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
-				if (skb2) {
-					skb2->sk = last;
-					rawv6_rcv(skb2, skb2->dev, &skb2->nh.ipv6h->saddr,
-						  &skb2->nh.ipv6h->daddr, NULL, skb2->len);
-				}
+				if (skb2)
+					rawv6_rcv(last, skb2, skb2->len);
 			}
 			last = sk;
 		}
 	}
 
 	if (last) {
-		skb->sk = last;
-		rawv6_rcv(skb, skb->dev, &skb->nh.ipv6h->saddr,
-			  &skb->nh.ipv6h->daddr, NULL, skb->len);
+		rawv6_rcv(last, skb, skb->len);
 		return 1;
 	}
 	return 0;
@@ -553,24 +616,16 @@
 {
 	struct dst_entry *dst = skb->dst;
 	struct ipv6hdr *hdr = skb->nh.ipv6h;
-	int size;
+	struct inet6_skb_parm *opt =(struct inet6_skb_parm*)skb->cb;
 	
-	if (ipv6_devconf.forwarding == 0)
+	if (ipv6_devconf.forwarding == 0 && opt->srcrt == 0)
 		goto drop;
 
 	/*
-	 *	check hop-by-hop options present
-	 */
-	/*
-	 *	Note, that NEXTHDR_HOP header must be checked
-	 *	always at the most beginning of ipv6_rcv.
-	 *	The result should be saved somewhere, but
-	 *	we do not it for now. Alas. Let's do it here. --ANK
-	 *
-	 *	Second note: we DO NOT make any processing on
+	 *	We DO NOT make any processing on
 	 *	RA packets, pushing them to user level AS IS
-	 *	without ane WARRANTY that application will able
-	 *	to interpret them. The reson is that we
+	 *	without ane WARRANTY that application will be able
+	 *	to interpret them. The reason is that we
 	 *	cannot make anything clever here.
 	 *
 	 *	We are not end-node, so that if packet contains
@@ -579,42 +634,9 @@
 	 *	cannot be fragmented, because there is no warranty
 	 *	that different fragments will go along one path. --ANK
 	 */
-	if (hdr->nexthdr == NEXTHDR_HOP) {
-		int ra_value = -1;
-		u8 *ptr = (u8*)(skb->nh.ipv6h+1);
-		int len = (ptr[1]+1)<<3;
-
-		if (len + sizeof(struct ipv6hdr) > skb->len)
-			goto drop;
-
-		ptr += 2;
-		len -= 2;
-		while (len > 0) {
-			u8 *opt;
-			int optlen;
-
-			if (ptr[0] == 0) {
-				len--;
-				ptr++;
-				continue;
-			}
-			opt = ptr;
-			optlen = ptr[1]+1;
-
-			len -= optlen;
-			ptr += optlen;
-			if (len < 0)
-				goto drop;
-
-			if (opt[0] == 20) {
-				/* Router Alert as of draft-ietf-ipngwg-ipv6router-alert-04 */
-				if (optlen < 4)
-					goto drop;
-				ra_value = opt[2] + (opt[3]<<8);
-			} else if (!ip6_dstopt_unknown(skb, (struct ipv6_tlvtype*)opt))
-				goto drop;
-		}
-		if (ra_value>=0 && ip6_call_ra_chain(skb, ra_value))
+	if (opt->ra) {
+		u8 *ptr = skb->nh.raw + opt->ra;
+		if (ip6_call_ra_chain(skb, (ptr[2]<<8) + ptr[3]))
 			return 0;
 	}
 
@@ -622,6 +644,8 @@
 	 *	check and decrement ttl
 	 */
 	if (hdr->hop_limit <= 1) {
+		/* Force OUTPUT device used as source address */
+		skb->dev = dst->dev;
 		icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
 			    0, skb->dev);
 
@@ -629,9 +653,10 @@
 		return -ETIMEDOUT;
 	}
 
-	hdr->hop_limit--;
-
-	if (skb->dev == dst->dev && dst->neighbour) {
+	/* IPv6 specs say nothing about it, but it is clear that we cannot
+	   send redirects to source routed frames.
+	 */
+	if (skb->dev == dst->dev && dst->neighbour && opt->srcrt == 0) {
 		struct in6_addr *target = NULL;
 		struct rt6_info *rt;
 		struct neighbour *n = dst->neighbour;
@@ -647,30 +672,40 @@
 		else
 			target = &hdr->daddr;
 
-		ndisc_send_redirect(skb, dst->neighbour, target);
+		/* Limit redirects both by destination (here)
+		   and by source (inside ndisc_send_redirect)
+		 */
+		if (xrlim_allow(dst, 1*HZ))
+			ndisc_send_redirect(skb, n, target);
+	} else if (ipv6_addr_type(&hdr->saddr)&(IPV6_ADDR_MULTICAST|IPV6_ADDR_LOOPBACK
+						|IPV6_ADDR_LINKLOCAL)) {
+		/* This check is security critical. */
+		goto drop;
 	}
-	
-	size = sizeof(struct ipv6hdr) + ntohs(hdr->payload_len);
 
-	if (size > dst->pmtu) {
+	if (skb->len > dst->pmtu) {
+		/* Again, force OUTPUT device used as source address */
+		skb->dev = dst->dev;
 		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev);
+		ipv6_statistics.Ip6InTooBigErrors++;
 		kfree_skb(skb);
 		return -EMSGSIZE;
 	}
 
-	if (skb_headroom(skb) < dst->dev->hard_header_len || skb_cloned(skb)) {
-		struct sk_buff *skb2;
-		skb2 = skb_realloc_headroom(skb, (dst->dev->hard_header_len + 15)&~15);
-		kfree_skb(skb);
-		skb = skb2;
-	}
+	if ((skb = skb_cow(skb, dst->dev->hard_header_len)) == NULL)
+		return 0;
 
-	ipv6_statistics.Ip6ForwDatagrams++;
-	dst->output(skb);
+	hdr = skb->nh.ipv6h;
 
-	return 0;
+	/* Mangling hops number delayed to point after skb COW */
+ 
+	hdr->hop_limit--;
+
+	ipv6_statistics.Ip6OutForwDatagrams++;
+	return dst->output(skb);
 
 drop:
+	ipv6_statistics.Ip6InAddrErrors++;
 	kfree_skb(skb);
 	return -EINVAL;
 }
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/ipv6_sockglue.c linux/net/ipv6/ipv6_sockglue.c
--- v2.1.119/linux/net/ipv6/ipv6_sockglue.c	Tue Jul 28 14:21:10 1998
+++ linux/net/ipv6/ipv6_sockglue.c	Thu Aug 27 19:33:09 1998
@@ -7,7 +7,7 @@
  *
  *	Based on linux/net/ipv4/ip_sockglue.c
  *
- *	$Id: ipv6_sockglue.c,v 1.22 1998/07/15 05:05:39 davem Exp $
+ *	$Id: ipv6_sockglue.c,v 1.23 1998/08/26 12:05:04 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -110,7 +110,7 @@
 		    int optlen)
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-	int val, err;
+	int val, valbool;
 	int retv = -ENOPROTOOPT;
 
 	if(level==SOL_IP && sk->type != SOCK_RAW)
@@ -119,19 +119,20 @@
 	if(level!=SOL_IPV6)
 		goto out;
 
-	if (optval == NULL) {
+	if (optval == NULL)
 		val=0;
-	} else {
-		err = get_user(val, (int *) optval);
-		if(err)
-			return err;
-	}
-	
+	else if (get_user(val, (int *) optval))
+		return -EFAULT;
+
+	valbool = (val!=0);
 
 	switch (optname) {
 
 	case IPV6_ADDRFORM:
 		if (val == PF_INET) {
+			struct ipv6_txoptions *opt;
+			struct sk_buff *pktopt;
+
 			if (sk->protocol != IPPROTO_UDP &&
 			    sk->protocol != IPPROTO_TCP)
 				goto out;
@@ -140,7 +141,7 @@
 				retv = ENOTCONN;
 				goto out;
 			}
-			
+
 			if (!(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED)) {
 				retv = -EADDRNOTAVAIL;
 				goto out;
@@ -153,10 +154,17 @@
 				tp->af_specific = &ipv4_specific;
 				sk->socket->ops = &inet_stream_ops;
 				sk->family = PF_INET;
+				tcp_sync_mss(sk, tp->pmtu_cookie);
 			} else {
 				sk->prot = &udp_prot;
 				sk->socket->ops = &inet_dgram_ops;
 			}
+			opt = xchg(&np->opt, NULL);
+			if (opt)
+				sock_kfree_s(sk, opt, opt->tot_len);
+			pktopt = xchg(&np->pktoptions, NULL);
+			if (pktopt)
+				kfree_skb(pktopt);
 			retv = 0;
 		} else {
 			retv = -EINVAL;
@@ -164,15 +172,85 @@
 		break;
 
 	case IPV6_PKTINFO:
-		np->rxinfo = val;
+		np->rxopt.bits.rxinfo = valbool;
 		retv = 0;
 		break;
 
 	case IPV6_HOPLIMIT:
-		np->rxhlim = val;
+		np->rxopt.bits.rxhlim = valbool;
+		retv = 0;
+		break;
+
+	case IPV6_RTHDR:
+		retv = -EINVAL;
+		if (val >= 0 && val <= 2) {
+			np->rxopt.bits.srcrt = val;
+			retv = 0;
+		}
+		break;
+
+	case IPV6_HOPOPTS:
+		np->rxopt.bits.hopopts = valbool;
+		retv = 0;
+		break;
+
+	case IPV6_AUTHHDR:
+		np->rxopt.bits.authhdr = valbool;
+		retv = 0;
+		break;
+
+	case IPV6_DSTOPTS:
+		np->rxopt.bits.dstopts = valbool;
 		retv = 0;
 		break;
 
+	case IPV6_PKTOPTIONS:
+	{
+		struct ipv6_txoptions *opt = NULL;
+		struct msghdr msg;
+		int junk;
+		struct in6_addr *saddr;
+
+		if (optlen == 0)
+			goto update;
+
+		opt = sock_kmalloc(sk, sizeof(*opt) + optlen, GFP_KERNEL);
+		retv = -ENOBUFS;
+		if (opt == NULL)
+			break;
+
+		memset(opt, 0, sizeof(*opt));
+		opt->tot_len = sizeof(*opt) + optlen;
+		retv = -EFAULT;
+		if (copy_from_user(opt+1, optval, optlen))
+			goto done;
+
+		msg.msg_controllen = optlen;
+		msg.msg_control = (void*)(opt+1);
+
+		retv = datagram_send_ctl(&msg, &junk, &saddr, opt, &junk);
+		if (retv)
+			goto done;
+update:
+		retv = 0;
+		start_bh_atomic();
+		if (opt && sk->type == SOCK_STREAM) {
+			struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+			if ((tcp_connected(sk->state) || sk->state == TCP_SYN_SENT)
+			    && sk->daddr != LOOPBACK4_IPV6) {
+				tp->ext_header_len = opt->opt_flen + opt->opt_nflen;
+				tcp_sync_mss(sk, tp->pmtu_cookie);
+			}
+		}
+		opt = xchg(&np->opt, opt);
+		dst_release(xchg(&sk->dst_cache, NULL));
+		end_bh_atomic();
+
+done:
+		if (opt)
+			sock_kfree_s(sk, opt, opt->tot_len);
+		break;
+	}
 	case IPV6_UNICAST_HOPS:
 		if (val > 255 || val < -1)
 			retv = -EINVAL;
@@ -190,10 +268,9 @@
 			retv = 0;
 		}
 		break;
-		break;
 
 	case IPV6_MULTICAST_LOOP:
-		np->mc_loop = (val != 0);
+		np->mc_loop = valbool;
 		retv = 0;
 		break;
 
@@ -229,12 +306,10 @@
 	case IPV6_DROP_MEMBERSHIP:
 	{
 		struct ipv6_mreq mreq;
-		int err;
 
-		err = copy_from_user(&mreq, optval, sizeof(struct ipv6_mreq));
-		if(err)
+		if (copy_from_user(&mreq, optval, sizeof(struct ipv6_mreq)))
 			return -EFAULT;
-		
+
 		if (optname == IPV6_ADD_MEMBERSHIP)
 			retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
 		else
@@ -253,10 +328,44 @@
 int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval, 
 		    int *optlen)
 {
+	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+	int len;
+
 	if(level==SOL_IP && sk->type != SOCK_RAW)
 		return udp_prot.getsockopt(sk, level, optname, optval, optlen);
 	if(level!=SOL_IPV6)
 		return -ENOPROTOOPT;
+	if (get_user(len, optlen))
+		return -EFAULT;
+	switch (optname) {
+	case IPV6_PKTOPTIONS:
+	{
+		struct msghdr msg;
+		struct sk_buff *skb;
+
+		start_bh_atomic();
+		skb = np->pktoptions;
+		if (skb)
+			atomic_inc(&skb->users);
+		end_bh_atomic();
+
+		if (skb) {
+			int err;
+
+			msg.msg_control = optval;
+			msg.msg_controllen = len;
+			msg.msg_flags = 0;
+			err = datagram_recv_ctl(sk, &msg, skb);
+			kfree_skb(skb);
+			if (err)
+				return err;
+			len -= msg.msg_controllen;
+		} else
+			len = 0;
+		return put_user(len, optlen);
+	}
+	default:
+	}
 	return -EINVAL;
 }
 
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/mcast.c linux/net/ipv6/mcast.c
--- v2.1.119/linux/net/ipv6/mcast.c	Thu May 14 19:47:45 1998
+++ linux/net/ipv6/mcast.c	Thu Aug 27 19:33:09 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: mcast.c,v 1.16 1998/05/07 15:43:10 davem Exp $
+ *	$Id: mcast.c,v 1.17 1998/08/26 12:05:06 davem Exp $
  *
  *	Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c 
  *
@@ -79,7 +79,7 @@
 	if (!(ipv6_addr_type(addr) & IPV6_ADDR_MULTICAST))
 		return -EINVAL;
 
-	mc_lst = kmalloc(sizeof(struct ipv6_mc_socklist), GFP_KERNEL);
+	mc_lst = sock_kmalloc(sk, sizeof(struct ipv6_mc_socklist), GFP_KERNEL);
 
 	if (mc_lst == NULL)
 		return -ENOMEM;
@@ -91,13 +91,15 @@
 	if (ifindex == 0) {
 		struct rt6_info *rt;
 		rt = rt6_lookup(addr, NULL, 0, 0);
-		if (rt)
+		if (rt) {
 			dev = rt->rt6i_dev;
+			dst_release(&rt->u.dst);
+		}
 	} else
 		dev = dev_get_by_index(ifindex);
 
 	if (dev == NULL) {
-		kfree(mc_lst);
+		sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
 		return -ENODEV;
 	}
 
@@ -108,7 +110,7 @@
 	err = ipv6_dev_mc_inc(dev, addr);
 
 	if (err) {
-		kfree(mc_lst);
+		sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
 		return err;
 	}
 
@@ -133,7 +135,7 @@
 			*lnk = mc_lst->next;
 			if ((dev = dev_get_by_index(ifindex)) != NULL)
 				ipv6_dev_mc_dec(dev, &mc_lst->addr);
-			kfree(mc_lst);
+			sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
 			return 0;
 		}
 	}
@@ -153,7 +155,7 @@
 			ipv6_dev_mc_dec(dev, &mc_lst->addr);
 
 		np->ipv6_mc_list = mc_lst->next;
-		kfree(mc_lst);
+		sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
 	}
 }
 
@@ -308,11 +310,19 @@
 {
 	unsigned long delay = resptime;
 
+	/* Do not start timer for addresses with link/host scope */
+	if (ipv6_addr_type(&ma->mca_addr)&(IPV6_ADDR_LINKLOCAL|IPV6_ADDR_LOOPBACK))
+		return;
+
 	if (del_timer(&ma->mca_timer))
 		delay = ma->mca_timer.expires - jiffies;
 
-	if (delay >= resptime)
-		delay = net_random() % resptime;
+	if (delay >= resptime) {
+		if (resptime)
+			delay = net_random() % resptime;
+		else
+			delay = 1;
+	}
 
 	ma->mca_flags |= MAF_TIMER_RUNNING;
 	ma->mca_timer.expires = jiffies + delay;
@@ -325,10 +335,16 @@
 	struct in6_addr *addrp;
 	unsigned long resptime;
 
-	if (len < sizeof(struct icmp6hdr) + sizeof(struct ipv6hdr))
+	if (len < sizeof(struct icmp6hdr) + sizeof(struct in6_addr))
 		return -EINVAL;
 
-	resptime = hdr->icmp6_maxdelay;
+	/* Drop queries with not link local source */
+	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL))
+		return -EINVAL;
+
+	resptime = ntohs(hdr->icmp6_maxdelay);
+	/* Translate milliseconds to jiffies */
+	resptime = (resptime<<10)/(1024000/HZ);
 
 	addrp = (struct in6_addr *) (hdr + 1);
 
@@ -365,7 +381,15 @@
 	struct device *dev;
 	int hash;
 
-	if (len < sizeof(struct icmp6hdr) + sizeof(struct ipv6hdr))
+	/* Our own report looped back. Ignore it. */
+	if (skb->pkt_type == PACKET_LOOPBACK)
+		return 0;
+
+	if (len < sizeof(struct icmp6hdr) + sizeof(struct in6_addr))
+		return -EINVAL;
+
+	/* Drop reports with not link local source */
+	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL))
 		return -EINVAL;
 
 	addrp = (struct in6_addr *) (hdr + 1);
@@ -399,14 +423,25 @@
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	struct inet6_ifaddr *ifp;
-	struct in6_addr *addrp; 
-	int err, len, plen;
+	struct in6_addr *snd_addr;
+	struct in6_addr *addrp;
+	struct in6_addr all_routers;
+	int err, len, payload_len, full_len;
+	u8 ra[8] = { IPPROTO_ICMPV6, 0,
+		     IPV6_TLV_ROUTERALERT, 0, 0, 0,
+		     IPV6_TLV_PADN, 0 };
+
+	snd_addr = addr;
+	if (type == ICMPV6_MGM_REDUCTION) {
+		snd_addr = &all_routers;
+		ipv6_addr_all_routers(&all_routers);
+	}
 
 	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+	payload_len = len + sizeof(ra);
+	full_len = sizeof(struct ipv6hdr) + payload_len;
 
-	plen = sizeof(struct ipv6hdr) + len;
-
-	skb = sock_alloc_send_skb(sk, dev->hard_header_len + plen + 15, 0, 0, &err);
+	skb = sock_alloc_send_skb(sk, dev->hard_header_len + full_len + 15, 0, 0, &err);
 
 	if (skb == NULL)
 		return;
@@ -414,8 +449,8 @@
 	skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
 	if (dev->hard_header) {
 		unsigned char ha[MAX_ADDR_LEN];
-		ndisc_mc_map(addr, ha, dev, 1);
-		dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, plen);
+		ndisc_mc_map(snd_addr, ha, dev, 1);
+		dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, full_len);
 	}
 
 	ifp = ipv6_get_lladdr(dev);
@@ -428,11 +463,9 @@
 		return;
 	}
 
-	ip6_nd_hdr(sk, skb, dev, &ifp->addr, addr, IPPROTO_ICMPV6, len);
+	ip6_nd_hdr(sk, skb, dev, &ifp->addr, snd_addr, NEXTHDR_HOP, payload_len);
 
-	/*
-	 *	need hop-by-hop router alert option.
-	 */
+	memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
 
 	hdr = (struct icmp6hdr *) skb_put(skb, sizeof(struct icmp6hdr));
 	memset(hdr, 0, sizeof(struct icmp6hdr));
@@ -441,11 +474,16 @@
 	addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr));
 	ipv6_addr_copy(addrp, addr);
 
-	hdr->icmp6_cksum = csum_ipv6_magic(&ifp->addr, addr, len,
+	hdr->icmp6_cksum = csum_ipv6_magic(&ifp->addr, snd_addr, len,
 					   IPPROTO_ICMPV6,
 					   csum_partial((__u8 *) hdr, len, 0));
 
 	dev_queue_xmit(skb);
+	if (type == ICMPV6_MGM_REDUCTION)
+		icmpv6_statistics.Icmp6OutGroupMembReductions++;
+	else
+		icmpv6_statistics.Icmp6OutGroupMembResponses++;
+	icmpv6_statistics.Icmp6OutMsgs++;
 }
 
 static void igmp6_join_group(struct ifmcaddr6 *ma)
@@ -455,7 +493,7 @@
 
 	addr_type = ipv6_addr_type(&ma->mca_addr);
 
-	if ((addr_type & IPV6_ADDR_LINKLOCAL))
+	if ((addr_type & (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_LOOPBACK)))
 		return;
 
 	igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- v2.1.119/linux/net/ipv6/ndisc.c	Thu May 14 19:47:45 1998
+++ linux/net/ipv6/ndisc.c	Thu Aug 27 19:33:09 1998
@@ -68,8 +68,7 @@
 #include <net/ndisc.h>
 #include <net/ip6_route.h>
 #include <net/addrconf.h>
-
-
+#include <net/icmp.h>
 
 #include <net/checksum.h>
 #include <linux/proc_fs.h>
@@ -350,6 +349,9 @@
 							      len, 0));
 
 	dev_queue_xmit(skb);
+
+	icmpv6_statistics.Icmp6OutNeighborAdvertisements++;
+	icmpv6_statistics.Icmp6OutMsgs++;
 }        
 
 void ndisc_send_ns(struct device *dev, struct neighbour *neigh,
@@ -410,6 +412,9 @@
 							      len, 0));
 	/* send it! */
 	dev_queue_xmit(skb);
+
+	icmpv6_statistics.Icmp6OutNeighborSolicits++;
+	icmpv6_statistics.Icmp6OutMsgs++;
 }
 
 void ndisc_send_rs(struct device *dev, struct in6_addr *saddr,
@@ -458,6 +463,9 @@
 
 	/* send it! */
 	dev_queue_xmit(skb);
+
+	icmpv6_statistics.Icmp6OutRouterSolicits++;
+	icmpv6_statistics.Icmp6OutMsgs++;
 }
 		   
 
@@ -575,6 +583,7 @@
 
 	if (rt && lifetime == 0) {
 		ip6_del_rt(rt);
+		dst_release(&rt->u.dst);
 		rt = NULL;
 	}
 
@@ -582,11 +591,6 @@
 		ND_PRINTK2("ndisc_rdisc: adding default router\n");
 
 		rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
-
-#if 1
-		/* BUGGGGG! Previous routine can return invalid pointer. */
-		rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
-#endif
 		if (rt == NULL) {
 			ND_PRINTK1("route_add failed\n");
 			return;
@@ -595,6 +599,7 @@
 		neigh = rt->rt6i_nexthop;
 		if (neigh == NULL) {
 			ND_PRINTK1("nd: add default router: null neighbour\n");
+			dst_release(&rt->u.dst);
 			return;
 		}
 		neigh->flags |= NTF_ROUTER;
@@ -658,7 +663,7 @@
 				
 				mtu = htonl(*(__u32 *)(opt+4));
 
-				if (mtu < 576 || mtu > skb->dev->mtu) {
+				if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
 					ND_PRINTK0("NDISC: router "
 						   "announcement with mtu = %d\n",
 						   mtu);
@@ -671,10 +676,7 @@
 					if (rt)
 						rt->u.dst.pmtu = mtu;
 
-					/* BUGGG... Scan routing tables and
-					   adjust mtu on routes going
-					   via this device
-					 */
+					rt6_mtu_change(skb->dev, mtu);
 				}
 			}
                         break;
@@ -689,6 +691,8 @@
                 optlen -= len;
                 opt += len;
         }
+	if (rt)
+		dst_release(&rt->u.dst);
 }
 
 static void ndisc_redirect_rcv(struct sk_buff *skb)
@@ -698,7 +702,6 @@
 	struct in6_addr *dest;
 	struct in6_addr *target;	/* new first hop to destination */
 	struct neighbour *neigh;
-	struct rt6_info *rt;
 	int on_link = 0;
 	int optlen;
 
@@ -740,20 +743,21 @@
 	if (!in6_dev || in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
 		return;
 
-	/* passed validation tests
+	/* passed validation tests */
 
-	   NOTE We should not install redirect if sender did not supply
-	   ll address on link, which requires it. It would break, if
-	   we have non-transitive address resolution protocol.
-	   Fix it later. --ANK
+	/*
+	   We install redirect only if nexthop state is valid.
 	 */
-	rt = rt6_redirect(dest, &skb->nh.ipv6h->saddr, target, skb->dev, on_link);
 
-	if (rt == NULL)
-		return;
-
-	neigh = rt->rt6i_nexthop;
-	ndisc_update(neigh, (u8*)(dest + 1), optlen, ND_OPT_TARGET_LL_ADDR);
+	neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
+	if (neigh) {
+		ndisc_update(neigh, (u8*)(dest + 1), optlen, ND_OPT_TARGET_LL_ADDR);
+		if (neigh->nud_state&NUD_VALID)
+			rt6_redirect(dest, &skb->nh.ipv6h->saddr, neigh, on_link);
+		else
+			__neigh_event_send(neigh, NULL);
+		neigh_release(neigh);
+	}
 }
 
 void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
@@ -773,17 +777,21 @@
 	int hlen;
 
 	dev = skb->dev;
-	rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev->ifindex, 0);
+	rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev->ifindex, 1);
 
-	if (rt == NULL || rt->u.dst.error) {
-		ND_PRINTK1("ndisc_send_redirect: hostunreach\n");
+	if (rt == NULL)
 		return;
-	}
 
 	if (rt->rt6i_flags & RTF_GATEWAY) {
 		ND_PRINTK1("ndisc_send_redirect: not a neighbour\n");
+		dst_release(&rt->u.dst);
 		return;
 	}
+	if (!xrlim_allow(&rt->u.dst, 1*HZ)) {
+		dst_release(&rt->u.dst);
+		return;
+	}
+	dst_release(&rt->u.dst);
 
 	if (dev->addr_len) {
 		if (neigh->nud_state&NUD_VALID) {
@@ -797,7 +805,7 @@
 		}
 	}
 
-	rd_len = min(536 - len, ntohs(skb->nh.ipv6h->payload_len) + 8);
+	rd_len = min(IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, ntohs(skb->nh.ipv6h->payload_len) + 8);
 	rd_len &= ~0x7;
 	len += rd_len;
 
@@ -814,14 +822,14 @@
 		ND_PRINTK1("ndisc_send_redirect: alloc_skb failed\n");
 		return;
 	}
-	
+
 	hlen = 0;
 
 	if (ndisc_build_ll_hdr(buff, dev, &skb->nh.ipv6h->saddr, NULL, len) == 0) {
 		kfree_skb(buff);
 		return;
 	}
-	
+
 	ip6_nd_hdr(sk, buff, dev, &ifp->addr, &skb->nh.ipv6h->saddr,
 		   IPPROTO_ICMPV6, len);
 
@@ -838,9 +846,9 @@
 	ipv6_addr_copy(addrp, target);
 	addrp++;
 	ipv6_addr_copy(addrp, &skb->nh.ipv6h->daddr);
-	
+
 	opt = (u8*) (addrp + 1);
-		
+
 	/*
 	 *	include target_address option
 	 */
@@ -858,12 +866,15 @@
 	opt += 6;
 
 	memcpy(opt, &skb->nh.ipv6h, rd_len - 8);
-	
+
 	icmph->icmp6_cksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
 					     len, IPPROTO_ICMPV6,
 					     csum_partial((u8 *) icmph, len, 0));
 
 	dev_queue_xmit(buff);
+
+	icmpv6_statistics.Icmp6OutRedirects++;
+	icmpv6_statistics.Icmp6OutMsgs++;
 }
 
 static __inline__ struct neighbour *
@@ -894,15 +905,15 @@
 
 static void pndisc_redo(struct sk_buff *skb)
 {
-	ndisc_rcv(skb, skb->dev, &skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,
-		  NULL, skb->len);
+	ndisc_rcv(skb, skb->len);
 	kfree_skb(skb);
 }
 
-int ndisc_rcv(struct sk_buff *skb, struct device *dev,
-	      struct in6_addr *saddr, struct in6_addr *daddr,
-	      struct ipv6_options *opt, unsigned short len)
+int ndisc_rcv(struct sk_buff *skb, unsigned long len)
 {
+	struct device *dev = skb->dev;
+	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
 	struct nd_msg *msg = (struct nd_msg *) skb->h.raw;
 	struct neighbour *neigh;
 	struct inet6_ifaddr *ifp;
@@ -977,7 +988,7 @@
 
 					if (neigh) {
 						ndisc_send_na(dev, neigh, saddr, &msg->target,
-							      1, 0, inc, inc);
+							      0, 0, inc, inc);
 						neigh_release(neigh);
 					}
 				} else {
@@ -1023,13 +1034,14 @@
 					/*
 					 *	Change: router to host
 					 */
-#if 0					
 					struct rt6_info *rt;
-					rt = ndisc_get_dflt_router(skb->dev,
-								   saddr);
-					if (rt)
-						ndisc_del_dflt_router(rt);
-#endif
+					rt = rt6_get_dflt_router(saddr, skb->dev);
+					if (rt) {
+						/* It is safe only because
+						   we aer in BH */
+						dst_release(&rt->u.dst);
+						ip6_del_rt(rt);
+					}
 				}
 			} else {
 				if (msg->icmph.icmp6_router)
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/proc.c linux/net/ipv6/proc.c
--- v2.1.119/linux/net/ipv6/proc.c	Sat May  2 14:19:55 1998
+++ linux/net/ipv6/proc.c	Thu Aug 27 19:33:09 1998
@@ -7,7 +7,7 @@
  *		PROC file system.  This is very similar to the IPv4 version,
  *		except it reports the sockets in the INET6 address family.
  *
- * Version:	$Id: proc.c,v 1.8 1998/04/13 17:06:03 davem Exp $
+ * Version:	$Id: proc.c,v 1.9 1998/08/26 12:05:11 davem Exp $
  *
  * Authors:	David S. Miller (davem@caip.rutgers.edu)
  *
@@ -20,9 +20,11 @@
 #include <linux/socket.h>
 #include <linux/net.h>
 #include <linux/in6.h>
+#include <linux/stddef.h>
 #include <net/sock.h>
 #include <net/tcp.h>
 #include <net/transp_v6.h>
+#include <net/ipv6.h>
 
 /* This is the main implementation workhorse of all these routines. */
 static int get__netinfo6(struct proto *pro, char *buffer, int format, char **start,
@@ -174,5 +176,107 @@
 	len -= offset;
 	if(len > length)
 		len = length;
+	return len;
+}
+
+
+struct snmp6_item
+{
+	char *name;
+	unsigned long *ptr;
+} snmp6_list[] = {
+/* ipv6 mib according to draft-ietf-ipngwg-ipv6-mib-04 */
+#define SNMP6_GEN(x) { #x , &ipv6_statistics.x }
+	SNMP6_GEN(Ip6InReceives),
+	SNMP6_GEN(Ip6InHdrErrors),
+	SNMP6_GEN(Ip6InTooBigErrors),
+	SNMP6_GEN(Ip6InNoRoutes),
+	SNMP6_GEN(Ip6InAddrErrors),
+	SNMP6_GEN(Ip6InUnknownProtos),
+	SNMP6_GEN(Ip6InTruncatedPkts),
+	SNMP6_GEN(Ip6InDiscards),
+	SNMP6_GEN(Ip6InDelivers),
+	SNMP6_GEN(Ip6OutForwDatagrams),
+	SNMP6_GEN(Ip6OutRequests),
+	SNMP6_GEN(Ip6OutDiscards),
+	SNMP6_GEN(Ip6OutNoRoutes),
+	SNMP6_GEN(Ip6ReasmTimeout),
+	SNMP6_GEN(Ip6ReasmReqds),
+	SNMP6_GEN(Ip6ReasmOKs),
+	SNMP6_GEN(Ip6ReasmFails),
+	SNMP6_GEN(Ip6FragOKs),
+	SNMP6_GEN(Ip6FragFails),
+	SNMP6_GEN(Ip6FragCreates),
+	SNMP6_GEN(Ip6InMcastPkts),
+	SNMP6_GEN(Ip6OutMcastPkts),
+#undef SNMP6_GEN
+/* icmpv6 mib according to draft-ietf-ipngwg-ipv6-icmp-mib-02
+
+   Exceptions:  {In|Out}AdminProhibs are removed, because I see
+                no good reasons to account them separately
+		of another dest.unreachs.
+		OutErrs is zero identically.
+		OutEchos too.
+		OutRouterAdvertisements too.
+		OutGroupMembQueries too.
+ */
+#define SNMP6_GEN(x) { #x , &icmpv6_statistics.x }
+	SNMP6_GEN(Icmp6InMsgs),
+	SNMP6_GEN(Icmp6InErrors),
+	SNMP6_GEN(Icmp6InDestUnreachs),
+	SNMP6_GEN(Icmp6InPktTooBigs),
+	SNMP6_GEN(Icmp6InTimeExcds),
+	SNMP6_GEN(Icmp6InParmProblems),
+	SNMP6_GEN(Icmp6InEchos),
+	SNMP6_GEN(Icmp6InEchoReplies),
+	SNMP6_GEN(Icmp6InGroupMembQueries),
+	SNMP6_GEN(Icmp6InGroupMembResponses),
+	SNMP6_GEN(Icmp6InGroupMembReductions),
+	SNMP6_GEN(Icmp6InRouterSolicits),
+	SNMP6_GEN(Icmp6InRouterAdvertisements),
+	SNMP6_GEN(Icmp6InNeighborSolicits),
+	SNMP6_GEN(Icmp6InNeighborAdvertisements),
+	SNMP6_GEN(Icmp6InRedirects),
+	SNMP6_GEN(Icmp6OutMsgs),
+	SNMP6_GEN(Icmp6OutDestUnreachs),
+	SNMP6_GEN(Icmp6OutPktTooBigs),
+	SNMP6_GEN(Icmp6OutTimeExcds),
+	SNMP6_GEN(Icmp6OutParmProblems),
+	SNMP6_GEN(Icmp6OutEchoReplies),
+	SNMP6_GEN(Icmp6OutRouterSolicits),
+	SNMP6_GEN(Icmp6OutNeighborSolicits),
+	SNMP6_GEN(Icmp6OutNeighborAdvertisements),
+	SNMP6_GEN(Icmp6OutRedirects),
+	SNMP6_GEN(Icmp6OutGroupMembResponses),
+	SNMP6_GEN(Icmp6OutGroupMembReductions),
+#undef SNMP6_GEN
+#define SNMP6_GEN(x) { "Udp6" #x , &udp_stats_in6.Udp##x }
+	SNMP6_GEN(InDatagrams),
+	SNMP6_GEN(NoPorts),
+	SNMP6_GEN(InErrors),
+	SNMP6_GEN(OutDatagrams)
+#undef SNMP6_GEN
+};
+
+
+int afinet6_get_snmp(char *buffer, char **start, off_t offset, int length,
+		     int dummy)
+{
+	int len = 0;
+	int i;
+
+	for (i=0; i<sizeof(snmp6_list)/sizeof(snmp6_list[0]); i++)
+		len += sprintf(buffer+len, "%-32s\t%ld\n", snmp6_list[i].name,
+			       *(snmp6_list[i].ptr));
+
+	len -= offset;
+
+	if (len > length)
+		len = length;
+	if(len < 0)
+		len = 0;
+
+	*start = buffer + offset;
+
 	return len;
 }
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/raw.c linux/net/ipv6/raw.c
--- v2.1.119/linux/net/ipv6/raw.c	Thu Aug 27 19:56:31 1998
+++ linux/net/ipv6/raw.c	Thu Aug 27 19:33:09 1998
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/raw.c
  *
- *	$Id: raw.c,v 1.20 1998/07/15 05:05:41 davem Exp $
+ *	$Id: raw.c,v 1.21 1998/08/26 12:05:13 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -156,9 +156,8 @@
 
 	/* Check if the address belongs to the host. */
 	if (addr_type == IPV6_ADDR_MAPPED) {
-		v4addr = addr->sin6_addr.s6_addr32[3];
-		if (inet_addr_type(v4addr) != RTN_LOCAL)
-			return(-EADDRNOTAVAIL);
+		/* Raw sockets are IPv6 only */
+		return(-EADDRNOTAVAIL);
 	} else {
 		if (addr_type != IPV6_ADDR_ANY) {
 			/* ipv4 addr of the socket is invalid.  Only the
@@ -182,10 +181,11 @@
 	return 0;
 }
 
-void rawv6_err(struct sock *sk, int type, int code, unsigned char *buff,
-	       struct in6_addr *saddr, struct in6_addr *daddr)
+void rawv6_err(struct sock *sk, struct sk_buff *skb, struct ipv6hdr *hdr,
+	       struct inet6_skb_parm *opt,
+	       int type, int code, unsigned char *buff, u32 info)
 {
-	if (sk == NULL) 
+	if (sk == NULL)
 		return;
 }
 
@@ -193,12 +193,12 @@
 {
 	/* Charge it to the socket. */
 	if (sock_queue_rcv_skb(sk,skb)<0) {
-		/* ip_statistics.IpInDiscards++; */
+		ipv6_statistics.Ip6InDiscards++;
 		kfree_skb(skb);
 		return 0;
 	}
 
-	/* ip_statistics.IpInDelivers++; */
+	ipv6_statistics.Ip6InDelivers++;
 	return 0;
 }
 
@@ -209,22 +209,11 @@
  *	maybe we could have the network decide uppon a hint if it 
  *	should call raw_rcv for demultiplexing
  */
-int rawv6_rcv(struct sk_buff *skb, struct device *dev,
-	      struct in6_addr *saddr, struct in6_addr *daddr,
-	      struct ipv6_options *opt, unsigned short len)
+int rawv6_rcv(struct sock *sk, struct sk_buff *skb, unsigned long len)
 {
-	struct sock *sk;
-
-	sk = skb->sk;
-
 	if (sk->ip_hdrincl)
 		skb->h.raw = skb->nh.raw;
 
-	if (atomic_read(&sk->sock_readers)) {
-		__skb_queue_tail(&sk->back_log, skb);
-		return 0;
-	}
-
 	rawv6_rcv_skb(sk, skb);
 	return 0;
 }
@@ -255,8 +244,12 @@
 	if (!skb)
 		goto out;
 	
-	copied = min(len, skb->tail - skb->h.raw);
-	
+	copied = skb->tail - skb->h.raw;
+  	if (copied > len) {
+  		copied = len;
+  		msg->msg_flags |= MSG_TRUNC;
+  	}
+
 	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 	sk->stamp=skb->stamp;
 	if (err)
@@ -269,7 +262,7 @@
 		       sizeof(struct in6_addr));
 	}
 
-	if (msg->msg_controllen)
+	if (sk->net_pinfo.af_inet6.rxopt.all)
 		datagram_recv_ctl(sk, msg, skb);
 	err = copied;
 
@@ -332,11 +325,9 @@
 			csum = (__u16 *) (buff + opt->offset);
 			*csum = hdr->cksum;
 		} else {
-			/* 
-			 *  FIXME 
-			 *  signal an error to user via sk->err
-			 */
-			printk(KERN_DEBUG "icmp: cksum offset too big\n");
+			if (net_ratelimit())
+				printk(KERN_DEBUG "icmp: cksum offset too big\n");
+			return -EINVAL;
 		}
 	}	
 	return 0; 
@@ -345,10 +336,10 @@
 
 static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
 {
-	struct ipv6_options opt_space;
+	struct ipv6_txoptions opt_space;
 	struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name;
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-	struct ipv6_options *opt = NULL;
+	struct ipv6_txoptions *opt = NULL;
 	struct in6_addr *saddr = NULL;
 	struct flowi fl;
 	int addr_len = msg->msg_namelen;
@@ -360,11 +351,8 @@
 
 	/* Rough check on arithmetic overflow,
 	   better check is made in ip6_build_xmit
-
-	   When jumbo header will be implemeted we will remove it
-	   at all (len will be size_t)
 	 */
-	if (len < 0 || len > 0xFFFF)
+	if (len < 0)
 		return -EMSGSIZE;
 
 	/* Mirror BSD error message compatibility */
@@ -394,14 +382,6 @@
 			return(-EINVAL);
 
 		daddr = &sin6->sin6_addr;
-		
-		/* BUGGGG If route is not cloned, this check always
-		   fails, hence dst_cache only slows down tramsmission --ANK
-		 */
-		if (sk->dst_cache && ipv6_addr_cmp(daddr, &np->daddr)) {
-			dst_release(sk->dst_cache);
-			sk->dst_cache = NULL;
-		}		
 	} else {
 		if (sk->state != TCP_ESTABLISHED) 
 			return(-EINVAL);
@@ -422,12 +402,14 @@
 
 	if (msg->msg_controllen) {
 		opt = &opt_space;
-		memset(opt, 0, sizeof(struct ipv6_options));
+		memset(opt, 0, sizeof(struct ipv6_txoptions));
 
 		err = datagram_send_ctl(msg, &fl.oif, &saddr, opt, &hlimit);
 		if (err < 0)
 			return err;
 	}
+	if (opt == NULL || !(opt->opt_nflen|opt->opt_flen))
+		opt = np->opt;
 
 	raw_opt = &sk->tp_pinfo.tp_raw;
 
@@ -594,8 +576,9 @@
 
 static void rawv6_close(struct sock *sk, unsigned long timeout)
 {
+	/* See for explanation: raw_close in ipv4/raw.c */
 	sk->state = TCP_CLOSE;
-	ipv6_sock_mc_close(sk);
+	raw_v6_unhash(sk);
 	if (sk->num == IPPROTO_RAW)
 		ip6_ra_control(sk, -1, NULL);
 	sk->dead = 1;
@@ -619,7 +602,7 @@
 	datagram_poll,			/* poll */
 	NULL,				/* ioctl */
 	rawv6_init_sk,			/* init */
-	NULL,				/* destroy */
+	inet6_destroy_sock,		/* destroy */
 	NULL,				/* shutdown */
 	rawv6_setsockopt,		/* setsockopt */
 	rawv6_getsockopt,		/* getsockopt */
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/reassembly.c linux/net/ipv6/reassembly.c
--- v2.1.119/linux/net/ipv6/reassembly.c	Thu May  7 22:51:56 1998
+++ linux/net/ipv6/reassembly.c	Thu Aug 27 19:33:09 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: reassembly.c,v 1.10 1998/04/30 16:24:32 freitag Exp $
+ *	$Id: reassembly.c,v 1.11 1998/08/26 12:05:16 davem Exp $
  *
  *	Based on: net/ipv4/ip_fragment.c
  *
@@ -41,83 +41,145 @@
 #include <net/ndisc.h>
 #include <net/addrconf.h>
 
+int sysctl_ip6frag_high_thresh = 256*1024;
+int sysctl_ip6frag_low_thresh = 192*1024;
+int sysctl_ip6frag_time = IPV6_FRAG_TIMEOUT;
+
+atomic_t ip6_frag_mem = ATOMIC_INIT(0);
+
+struct ipv6_frag {
+	__u16			offset;
+	__u16			len;
+	struct sk_buff		*skb;
+
+	struct frag_hdr		*fhdr;
+
+	struct ipv6_frag	*next;
+};
+
+/*
+ *	Equivalent of ipv4 struct ipq
+ */
+
+struct frag_queue {
+
+	struct frag_queue	*next;
+	struct frag_queue	*prev;
+
+	__u32			id;		/* fragment id		*/
+	struct in6_addr		saddr;
+	struct in6_addr		daddr;
+	struct timer_list	timer;		/* expire timer		*/
+	struct ipv6_frag	*fragments;
+	struct device		*dev;
+	int			iif;
+	__u8			last_in;	/* has first/last segment arrived? */
+#define FIRST_IN		2
+#define LAST_IN			1
+	__u8			nexthdr;
+	__u16			nhoffset;
+};
 
 static struct frag_queue ipv6_frag_queue = {
 	&ipv6_frag_queue, &ipv6_frag_queue,
 	0, {{{0}}}, {{{0}}},
 	{0}, NULL, NULL,
-	0, 0, NULL
+	0, 0, 0, 0
 };
 
+/* Memory Tracking Functions. */
+extern __inline__ void frag_kfree_skb(struct sk_buff *skb)
+{
+	atomic_sub(skb->truesize, &ip6_frag_mem);
+	kfree_skb(skb);
+}
+
+extern __inline__ void frag_kfree_s(void *ptr, int len)
+{
+	atomic_sub(len, &ip6_frag_mem);
+	kfree(ptr);
+}
+ 
+extern __inline__ void *frag_kmalloc(int size, int pri)
+{
+	void *vp = kmalloc(size, pri);
+
+	if(!vp)
+		return NULL;
+	atomic_add(size, &ip6_frag_mem);
+	return vp;
+}
+
+
 static void			create_frag_entry(struct sk_buff *skb, 
-						  struct device *dev,
 						  __u8 *nhptr,
 						  struct frag_hdr *fhdr);
-static int			reasm_frag_1(struct frag_queue *fq, 
-					     struct sk_buff **skb_in);
+static u8 *			reasm_frag(struct frag_queue *fq, 
+					   struct sk_buff **skb_in);
 
 static void			reasm_queue(struct frag_queue *fq, 
 					    struct sk_buff *skb, 
-					    struct frag_hdr *fhdr);
-
-static int reasm_frag(struct frag_queue *fq, struct sk_buff **skb, 
-		      __u8 *nhptr,
-		      struct frag_hdr *fhdr)
-{
-	__u32	expires = jiffies + IPV6_FRAG_TIMEOUT;
-	int nh;
+					    struct frag_hdr *fhdr,
+					    u8 *nhptr);
 
-	if (del_timer(&fq->timer))
-		expires = fq->timer.expires;
+static void			fq_free(struct frag_queue *fq);
 
-	/*
-	 *	We queue the packet even if it's the last.
-	 *	It's a trade off. This allows the reassembly 
-	 *	code to be simpler (=faster) and of the
-	 *	steps we do for queueing the only unnecessary 
-	 *	one it's the kmalloc for a struct ipv6_frag.
-	 *	Feel free to try other alternatives...
-	 */
-	if ((fhdr->frag_off & __constant_htons(0x0001)) == 0) {
-		fq->last_in = 1;
-		fq->nhptr = nhptr;
-	}
-	reasm_queue(fq, *skb, fhdr);
+static void frag_prune(void)
+{
+	struct frag_queue *fq;
 
-	if (fq->last_in) {
-		if ((nh = reasm_frag_1(fq, skb)))
-			return nh;
+	while ((fq = ipv6_frag_queue.next) != &ipv6_frag_queue) {
+		ipv6_statistics.Ip6ReasmFails++;
+		fq_free(fq);
+		if (atomic_read(&ip6_frag_mem) <= sysctl_ip6frag_low_thresh)
+			return;
 	}
-
-	fq->timer.expires = expires;
-	add_timer(&fq->timer);
-	
-	return 0;
+	if (atomic_read(&ip6_frag_mem))
+		printk(KERN_DEBUG "IPv6 frag_prune: memleak\n");
+	atomic_set(&ip6_frag_mem, 0);
 }
 
-int ipv6_reassembly(struct sk_buff **skbp, struct device *dev, __u8 *nhptr,
-		    struct ipv6_options *opt)
+
+u8* ipv6_reassembly(struct sk_buff **skbp, __u8 *nhptr)
 {
 	struct sk_buff *skb = *skbp; 
 	struct frag_hdr *fhdr = (struct frag_hdr *) (skb->h.raw);
 	struct frag_queue *fq;
 	struct ipv6hdr *hdr;
 
+	hdr = skb->nh.ipv6h;
+
+	ipv6_statistics.Ip6ReasmReqds++;
+
+	/* Jumbo payload inhibits frag. header */
+	if (hdr->payload_len==0) {
+		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw);
+		return NULL;
+	}
 	if ((u8 *)(fhdr+1) > skb->tail) {
 		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw);
-		return 0;
+		return NULL;
 	}
-	hdr = skb->nh.ipv6h;
+	if (atomic_read(&ip6_frag_mem) > sysctl_ip6frag_high_thresh)
+		frag_prune();
+
 	for (fq = ipv6_frag_queue.next; fq != &ipv6_frag_queue; fq = fq->next) {
 		if (fq->id == fhdr->identification && 
 		    !ipv6_addr_cmp(&hdr->saddr, &fq->saddr) &&
-		    !ipv6_addr_cmp(&hdr->daddr, &fq->daddr))
-			return reasm_frag(fq, skbp, nhptr,fhdr);
+		    !ipv6_addr_cmp(&hdr->daddr, &fq->daddr)) {
+
+			reasm_queue(fq, skb, fhdr, nhptr);
+
+			if (fq->last_in == (FIRST_IN|LAST_IN))
+				return reasm_frag(fq, skbp);
+
+			return NULL;
+		}
 	}
-	
-	create_frag_entry(skb, dev, nhptr, fhdr);
 
-	return 0;
+	create_frag_entry(skb, nhptr, fhdr);
+
+	return NULL;
 }
 
 
@@ -125,11 +187,13 @@
 {
 	struct ipv6_frag *fp, *back;
 
-	for(fp = fq->fragments; fp; ) {
-		kfree_skb(fp->skb);		
+	del_timer(&fq->timer);
+
+	for (fp = fq->fragments; fp; ) {
+		frag_kfree_skb(fp->skb);
 		back = fp;
 		fp=fp->next;
-		kfree(back);
+		frag_kfree_s(back, sizeof(*back));
 	}
 
 	fq->prev->next = fq->next;
@@ -137,7 +201,7 @@
 
 	fq->prev = fq->next = NULL;
 	
-	kfree(fq);
+	frag_kfree_s(fq, sizeof(*fq));
 }
 
 static void frag_expire(unsigned long data)
@@ -147,33 +211,50 @@
 
 	fq = (struct frag_queue *) data;
 
-	del_timer(&fq->timer);
-
 	frag = fq->fragments;
 
+	ipv6_statistics.Ip6ReasmTimeout++;
+	ipv6_statistics.Ip6ReasmFails++;
+
 	if (frag == NULL) {
 		printk(KERN_DEBUG "invalid fragment queue\n");
 		return;
 	}
 
-	icmpv6_send(frag->skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0,
-		    frag->skb->dev);
+	/* Send error only if the first segment arrived.
+	   (fixed --ANK (980728))
+	 */
+	if (fq->last_in&FIRST_IN) {
+		struct device *dev = dev_get_by_index(fq->iif);
+
+		/*
+		   But use as source device on which LAST ARRIVED
+		   segment was received. And do not use fq->dev
+		   pointer directly, device might already disappeared.
+		 */
+		if (dev) {
+			frag->skb->dev = dev;
+			icmpv6_send(frag->skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0,
+				    dev);
+		}
+	}
 	
 	fq_free(fq);
 }
 
 
-static void create_frag_entry(struct sk_buff *skb, struct device *dev, 
+static void create_frag_entry(struct sk_buff *skb,
 			      __u8 *nhptr,
 			      struct frag_hdr *fhdr)
 {
 	struct frag_queue *fq;
 	struct ipv6hdr *hdr; 
 
-	fq = (struct frag_queue *) kmalloc(sizeof(struct frag_queue), 
-					   GFP_ATOMIC);
+	fq = (struct frag_queue *) frag_kmalloc(sizeof(struct frag_queue), 
+						GFP_ATOMIC);
 
 	if (fq == NULL) {
+		ipv6_statistics.Ip6ReasmFails++;
 		kfree_skb(skb);
 		return;
 	}
@@ -186,38 +267,41 @@
 	ipv6_addr_copy(&fq->saddr, &hdr->saddr);
 	ipv6_addr_copy(&fq->daddr, &hdr->daddr);
 
-	fq->dev = dev;
-
 	/* init_timer has been done by the memset */
 	fq->timer.function = frag_expire;
 	fq->timer.data = (long) fq;
-	fq->timer.expires = jiffies + IPV6_FRAG_TIMEOUT;
-
-	fq->nexthdr = fhdr->nexthdr;
-
+	fq->timer.expires = jiffies + sysctl_ip6frag_time;
 
-	if ((fhdr->frag_off & __constant_htons(0x0001)) == 0) {
-		fq->last_in = 1;
-		fq->nhptr = nhptr;
-	}
-	reasm_queue(fq, skb, fhdr);
+	reasm_queue(fq, skb, fhdr, nhptr);
 
-	fq->prev = ipv6_frag_queue.prev;
-	fq->next = &ipv6_frag_queue;
-	fq->prev->next = fq;
-	ipv6_frag_queue.prev = fq;
-	
-	add_timer(&fq->timer);
+	if (fq->fragments) {
+		fq->prev = ipv6_frag_queue.prev;
+		fq->next = &ipv6_frag_queue;
+		fq->prev->next = fq;
+		ipv6_frag_queue.prev = fq;
+
+		add_timer(&fq->timer);
+	} else
+		frag_kfree_s(fq, sizeof(*fq));
 }
 
 
+/*
+ *	We queue the packet even if it's the last.
+ *	It's a trade off. This allows the reassembly 
+ *	code to be simpler (=faster) and of the
+ *	steps we do for queueing the only unnecessary 
+ *	one it's the kmalloc for a struct ipv6_frag.
+ *	Feel free to try other alternatives...
+ */
+
 static void reasm_queue(struct frag_queue *fq, struct sk_buff *skb, 
-				     struct frag_hdr *fhdr)
+				     struct frag_hdr *fhdr, u8 *nhptr)
 {
 	struct ipv6_frag *nfp, *fp, **bptr;
 
-	nfp = (struct ipv6_frag *) kmalloc(sizeof(struct ipv6_frag), 
-					   GFP_ATOMIC);
+	nfp = (struct ipv6_frag *) frag_kmalloc(sizeof(struct ipv6_frag), 
+						GFP_ATOMIC);
 
 	if (nfp == NULL) {		
 		kfree_skb(skb);
@@ -228,24 +312,40 @@
 	nfp->len = (ntohs(skb->nh.ipv6h->payload_len) -
 		    ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
 
-	if ((u32)nfp->offset + (u32)nfp->len > 65536) {
+	if ((u32)nfp->offset + (u32)nfp->len >= 65536) {
 		icmpv6_param_prob(skb,ICMPV6_HDR_FIELD, (u8*)&fhdr->frag_off); 
 		goto err;
 	}
+	if (fhdr->frag_off & __constant_htons(0x0001)) {
+		/* Check if the fragment is rounded to 8 bytes.
+		 * Required by the RFC.
+		 * ... and would break our defragmentation algorithm 8)
+		 */
+		if (nfp->len & 0x7) {
+			printk(KERN_DEBUG "fragment not rounded to 8bytes\n");
+
+			/*
+			   It is not in specs, but I see no reasons
+			   to send an error in this case. --ANK
+			 */
+			if (nfp->offset == 0)
+				icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 
+						  &skb->nh.ipv6h->payload_len);
+			goto err;
+		}
+	}
 
 	nfp->skb  = skb;
 	nfp->fhdr = fhdr;
-
 	nfp->next = NULL;
 
 	bptr = &fq->fragments;
-	
+
 	for (fp = fq->fragments; fp; fp=fp->next) {
 		if (nfp->offset <= fp->offset)
 			break;
 		bptr = &fp->next;
 	}
-	
 	if (fp && fp->offset == nfp->offset) {
 		if (nfp->len != fp->len) {
 			printk(KERN_DEBUG "reasm_queue: dup with wrong len\n");
@@ -254,29 +354,40 @@
 		/* duplicate. discard it. */
 		goto err;
 	}
-	
-	*bptr = nfp;
-	nfp->next = fp;
 
-#ifdef STRICT_RFC
-	if (fhdr->frag_off & __constant_htons(0x0001)) {
-		/* Check if the fragment is rounded to 8 bytes.
-		 * Required by the RFC.
-		 */
-		if (nfp->len & 0x7) {
-			printk(KERN_DEBUG "fragment not rounded to 8bytes\n");
+	atomic_add(skb->truesize, &ip6_frag_mem);
 
-			icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 
-					  &skb->nh.ipv6h->payload_len);
-			goto err;
-		}
+	/* All the checks are done, fragment is acepted.
+	   Only now we are allowed to update reassembly data!
+	   (fixed --ANK (980728))
+	 */
+
+	/* iif always set to one of the last arrived segment */
+	fq->dev = skb->dev;
+	fq->iif = skb->dev->ifindex;
+
+	/* Last fragment */
+	if ((fhdr->frag_off & __constant_htons(0x0001)) == 0)
+		fq->last_in |= LAST_IN;
+
+	/* First fragment.
+	   nexthdr and nhptr are get from the first fragment.
+	   Moreover, nexthdr is UNDEFINED for all the fragments but the
+	   first one.
+	   (fixed --ANK (980728))
+	 */
+	if (nfp->offset == 0) {
+		fq->nexthdr = fhdr->nexthdr;
+		fq->last_in |= FIRST_IN;
+		fq->nhoffset = nhptr - skb->nh.raw;
 	}
-#endif 
 
+	*bptr = nfp;
+	nfp->next = fp;
 	return;
 
 err:
-	kfree(nfp);
+	frag_kfree_s(nfp, sizeof(*nfp));
 	kfree_skb(skb);
 }
 
@@ -284,20 +395,21 @@
  *	check if this fragment completes the packet
  *	returns true on success
  */
-static int reasm_frag_1(struct frag_queue *fq, struct sk_buff **skb_in)
+static u8* reasm_frag(struct frag_queue *fq, struct sk_buff **skb_in)
 {
 	struct ipv6_frag *fp;
+	struct ipv6_frag *head = fq->fragments;
 	struct ipv6_frag *tail = NULL;
 	struct sk_buff *skb;
 	__u32  offset = 0;
 	__u32  payload_len;
 	__u16  unfrag_len;
 	__u16  copy;
-	int    nh;
+	u8     *nhptr;
 
-	for(fp = fq->fragments; fp; fp=fp->next) {
+	for(fp = head; fp; fp=fp->next) {
 		if (offset != fp->offset)
-			return 0;
+			return NULL;
 
 		offset += fp->len;
 		tail = fp;
@@ -309,31 +421,42 @@
 	 * this means we have all fragments.
 	 */
 
-	unfrag_len = (u8 *) (tail->fhdr) - (u8 *) (tail->skb->nh.ipv6h + 1);
+	/* Unfragmented part is taken from the first segment.
+	   (fixed --ANK (980728))
+	 */
+	unfrag_len = (u8 *) (head->fhdr) - (u8 *) (head->skb->nh.ipv6h + 1);
 
 	payload_len = (unfrag_len + tail->offset + 
 		       (tail->skb->tail - (__u8 *) (tail->fhdr + 1)));
 
-#if 0
-	printk(KERN_DEBUG "reasm: payload len = %d\n", payload_len);
-#endif
+	if (payload_len > 65535) {
+		if (net_ratelimit())
+			printk(KERN_DEBUG "reasm_frag: payload len = %d\n", payload_len);
+		ipv6_statistics.Ip6ReasmFails++;
+		fq_free(fq);
+		return NULL;
+	}
 
 	if ((skb = dev_alloc_skb(sizeof(struct ipv6hdr) + payload_len))==NULL) {
-		printk(KERN_DEBUG "reasm_frag: no memory for reassembly\n");
+		if (net_ratelimit())
+			printk(KERN_DEBUG "reasm_frag: no memory for reassembly\n");
+		ipv6_statistics.Ip6ReasmFails++;
 		fq_free(fq);
-		return 1;
+		return NULL;
 	}
 
 	copy = unfrag_len + sizeof(struct ipv6hdr);
 
 	skb->nh.ipv6h = (struct ipv6hdr *) skb->data;
-
 	skb->dev = fq->dev;
-
-	nh = fq->nexthdr;
-
-	*(fq->nhptr) = nh;
-	memcpy(skb_put(skb, copy), tail->skb->nh.ipv6h, copy);
+	skb->protocol = __constant_htons(ETH_P_IPV6);
+	skb->pkt_type = head->skb->pkt_type;
+	memcpy(skb->cb, head->skb->cb, sizeof(skb->cb));
+	skb->dst = dst_clone(head->skb->dst);
+
+	memcpy(skb_put(skb, copy), head->skb->nh.ipv6h, copy);
+	nhptr = skb->nh.raw + fq->nhoffset;
+	*nhptr = fq->nexthdr;
 
 	skb->h.raw = skb->tail;
 
@@ -351,18 +474,19 @@
 		struct ipv6_frag *back;
 
 		memcpy(skb_put(skb, fp->len), (__u8*)(fp->fhdr + 1), fp->len);
-		kfree_skb(fp->skb);
+		frag_kfree_skb(fp->skb);
 		back = fp;
 		fp=fp->next;
-		kfree(back);
+		frag_kfree_s(back, sizeof(*back));
 	}
-	
+
+	del_timer(&fq->timer);
 	fq->prev->next = fq->next;
 	fq->next->prev = fq->prev;
-
 	fq->prev = fq->next = NULL;
-	
-	kfree(fq);
 
-	return nh;
+	frag_kfree_s(fq, sizeof(*fq));
+
+	ipv6_statistics.Ip6ReasmOKs++;
+	return nhptr;
 }
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/route.c linux/net/ipv6/route.c
--- v2.1.119/linux/net/ipv6/route.c	Tue Jul 28 14:21:10 1998
+++ linux/net/ipv6/route.c	Thu Aug 27 19:33:09 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: route.c,v 1.32 1998/07/25 23:28:52 davem Exp $
+ *	$Id: route.c,v 1.33 1998/08/26 12:05: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
@@ -53,10 +53,19 @@
 
 #if RT6_DEBUG >= 3
 #define RDBG(x) printk x
+#define RT6_TRACE(x...) printk(KERN_DEBUG x)
 #else
 #define RDBG(x)
+#define RT6_TRACE(x...) do { ; } while (0)
 #endif
 
+#if RT6_DEBUG >= 1
+#define BUG_TRAP(x) ({ if (!(x)) { printk("Assertion (" #x ") failed at " __FILE__ "(%d):" __FUNCTION__ "\n", __LINE__); } })
+#else
+#define BUG_TRAP(x) do { ; } while (0)
+#endif
+
+
 int ip6_rt_max_size = 4096;
 int ip6_rt_gc_min_interval = 5*HZ;
 int ip6_rt_gc_timeout = 60*HZ;
@@ -87,16 +96,16 @@
 };
 
 struct rt6_info ip6_null_entry = {
-	{{NULL, ATOMIC_INIT(1), ATOMIC_INIT(1), NULL,
-	  -1, 0, 0, 0, 0, 0, 0, 0, 0,
+	{{NULL, ATOMIC_INIT(1), ATOMIC_INIT(1), &loopback_dev,
+	  -1, 0, 0, 0, 0, 0, 0, 0,
 	  -ENETUNREACH, NULL, NULL,
 	  ip6_pkt_discard, ip6_pkt_discard,
 #ifdef CONFIG_NET_CLS_ROUTE
 	  0,
 #endif
 	  &ip6_dst_ops}},
-	NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0U,
-	255, 0, {NULL}, {{{{0}}}, 0}, {{{{0}}}, 0}
+	NULL, {{{0}}}, RTF_REJECT|RTF_NONEXTHOP, ~0U,
+	255, 0, ATOMIC_INIT(1), {NULL}, {{{{0}}}, 0}, {{{{0}}}, 0}
 };
 
 struct fib6_node ip6_routing_table = {
@@ -123,89 +132,6 @@
 #define ip6_rt_policy (0)
 #endif
 
-static atomic_t	rt6_tbl_lock	= ATOMIC_INIT(0);
-static int	rt6_bh_mask	= 0;
-
-#define RT_BH_REQUEST		1
-#define RT_BH_GC		2
-
-static void __rt6_run_bh(void);
-
-/*
- *	request queue operations
- *	FIFO queue/dequeue
- */
-
-static struct rt6_req request_queue = {
-	0, NULL, &request_queue, &request_queue
-};
-
-static __inline__ void rtreq_queue(struct rt6_req * req)
-{
-	unsigned long flags;
-	struct rt6_req *next = &request_queue;
-
-	save_flags(flags);
-	cli();
-
-	req->prev = next->prev;
-	req->prev->next = req;
-	next->prev = req;
-	req->next = next;
-	restore_flags(flags);
-}
-
-static __inline__ struct rt6_req * rtreq_dequeue(void)
-{
-	struct rt6_req *next = &request_queue;
-	struct rt6_req *head;
-
-	head = next->next;
-
-	if (head == next)
-		return NULL;
-
-	head->next->prev = head->prev;
-	next->next = head->next;
-
-	head->next = NULL;
-	head->prev = NULL;
-
-	return head;
-}
-
-void rtreq_add(struct rt6_info *rt, int operation)
-{
-	struct rt6_req *rtreq;
-
-	rtreq = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
-	
-	if (rtreq == NULL)
-		return;
-
-	memset(rtreq, 0, sizeof(struct rt6_req));
-
-	rtreq->operation = operation;
-	rtreq->ptr = rt;
-	rtreq_queue(rtreq);
-
-	rt6_bh_mask |= RT_BH_REQUEST;
-}
-
-static __inline__ void rt6_lock(void)
-{
-	atomic_inc(&rt6_tbl_lock);
-}
-
-static __inline__ void rt6_unlock(void)
-{
-	if (atomic_dec_and_test(&rt6_tbl_lock) && rt6_bh_mask) {
-		start_bh_atomic();
-		__rt6_run_bh();
-		end_bh_atomic();
-	}
-}
-
 /*
  *	Route lookup
  */
@@ -219,23 +145,19 @@
 
 	if (oif) {
 		for (sprt = rt; sprt; sprt = sprt->u.next) {
-			if (sprt->rt6i_dev) {
-				if (sprt->rt6i_dev->ifindex == oif)
-					return sprt;
-				if (sprt->rt6i_dev->flags&IFF_LOOPBACK)
-					local = sprt;
-			}
+			struct device *dev = sprt->rt6i_dev;
+			if (dev->ifindex == oif)
+				return sprt;
+			if (dev->flags&IFF_LOOPBACK)
+				local = sprt;
 		}
 
 		if (local)
 			return local;
 
-		if (strict) {
-			RDBG(("nomatch & STRICT --> ip6_null_entry\n"));
+		if (strict)
 			return &ip6_null_entry;
-		}
 	}
-	RDBG(("!dev or (no match and !strict) --> rt(%p)\n", rt));
 	return rt;
 }
 
@@ -282,7 +204,7 @@
 				break;
 			};
 
-			if (oif && sprt->rt6i_dev && sprt->rt6i_dev->ifindex == oif) {
+			if (oif && sprt->rt6i_dev->ifindex == oif) {
 				m += 2;
 			}
 
@@ -319,21 +241,40 @@
 }
 
 struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr,
-			    int oif, int flags)
+			    int oif, int strict)
 {
 	struct fib6_node *fn;
 	struct rt6_info *rt;
 
-	rt6_lock();
+	start_bh_atomic();
 	fn = fib6_lookup(&ip6_routing_table, daddr, saddr);
-	rt = rt6_device_match(fn->leaf, oif, flags&RTF_LINKRT);
-	rt6_unlock();
-	return rt;
+	rt = rt6_device_match(fn->leaf, oif, strict);
+	atomic_inc(&rt->u.dst.use);
+	atomic_inc(&rt->u.dst.refcnt);
+	end_bh_atomic();
+
+	rt->u.dst.lastuse = jiffies;
+	if (rt->u.dst.error == 0)
+		return rt;
+	dst_release(&rt->u.dst);
+	return NULL;
+}
+
+static int rt6_ins(struct rt6_info *rt)
+{
+	int err;
+
+	start_bh_atomic();
+	err = fib6_add(&ip6_routing_table, rt);
+	end_bh_atomic();
+
+	return err;
 }
 
 static struct rt6_info *rt6_cow(struct rt6_info *ort, struct in6_addr *daddr,
 				struct in6_addr *saddr)
 {
+	int err;
 	struct rt6_info *rt;
 
 	/*
@@ -351,18 +292,24 @@
 		rt->rt6i_dst.plen = 128;
 		rt->rt6i_flags |= RTF_CACHE;
 
-		if (rt->rt6i_src.plen) {
+#ifdef CONFIG_IPV6_SUBTREES
+		if (rt->rt6i_src.plen && saddr) {
 			ipv6_addr_copy(&rt->rt6i_src.addr, saddr);
 			rt->rt6i_src.plen = 128;
 		}
+#endif
 
 		rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
 
-		rtreq_add(rt, RT_OPER_ADD);
-	} else {
-		rt = &ip6_null_entry;
+		dst_clone(&rt->u.dst);
+		err = rt6_ins(rt);
+		if (err == 0)
+			return rt;
+		rt->u.dst.error = err;
+		return rt;
 	}
-	return rt;
+	dst_clone(&ip6_null_entry.u.dst);
+	return &ip6_null_entry;
 }
 
 #ifdef CONFIG_RT6_POLICY
@@ -397,24 +344,38 @@
 
 #endif
 
+#define BACKTRACK() \
+if (rt == &ip6_null_entry && strict) { \
+       while ((fn = fn->parent) != NULL) { \
+		if (fn->fn_flags & RTN_ROOT) { \
+			dst_clone(&rt->u.dst); \
+			goto out; \
+		} \
+		if (fn->fn_flags & RTN_RTINFO) \
+			goto restart; \
+	} \
+}
+
+
 void ip6_route_input(struct sk_buff *skb)
 {
 	struct fib6_node *fn;
 	struct rt6_info *rt;
-	struct dst_entry *dst;
+	int strict;
+
+	strict = ipv6_addr_type(&skb->nh.ipv6h->daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL);
 
-	RDBG(("ip6_route_input(%p) from %p\n", skb, __builtin_return_address(0)));
-	if ((dst = skb->dst) != NULL)
-		goto looped_back;
-	rt6_lock();
 	fn = fib6_lookup(&ip6_routing_table, &skb->nh.ipv6h->daddr,
 			 &skb->nh.ipv6h->saddr);
 
+restart:
 	rt = fn->leaf;
 
 	if ((rt->rt6i_flags & RTF_CACHE)) {
 		if (ip6_rt_policy == 0) {
-			rt = rt6_device_match(rt, skb->dev->ifindex, 0);
+			rt = rt6_device_match(rt, skb->dev->ifindex, strict);
+			BACKTRACK();
+			dst_clone(&rt->u.dst);
 			goto out;
 		}
 
@@ -425,6 +386,7 @@
 			for (sprt = rt; sprt; sprt = sprt->u.next) {
 				if (rt6_flow_match_in(sprt, skb)) {
 					rt = sprt;
+					dst_clone(&rt->u.dst);
 					goto out;
 				}
 			}
@@ -433,38 +395,38 @@
 	}
 
 	rt = rt6_device_match(rt, skb->dev->ifindex, 0);
+	BACKTRACK();
 
 	if (ip6_rt_policy == 0) {
-		if (!rt->rt6i_nexthop && rt->rt6i_dev &&
-		    ((rt->rt6i_flags & RTF_NONEXTHOP) == 0)) {
+		if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) {
 			rt = rt6_cow(rt, &skb->nh.ipv6h->daddr,
 				     &skb->nh.ipv6h->saddr);
+			goto out;
 		}
+		dst_clone(&rt->u.dst);
 	} else {
 #ifdef CONFIG_RT6_POLICY
 		rt = rt6_flow_lookup_in(rt, skb);
+#else
+		/* NEVER REACHED */
 #endif
 	}
 
 out:
-	dst = dst_clone((struct dst_entry *) rt);
-	rt6_unlock();
-
-	skb->dst = dst;
-looped_back:
-	dst->input(skb);
+	rt->u.dst.lastuse = jiffies;
+	atomic_inc(&rt->u.dst.refcnt);
+	skb->dst = (struct dst_entry *) rt;
 }
 
 struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl)
 {
 	struct fib6_node *fn;
 	struct rt6_info *rt;
-	struct dst_entry *dst;
 	int strict;
 
 	strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL);
 
-	rt6_lock();
+	start_bh_atomic();
 	fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr,
 			 fl->nl_u.ip6_u.saddr);
 
@@ -472,25 +434,10 @@
 	rt = fn->leaf;
 
 	if ((rt->rt6i_flags & RTF_CACHE)) {
-		RDBG(("RTF_CACHE "));
 		if (ip6_rt_policy == 0) {
 			rt = rt6_device_match(rt, fl->oif, strict);
-
-			/* BUGGGG! It is capital bug, that was hidden
-			   by not-cloning multicast routes. However,
-			   the same problem was with link-local addresses.
-			   Fix is the following if-statement,
-			   but it will not properly handle Pedro's subtrees --ANK
-			 */
-			if (rt == &ip6_null_entry && strict) {
-				while ((fn = fn->parent) != NULL) {
-					if (fn->fn_flags & RTN_ROOT)
-						goto out;
-					if (fn->fn_flags & RTN_RTINFO)
-						goto restart;
-				}
-			}
-			RDBG(("devmatch(%p) ", rt));
+			BACKTRACK();
+			dst_clone(&rt->u.dst);
 			goto out;
 		}
 
@@ -501,68 +448,46 @@
 			for (sprt = rt; sprt; sprt = sprt->u.next) {
 				if (rt6_flow_match_out(sprt, sk)) {
 					rt = sprt;
+					dst_clone(&rt->u.dst);
 					goto out;
 				}
 			}
 		}
 #endif
 	}
-	RDBG(("!RTF_CACHE "));
 	if (rt->rt6i_flags & RTF_DEFAULT) {
-		RDBG(("RTF_DEFAULT "));
-		if (rt->rt6i_metric >= IP6_RT_PRIO_ADDRCONF) {
+		if (rt->rt6i_metric >= IP6_RT_PRIO_ADDRCONF)
 			rt = rt6_best_dflt(rt, fl->oif);
-			RDBG(("best_dflt(%p) ", rt));
-		}
 	} else {
 		rt = rt6_device_match(rt, fl->oif, strict);
-		RDBG(("!RTF_DEFAULT devmatch(%p) ", rt));
+		BACKTRACK();
 	}
 
 	if (ip6_rt_policy == 0) {
-		if (!rt->rt6i_nexthop && rt->rt6i_dev &&
-		    ((rt->rt6i_flags & RTF_NONEXTHOP) == 0)) {
+		if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) {
 			rt = rt6_cow(rt, fl->nl_u.ip6_u.daddr,
 				     fl->nl_u.ip6_u.saddr);
-			RDBG(("(!nhop&&rt6i_dev&&!RTF_NONEXTHOP) cow(%p) ", rt));
+			goto out;
 		}
+		dst_clone(&rt->u.dst);
 	} else {
 #ifdef CONFIG_RT6_POLICY
 		rt = rt6_flow_lookup_out(rt, sk, fl);
+#else
+		/* NEVER REACHED */
 #endif
 	}
 
 out:
-	dst = dst_clone((struct dst_entry *) rt);
-	rt6_unlock();
-	RDBG(("dclone/ret(%p)\n", dst));
-	return dst;
-}
-
-
-static void rt6_ins(struct rt6_info *rt)
-{
-	start_bh_atomic();
-	if (atomic_read(&rt6_tbl_lock) == 1)
-		fib6_add(&ip6_routing_table, rt);
-	else
-		rtreq_add(rt, RT_OPER_ADD);
+	rt->u.dst.lastuse = jiffies;
+	atomic_inc(&rt->u.dst.refcnt);
 	end_bh_atomic();
+	return &rt->u.dst;
 }
 
+
 /*
  *	Destination cache support functions
- *
- *	BUGGG! This function is absolutely wrong.
- *	First of all it is never called. (look at include/net/dst.h)
- *	Second, even when it is called rt->rt6i_node == NULL
- *	  ** partially fixed: now dst->obsolete = -1 for IPv6 not cache routes.
- *	Third, even we fixed previous bugs,
- *	it will not work because sernum is incorrectly checked/updated and
- *	it does not handle change of the parent of cloned route.
- *	Purging stray clones is not easy task, it would require
- *	massive remake of ip6_fib.c. Alas...
- *							--ANK
  */
 
 static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
@@ -646,7 +571,7 @@
 	if (idev)
 		return idev->cnf.mtu6;
 	else
-		return 576;
+		return IPV6_MIN_MTU;
 }
 
 static int ipv6_get_hoplimit(struct device *dev)
@@ -664,72 +589,68 @@
  *
  */
 
-struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err)
+int ip6_route_add(struct in6_rtmsg *rtmsg)
 {
+	int err;
 	struct rt6_info *rt;
 	struct device *dev = NULL;
 	int addr_type;
-	
-	if (rtmsg->rtmsg_dst_len > 128 || rtmsg->rtmsg_src_len > 128) {
-		*err = -EINVAL;
-		return NULL;
-	}
+
+	if (rtmsg->rtmsg_dst_len > 128 || rtmsg->rtmsg_src_len > 128)
+		return -EINVAL;
+#ifndef CONFIG_IPV6_SUBTREES
+	if (rtmsg->rtmsg_src_len)
+		return -EINVAL;
+#endif
 	if (rtmsg->rtmsg_metric == 0)
 		rtmsg->rtmsg_metric = IP6_RT_PRIO_USER;
 
-	*err = 0;
-	
 	rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
 
-	if (rt == NULL) {
-		RDBG(("dalloc fails, "));
-		*err = -ENOMEM;
-		return NULL;
-	}
+	if (rt == NULL)
+		return -ENOMEM;
 
 	rt->u.dst.obsolete = -1;
 	rt->rt6i_expires = rtmsg->rtmsg_info;
 
 	addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst);
 
-	if (addr_type & IPV6_ADDR_MULTICAST) {
-		RDBG(("MCAST, "));
+	if (addr_type & IPV6_ADDR_MULTICAST)
 		rt->u.dst.input = ip6_mc_input;
-	} else {
-		RDBG(("!MCAST "));
+	else
 		rt->u.dst.input = ip6_forward;
-	}
 
 	rt->u.dst.output = ip6_output;
 
 	if (rtmsg->rtmsg_ifindex) {
 		dev = dev_get_by_index(rtmsg->rtmsg_ifindex);
-		if (dev == NULL) {
-			*err = -ENODEV;
+		err = -ENODEV;
+		if (dev == NULL)
 			goto out;
-		}
 	}
 
 	ipv6_addr_copy(&rt->rt6i_dst.addr, &rtmsg->rtmsg_dst);
 	rt->rt6i_dst.plen = rtmsg->rtmsg_dst_len;
 	ipv6_wash_prefix(&rt->rt6i_dst.addr, rt->rt6i_dst.plen);
 
+#ifdef CONFIG_IPV6_SUBTREES
 	ipv6_addr_copy(&rt->rt6i_src.addr, &rtmsg->rtmsg_src);
 	rt->rt6i_src.plen = rtmsg->rtmsg_src_len;
 	ipv6_wash_prefix(&rt->rt6i_src.addr, rt->rt6i_src.plen);
+#endif
+
+	rt->rt6i_metric = rtmsg->rtmsg_metric;
 
 	/* We cannot add true routes via loopback here,
 	   they would result in kernel looping; promote them to reject routes
 	 */
 	if ((rtmsg->rtmsg_flags&RTF_REJECT) ||
 	    (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) {
-		dev = dev_get("lo");
+		dev = &loopback_dev;
 		rt->u.dst.output = ip6_pkt_discard;
 		rt->u.dst.input = ip6_pkt_discard;
 		rt->u.dst.error = -ENETUNREACH;
 		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
-		rt->rt6i_metric = rtmsg->rtmsg_metric;
-		rt->rt6i_dev = dev;
 		goto install_route;
 	}
 
@@ -746,50 +667,44 @@
 
 			/* IPv6 strictly inhibits using not link-local
 			   addresses as nexthop address.
+			   Otherwise, router will not able to send redirects.
 			   It is very good, but in some (rare!) curcumstances
-			   (SIT, NBMA NOARP links) it is handy to allow
-			   some exceptions.
+			   (SIT, PtP, NBMA NOARP links) it is handy to allow
+			   some exceptions. --ANK
 			 */
-			if (!(gwa_type&IPV6_ADDR_UNICAST)) {
-				*err = -EINVAL;
+			err = -EINVAL;
+			if (!(gwa_type&IPV6_ADDR_UNICAST))
 				goto out;
-			}
 
-			grt = rt6_lookup(gw_addr, NULL, rtmsg->rtmsg_ifindex, RTF_LINKRT);
+			grt = rt6_lookup(gw_addr, NULL, rtmsg->rtmsg_ifindex, 1);
 
-			if (grt == NULL || (grt->rt6i_flags&RTF_GATEWAY)) {
-				*err = -EHOSTUNREACH;
+			err = -EHOSTUNREACH;
+			if (grt == NULL)
 				goto out;
-			}
+			if (!(grt->rt6i_flags&RTF_GATEWAY))
+				err = 0;
 			dev = grt->rt6i_dev;
+			dst_release(&grt->u.dst);
+
+			if (err)
+				goto out;
 		}
-		if (dev == NULL || (dev->flags&IFF_LOOPBACK)) {
-			*err = -EINVAL;
+		err = -EINVAL;
+		if (dev == NULL || (dev->flags&IFF_LOOPBACK))
 			goto out;
-		}
 	}
 
-	if (dev == NULL) {
-		RDBG(("!dev, "));
-		*err = -ENODEV;
+	err = -ENODEV;
+	if (dev == NULL)
 		goto out;
-	}
 
 	if (rtmsg->rtmsg_flags & (RTF_GATEWAY|RTF_NONEXTHOP)) {
 		rt->rt6i_nexthop = ndisc_get_neigh(dev, &rt->rt6i_gateway);
-		if (rt->rt6i_nexthop == NULL) {
-			RDBG(("!nxthop, "));
-			*err = -ENOMEM;
+		err = -ENOMEM;
+		if (rt->rt6i_nexthop == NULL)
 			goto out;
-		}
-		RDBG(("nxthop, "));
 	}
 
-	rt->rt6i_metric = rtmsg->rtmsg_metric;
-
-	rt->rt6i_dev = dev;
-	rt->u.dst.pmtu = ipv6_get_mtu(dev);
-	rt->u.dst.rtt = TCP_TIMEOUT_INIT;
 	if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr))
 		rt->rt6i_hoplimit = IPV6_DEFAULT_MCASTHOPS;
 	else
@@ -797,153 +712,59 @@
 	rt->rt6i_flags = rtmsg->rtmsg_flags;
 
 install_route:
-	RDBG(("rt6ins(%p) ", rt));
-
-	rt6_lock();
-	rt6_ins(rt);
-	rt6_unlock();
-
-	/* BUGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG!
-
-	   If rt6_ins will fail (and it occurs regularly f.e. if route
-	   already existed), the route will be freed -> Finita.
-	   Crash. No recovery. NO FIX. Unfortunately, it is not the only
-	   place will it is fatal. It is sad, I believed this
-	   code is a bit more accurate :-(
-
-	   Really, the problem can be solved in two ways:
-
-	   * As I did in old 2.0 IPv4: to increase use count and force
-	     user to destroy stray route. It requires some care,
-	     well, much more care.
-	   * Second and the best: to get rid of this damn backlogging
-	     system. I wonder why Pedro so liked it. It was the most
-	     unhappy day when I invented it (well, by a strange reason
-	     I believed that it is very clever :-)),
-	     and when I managed to clean IPv4 of this crap,
-	     it was really great win.
-	     BTW I forgot how 2.0 route/arp works :-) :-)
-	                                                               --ANK
-	 */
+	rt->u.dst.pmtu = ipv6_get_mtu(dev);
+	rt->u.dst.rtt = TCP_TIMEOUT_INIT;
+	rt->rt6i_dev = dev;
+	return rt6_ins(rt);
 
 out:
-	if (*err) {
-		RDBG(("dfree(%p) ", rt));
-		dst_free((struct dst_entry *) rt);
-		rt = NULL;
-	}
-	RDBG(("ret(%p)\n", rt));
-#if 0
-	return rt;
-#else
-	/* BUGGG! For now always return NULL. (see above)
-
-	   Really, it was used only in two places, and one of them
-	   (rt6_add_dflt_router) is repaired, ip6_fw is not essential
-	   at all. --ANK
-	 */
-	return NULL;
-#endif
+	dst_free((struct dst_entry *) rt);
+	return err;
 }
 
 int ip6_del_rt(struct rt6_info *rt)
 {
-	rt6_lock();
+	int err;
 
 	start_bh_atomic();
-
-	/* I'd add here couple of cli()
-	   cli(); cli(); cli();
-
-	   Now it is really LOCKED. :-) :-) --ANK
-	 */
-
 	rt6_dflt_pointer = NULL;
-
-	if (atomic_read(&rt6_tbl_lock) == 1)
-		fib6_del(rt);
-	else
-		rtreq_add(rt, RT_OPER_DEL);
+	err = fib6_del(rt);
 	end_bh_atomic();
-	rt6_unlock();
-	return 0;
+
+	return err;
 }
 
 int ip6_route_del(struct in6_rtmsg *rtmsg)
 {
 	struct fib6_node *fn;
 	struct rt6_info *rt;
+	int err = -ESRCH;
 
-	rt6_lock();
-	fn = fib6_lookup(&ip6_routing_table, &rtmsg->rtmsg_dst, &rtmsg->rtmsg_src);
-	rt = fn->leaf;
-
-	/*
-	 *	Blow it away
-	 *
-	 *	BUGGGG It will not help with Pedro's subtrees.
-	 *	We urgently need fib6_locate_node function, and
-	 *	it is not the only place where rt6_lookup is used
-	 *	for wrong purpose.
-	 *							--ANK
-	 */
-restart:
-	if (rt && rt->rt6i_src.plen == rtmsg->rtmsg_src_len) {
-		if (rt->rt6i_dst.plen > rtmsg->rtmsg_dst_len) {
-			struct fib6_node *fn = rt->rt6i_node;
-			while ((fn = fn->parent) != NULL) {
-				if (fn->fn_flags & RTN_ROOT)
-					break;
-				if (fn->fn_flags & RTN_RTINFO) {
-					rt = fn->leaf;
-					goto restart;
-				}
-			}
-		}
+	start_bh_atomic();
 
-		if (rt->rt6i_dst.plen == rtmsg->rtmsg_dst_len) {
-			for ( ; rt; rt = rt->u.next) {
-				if (rtmsg->rtmsg_ifindex &&
-				    (rt->rt6i_dev == NULL ||
-				     rt->rt6i_dev->ifindex != rtmsg->rtmsg_ifindex))
-					continue;
-				if (rtmsg->rtmsg_flags&RTF_GATEWAY &&
-				     ipv6_addr_cmp(&rtmsg->rtmsg_gateway, &rt->rt6i_gateway))
-					continue;
-				if (rtmsg->rtmsg_metric &&
-				    rtmsg->rtmsg_metric != rt->rt6i_metric)
-					continue;
-				ip6_del_rt(rt);
-				rt6_unlock();
-				return 0;
-			}
+	fn = fib6_locate(&ip6_routing_table,
+			 &rtmsg->rtmsg_dst, rtmsg->rtmsg_dst_len,
+			 &rtmsg->rtmsg_src, rtmsg->rtmsg_src_len);
+	
+	if (fn) {
+		for (rt = fn->leaf; rt; rt = rt->u.next) {
+			if (rtmsg->rtmsg_ifindex &&
+			    (rt->rt6i_dev == NULL ||
+			     rt->rt6i_dev->ifindex != rtmsg->rtmsg_ifindex))
+				continue;
+			if (rtmsg->rtmsg_flags&RTF_GATEWAY &&
+			    ipv6_addr_cmp(&rtmsg->rtmsg_gateway, &rt->rt6i_gateway))
+				continue;
+			if (rtmsg->rtmsg_metric &&
+			    rtmsg->rtmsg_metric != rt->rt6i_metric)
+				continue;
+			err = ip6_del_rt(rt);
+			break;
 		}
 	}
-	rt6_unlock();
-
-	return -ESRCH;
-}
-
-
-/*
- *	bottom handler, runs with atomic_bh protection
- */
-void __rt6_run_bh(void)
-{
-	struct rt6_req *rtreq;
+	end_bh_atomic();
 
-	while ((rtreq = rtreq_dequeue())) {
-		switch (rtreq->operation) {
-		case RT_OPER_ADD:
-			fib6_add(&ip6_routing_table, rtreq->ptr);
-			break;
-		case RT_OPER_DEL:
-			fib6_del(rtreq->ptr);
-			break;
-		};
-		kfree(rtreq);
-	}
-	rt6_bh_mask = 0;
+	return err;
 }
 
 #ifdef CONFIG_IPV6_NETLINK
@@ -971,10 +792,10 @@
 
 		switch (rtmsg->rtmsg_type) {
 		case RTMSG_NEWROUTE:
-			ip6_route_add(rtmsg, &err);
+			err = ip6_route_add(rtmsg);
 			break;
 		case RTMSG_DELROUTE:
-			ip6_route_del(rtmsg);
+			err = ip6_route_del(rtmsg);
 			break;
 		default:
 			count = -EINVAL;
@@ -1047,17 +868,19 @@
 /*
  *	Handle redirects
  */
-struct rt6_info *rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr,
-			      struct in6_addr *target, struct device *dev,
-			      int on_link)
+void rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr,
+		  struct neighbour *neigh, int on_link)
 {
 	struct rt6_info *rt, *nrt;
 
 	/* Locate old route to this destination. */
-	rt = rt6_lookup(dest, NULL, dev->ifindex, 0);
+	rt = rt6_lookup(dest, NULL, neigh->dev->ifindex, 1);
 
-	if (rt == NULL || rt->u.dst.error)
-		return NULL;
+	if (rt == NULL)
+		return;
+
+	if (neigh->dev != rt->rt6i_dev)
+		goto out;
 
 	/* Redirect received -> path was valid.
 	   Look, redirects are sent only in response to data packets,
@@ -1066,12 +889,18 @@
 	dst_confirm(&rt->u.dst);
 
 	/* Duplicate redirect: silently ignore. */
-	if (ipv6_addr_cmp(target, &rt->rt6i_gateway) == 0)
-		return NULL;
+	if (neigh == rt->u.dst.neighbour)
+		goto out;
 
-	/* Current route is on-link; redirect is always invalid. */
+	/* Current route is on-link; redirect is always invalid.
+	   
+	   Seems, previous statement is not true. It could
+	   be node, which looks for us as on-link (f.e. proxy ndisc)
+	   But then router serving it might decide, that we should
+	   know truth 8)8) --ANK (980726).
+	 */
 	if (!(rt->rt6i_flags&RTF_GATEWAY))
-		return NULL;
+		goto out;
 
 #if !defined(CONFIG_IPV6_EUI64) || defined(CONFIG_IPV6_NO_PB)
 	/*
@@ -1089,16 +918,21 @@
 
 	if (ipv6_addr_cmp(saddr, &rt->rt6i_gateway)) {
 		if (rt->rt6i_flags & RTF_DEFAULT) {
-			rt = ip6_routing_table.leaf;
+			struct rt6_info *rt1;
 
-			for (; rt; rt = rt->u.next) {
-				if (!ipv6_addr_cmp(saddr, &rt->rt6i_gateway))
+			for (rt1 = ip6_routing_table.leaf; rt1; rt1 = rt1->u.next) {
+				if (!ipv6_addr_cmp(saddr, &rt1->rt6i_gateway)) {
+					dst_clone(&rt1->u.dst);
+					dst_release(&rt->u.dst);
+					rt = rt1;
 					goto source_ok;
+				}
 			}
 		}
-		printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
+		if (net_ratelimit())
+			printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
 			       "for redirect target\n");
-		return NULL;
+		goto out;
 	}
 
 source_ok:
@@ -1107,36 +941,11 @@
 	/*
 	 *	We have finally decided to accept it.
 	 */
-	if (rt->rt6i_dst.plen == 128) {
-		/* BUGGGG! Very bad bug. Fast path code does not protect
-		 * itself of changing nexthop on the fly, it was supposed
-		 * that crucial parameters (dev, nexthop, hh) ARE VOLATILE.
-		 *                                                   --ANK
-		 * Not fixed!! I plugged it to avoid random crashes
-		 * (they are very unlikely, but I do not want to shrug
-		 *  every time when redirect arrives)
-		 * but the plug must be removed. --ANK
-		 */
-
-#if 0
-		/*
-		 *	Already a host route.
-		 *
-		 */
-		if (rt->rt6i_nexthop)
-			neigh_release(rt->rt6i_nexthop);
-		rt->rt6i_flags |= RTF_MODIFIED | RTF_CACHE;
-		if (on_link)
-			rt->rt6i_flags &= ~RTF_GATEWAY;
-		ipv6_addr_copy(&rt->rt6i_gateway, target);
-		rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, target);
-		return rt;
-#else
-		return NULL;
-#endif
-	}
 
 	nrt = ip6_rt_copy(rt);
+	if (nrt == NULL)
+		goto out;
+
 	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
 	if (on_link)
 		nrt->rt6i_flags &= ~RTF_GATEWAY;
@@ -1144,19 +953,24 @@
 	ipv6_addr_copy(&nrt->rt6i_dst.addr, dest);
 	nrt->rt6i_dst.plen = 128;
 
-	ipv6_addr_copy(&nrt->rt6i_gateway, target);
-	nrt->rt6i_nexthop = ndisc_get_neigh(nrt->rt6i_dev, target);
-	nrt->rt6i_dev = dev;
-	nrt->u.dst.pmtu = ipv6_get_mtu(dev);
-	if (!ipv6_addr_is_multicast(&nrt->rt6i_dst.addr))
-		nrt->rt6i_hoplimit = ipv6_get_hoplimit(dev);
-
-	rt6_lock();
-	rt6_ins(nrt);
-	rt6_unlock();
+	ipv6_addr_copy(&nrt->rt6i_gateway, (struct in6_addr*)neigh->primary_key);
+	nrt->rt6i_nexthop = neigh_clone(neigh);
+	/* Reset pmtu, it may be better */
+	nrt->u.dst.pmtu = ipv6_get_mtu(neigh->dev);
+	nrt->rt6i_hoplimit = ipv6_get_hoplimit(neigh->dev);
 
-	/* BUGGGGGGG! nrt can point to nowhere. */
-	return nrt;
+	if (rt6_ins(nrt))
+		goto out;
+
+	/* Sic! rt6_redirect is called by bh, so that it is allowed */
+	dst_release(&rt->u.dst);
+	if (rt->rt6i_flags&RTF_CACHE)
+		ip6_del_rt(rt);
+	return;
+
+out:
+        dst_release(&rt->u.dst);
+	return;
 }
 
 /*
@@ -1164,29 +978,25 @@
  *	i.e. Path MTU discovery
  */
 
-void rt6_pmtu_discovery(struct in6_addr *addr, struct device *dev, int pmtu)
+void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr,
+			struct device *dev, u32 pmtu)
 {
 	struct rt6_info *rt, *nrt;
 
-	if (pmtu < 576 || pmtu > 65536) {
-#if RT6_DEBUG >= 1
-		printk(KERN_DEBUG "rt6_pmtu_discovery: invalid MTU value %d\n",
-		       pmtu);
-#endif
+	if (pmtu < IPV6_MIN_MTU) {
+		if (net_ratelimit())
+			printk(KERN_DEBUG "rt6_pmtu_discovery: invalid MTU value %d\n",
+			       pmtu);
 		return;
 	}
 
-	rt = rt6_lookup(addr, NULL, dev->ifindex, 0);
+	rt = rt6_lookup(daddr, saddr, dev->ifindex, 0);
 
-	if (rt == NULL || rt->u.dst.error) {
-#if RT6_DEBUG >= 2
-		printk(KERN_DEBUG "rt6_pmtu_discovery: no route to host\n");
-#endif
+	if (rt == NULL)
 		return;
-	}
 
 	if (pmtu >= rt->u.dst.pmtu)
-		return;
+		goto out;
 
 	/* New mtu received -> path was valid.
 	   They are sent only in response to data packets,
@@ -1194,39 +1004,42 @@
 	 */
 	dst_confirm(&rt->u.dst);
 
-	/* It is wrong, but I plugged the hole here.
-	   On-link routes are cloned differently,
-	   look at rt6_redirect --ANK
+	/* Host route. If it is static, it would be better
+	   not to override it, but add new one, so that
+	   when cache entry will expire old pmtu
+	   would return automatically.
 	 */
-	if (!(rt->rt6i_flags&RTF_GATEWAY))
-		return;
-
 	if (rt->rt6i_dst.plen == 128) {
 		/*
 		 *	host route
 		 */
 		rt->u.dst.pmtu = pmtu;
 		rt->rt6i_flags |= RTF_MODIFIED;
-
-		return;
+		goto out;
 	}
 
-	nrt = ip6_rt_copy(rt);
-	ipv6_addr_copy(&nrt->rt6i_dst.addr, addr);
-	nrt->rt6i_dst.plen = 128;
-
-	nrt->rt6i_flags |= (RTF_DYNAMIC | RTF_CACHE);
-
-	/* It was missing. :-) :-)
-	   I wonder, kernel was deemed to crash after pkt_too_big
-	   and nobody noticed it. Hey, guys, do someone really
-	   use it? --ANK
+	/* Network route.
+	   Two cases are possible:
+	   1. It is connected route. Action: COW
+	   2. It is gatewayed route or NONEXTHOP route. Action: clone it.
 	 */
-	nrt->rt6i_nexthop = neigh_clone(rt->rt6i_nexthop);
+	if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) {
+		nrt = rt6_cow(rt, daddr, saddr);
+		nrt->rt6i_flags |= RTF_DYNAMIC;
+		dst_release(&nrt->u.dst);
+	} else {
+		nrt = ip6_rt_copy(rt);
+		if (nrt == NULL)
+			goto out;
+		ipv6_addr_copy(&nrt->rt6i_dst.addr, daddr);
+		nrt->rt6i_dst.plen = 128;
+		nrt->rt6i_nexthop = neigh_clone(rt->rt6i_nexthop);
+		nrt->rt6i_flags |= (RTF_DYNAMIC | RTF_CACHE);
+		rt6_ins(nrt);
+	}
 
-	rt6_lock();
-	rt6_ins(rt);
-	rt6_unlock();
+out:
+	dst_release(&rt->u.dst);
 }
 
 /*
@@ -1247,16 +1060,19 @@
 		rt->u.dst.rtt = ort->u.dst.rtt;
 		rt->u.dst.window = ort->u.dst.window;
 		rt->u.dst.mxlock = ort->u.dst.mxlock;
+		rt->u.dst.dev = ort->u.dst.dev;
+		rt->u.dst.lastuse = jiffies;
 		rt->rt6i_hoplimit = ort->rt6i_hoplimit;
-		rt->rt6i_dev = ort->rt6i_dev;
+		rt->rt6i_expires = ort->rt6i_expires;
 
 		ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway);
-		rt->rt6i_keylen = ort->rt6i_keylen;
 		rt->rt6i_flags = ort->rt6i_flags;
 		rt->rt6i_metric = ort->rt6i_metric;
 
 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
+#ifdef CONFIG_IPV6_SUBTREES
 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
+#endif
 	}
 	return rt;
 }
@@ -1266,31 +1082,17 @@
 	struct rt6_info *rt;
 	struct fib6_node *fn;
 
-	RDBG(("rt6_get_dflt_router(%p,%p)[%p]", addr, dev,
-	      __builtin_return_address(0)));
-#if RT6_DEBUG >= 3
-	{
-		int i;
-
-		RDBG(("addr["));
-		for(i = 0; i < 8; i++) {
-			RDBG(("%04x%c", addr->s6_addr16[i],
-			      i == 7 ? ']' : ':'));
-		}
-	}
-#endif
-	RDBG(("\n"));
-	rt6_lock();
-
 	fn = &ip6_routing_table;
 
+	start_bh_atomic();
 	for (rt = fn->leaf; rt; rt=rt->u.next) {
 		if (dev == rt->rt6i_dev &&
 		    ipv6_addr_cmp(&rt->rt6i_gateway, addr) == 0)
 			break;
 	}
-
-	rt6_unlock();
+	if (rt)
+		dst_clone(&rt->u.dst);
+	end_bh_atomic();
 	return rt;
 }
 
@@ -1298,24 +1100,6 @@
 				     struct device *dev)
 {
 	struct in6_rtmsg rtmsg;
-	struct rt6_info *rt;
-	int err;
-
-	RDBG(("rt6_add_dflt_router(%p,%p)[%p] ", gwaddr, dev,
-	      __builtin_return_address(0)));
-#if RT6_DEBUG >= 3
-	{
-		struct in6_addr *addr = gwaddr;
-		int i;
-
-		RDBG(("gwaddr["));
-		for(i = 0; i < 8; i++) {
-			RDBG(("%04x%c", addr->s6_addr16[i],
-			      i == 7 ? ']' : ':'));
-		}
-	}
-#endif
-	RDBG(("\n"));
 
 	memset(&rtmsg, 0, sizeof(struct in6_rtmsg));
 	rtmsg.rtmsg_type = RTMSG_NEWROUTE;
@@ -1325,48 +1109,28 @@
 
 	rtmsg.rtmsg_ifindex = dev->ifindex;
 
-	rt = ip6_route_add(&rtmsg, &err);
-
-	/* BUGGGGGGGGGGGGGGGGGGGG!
-	   rt can be not NULL, but point to heavens.
-	 */
-
-	if (err) {
-		printk(KERN_DEBUG "rt6_add_dflt: ip6_route_add error %d\n",
-		       err);
-	}
-	return rt;
+	ip6_route_add(&rtmsg);
+	return rt6_get_dflt_router(gwaddr, dev);
 }
 
 void rt6_purge_dflt_routers(int last_resort)
 {
 	struct rt6_info *rt;
-	struct fib6_node *fn;
 	u32 flags;
 
-	RDBG(("rt6_purge_dflt_routers(%d)[%p]\n", last_resort,
-	      __builtin_return_address(0)));
-	fn = &ip6_routing_table;
-
-	rt6_dflt_pointer = NULL;
-
 	if (last_resort)
 		flags = RTF_ALLONLINK;
 	else
 		flags = RTF_DEFAULT | RTF_ADDRCONF;	
 
-	for (rt = fn->leaf; rt; ) {
-		if ((rt->rt6i_flags & flags)) {
-			struct rt6_info *drt;
-#if RT6_DEBUG >= 2
-			printk(KERN_DEBUG "rt6_purge_dflt: deleting entry\n");
-#endif
-			drt = rt;
-			rt = rt->u.next;
-			ip6_del_rt(drt);
-			continue;
+restart:
+	rt6_dflt_pointer = NULL;
+
+	for (rt = ip6_routing_table.leaf; rt; rt = rt->u.next) {
+		if (rt->rt6i_flags & flags) {
+			ip6_del_rt(rt);
+			goto restart;
 		}
-		rt = rt->u.next;
 	}
 }
 
@@ -1389,7 +1153,7 @@
 		rtnl_lock();
 		switch (cmd) {
 		case SIOCADDRT:
-			ip6_route_add(&rtmsg, &err);
+			err = ip6_route_add(&rtmsg);
 			break;
 		case SIOCDELRT:
 			err = ip6_route_del(&rtmsg);
@@ -1414,7 +1178,7 @@
  */
 
 int ip6_pkt_discard(struct sk_buff *skb)
-{	
+{
 	ipv6_statistics.Ip6OutNoRoutes++;
 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev);
 	kfree_skb(skb);
@@ -1429,21 +1193,6 @@
 {
 	struct rt6_info *rt;
 
-	RDBG(("ip6_rt_addr_add(%p,%p)[%p]\n", addr, dev,
-	      __builtin_return_address(0)));
-#if RT6_DEBUG >= 3
-	{
-		int i;
-
-		RDBG(("addr["));
-		for(i = 0; i < 8; i++) {
-			RDBG(("%04x%c", addr->s6_addr16[i],
-			      i == 7 ? ']' : ':'));
-		}
-	}
-#endif
-	RDBG(("\n"));
-
 	rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
 	if (rt == NULL)
 		return -ENOMEM;
@@ -1465,10 +1214,7 @@
 
 	ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
 	rt->rt6i_dst.plen = 128;
-
-	rt6_lock();
 	rt6_ins(rt);
-	rt6_unlock();
 
 	return 0;
 }
@@ -1480,12 +1226,16 @@
 int ip6_rt_addr_del(struct in6_addr *addr, struct device *dev)
 {
 	struct rt6_info *rt;
+	int err = -ENOENT;
 
-	rt = rt6_lookup(addr, NULL, loopback_dev.ifindex, RTF_LINKRT);
-	if (rt && rt->rt6i_dst.plen == 128)
-		return ip6_del_rt(rt);
+	rt = rt6_lookup(addr, NULL, loopback_dev.ifindex, 1);
+	if (rt) {
+		if (rt->rt6i_dst.plen == 128)
+			err= ip6_del_rt(rt);
+		dst_release(&rt->u.dst);
+	}
 
-	return 0;
+	return err;
 }
 
 #ifdef CONFIG_RT6_POLICY
@@ -1587,75 +1337,65 @@
 	}
 
 error:
+	dst_clone(&ip6_null_entry.u.dst);
 	return &ip6_null_entry;
 
 found:
-
 	if (nrt == NULL)
 		goto error;
 
 	nrt->rt6i_flags |= RTF_CACHE;
-	/* BUGGGG! nrt can point to nowhere! */
-	rt6_ins(nrt);
-
+	dst_clone(&nrt->u.dst);
+	err = rt6_ins(nrt);
+	if (err)
+		nrt->u.dst.error = err;
 	return nrt;
 }
 #endif
 
-/* 
- * Nope, I am not idiot. I see that it is the ugliest of ugly routines.
- * Anyone is advertised to write better one. --ANK
- */
+static int fib6_ifdown(struct rt6_info *rt, void *arg)
+{
+	if (((void*)rt->rt6i_dev == arg || arg == NULL) &&
+	    rt != &ip6_null_entry) {
+		RT6_TRACE("deleted by ifdown %p\n", rt);
+		return -1;
+	}
+	return 0;
+}
 
-struct rt6_ifdown_arg {
+void rt6_ifdown(struct device *dev)
+{
+	fib6_clean_tree(&ip6_routing_table, fib6_ifdown, 0, dev);
+}
+
+struct rt6_mtu_change_arg
+{
 	struct device *dev;
-	struct rt6_info *rt;
+	unsigned mtu;
 };
 
-
-static void rt6_ifdown_node(struct fib6_node *fn, void *p_arg)
+static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
 {
-	struct rt6_info *rt;
-	struct rt6_ifdown_arg *arg = (struct rt6_ifdown_arg *) p_arg;
-
-	if (arg->rt != NULL)
-		return;
+	struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
 
-	for (rt = fn->leaf; rt; rt = rt->u.next) {
-		if (rt->rt6i_dev == arg->dev || arg->dev == NULL) {
-			arg->rt = rt;
-			return;
-		}
-	}
+	/* In IPv6 pmtu discovery is not optional,
+	   so that RTAX_MTU lock cannot dissable it.
+	   We still use this lock to block changes
+	   caused by addrconf/ndisc.
+	   */
+	if (rt->rt6i_dev == arg->dev &&
+	    !(rt->u.dst.mxlock&(1<<RTAX_MTU)))
+		rt->u.dst.pmtu = arg->mtu;
+	return 0;
 }
 
-void rt6_ifdown(struct device *dev)
+void rt6_mtu_change(struct device *dev, unsigned mtu)
 {
-	int count = 0;
-	struct rt6_ifdown_arg arg;
-	struct rt6_info *rt;
+	struct rt6_mtu_change_arg arg;
 
-	do {
-		arg.dev = dev;
-		arg.rt = NULL;
-		fib6_walk_tree(&ip6_routing_table, rt6_ifdown_node, &arg,
-			       RT6_FILTER_RTNODES);
-		if (arg.rt != NULL)
-			ip6_del_rt(arg.rt);
-		count++;
-	} while (arg.rt != NULL);
-
-	/* And default routes ... */
-
-	for (rt = ip6_routing_table.leaf; rt; ) {
-		if (rt != &ip6_null_entry && (rt->rt6i_dev == dev || dev == NULL)) {
-			struct rt6_info *deleting = rt;
-			rt = rt->u.next;
-			ip6_del_rt(deleting);
-			continue;
-		}
-		rt = rt->u.next;
-	}
+	arg.dev = dev;
+	arg.mtu = mtu;
+	fib6_clean_tree(&ip6_routing_table, rt6_mtu_change_route, 0, &arg);
 }
 
 #ifdef CONFIG_RTNETLINK
@@ -1714,37 +1454,28 @@
 {
 	struct rtmsg *r = NLMSG_DATA(nlh);
 	struct in6_rtmsg rtmsg;
-	int err = 0;
 
 	if (inet6_rtm_to_rtmsg(r, arg, &rtmsg))
 		return -EINVAL;
-	ip6_route_add(&rtmsg, &err);
-	return err;
+	return ip6_route_add(&rtmsg);
 }
 
 struct rt6_rtnl_dump_arg
 {
 	struct sk_buff *skb;
 	struct netlink_callback *cb;
-	int skip;
-	int count;
-	int stop;
 };
 
 static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
 			 struct in6_addr *dst,
 			 struct in6_addr *src,
 			 int iif,
-			 int type, pid_t pid, u32 seq)
+			 int type, u32 pid, u32 seq)
 {
 	struct rtmsg *rtm;
 	struct nlmsghdr  *nlh;
 	unsigned char	 *b = skb->tail;
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	unsigned char 	 *o;
-#else
 	struct rtattr *mx;
-#endif
 	struct rta_cacheinfo ci;
 
 	nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*rtm));
@@ -1762,9 +1493,6 @@
 		rtm->rtm_type = RTN_UNICAST;
 	rtm->rtm_flags = 0;
 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	rtm->rtm_nhs = 0;
-#endif
 	rtm->rtm_protocol = RTPROT_BOOT;
 	if (rt->rt6i_flags&RTF_DYNAMIC)
 		rtm->rtm_protocol = RTPROT_REDIRECT;
@@ -1776,19 +1504,18 @@
 	if (rt->rt6i_flags&RTF_CACHE)
 		rtm->rtm_flags |= RTM_F_CLONED;
 
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	o = skb->tail;
-#endif
 	if (dst) {
 		RTA_PUT(skb, RTA_DST, 16, dst);
 	        rtm->rtm_dst_len = 128;
 	} else if (rtm->rtm_dst_len)
 		RTA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
+#ifdef CONFIG_IPV6_SUBTREES
 	if (src) {
 		RTA_PUT(skb, RTA_SRC, 16, src);
 	        rtm->rtm_src_len = 128;
 	} else if (rtm->rtm_src_len)
 		RTA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
+#endif
 	if (iif)
 		RTA_PUT(skb, RTA_IIF, 4, &iif);
 	else if (dst) {
@@ -1796,14 +1523,6 @@
 		if (ifp)
 			RTA_PUT(skb, RTA_PREFSRC, 16, &ifp->addr);
 	}
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	if (rt->u.dst.pmtu)
-		RTA_PUT(skb, RTA_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
-	if (rt->u.dst.window)
-		RTA_PUT(skb, RTA_WINDOW, sizeof(unsigned), &rt->u.dst.window);
-	if (rt->u.dst.rtt)
-		RTA_PUT(skb, RTA_RTT, sizeof(unsigned), &rt->u.dst.rtt);
-#else
 	mx = (struct rtattr*)skb->tail;
 	RTA_PUT(skb, RTA_METRICS, 0, NULL);
 	if (rt->u.dst.mxlock)
@@ -1817,7 +1536,6 @@
 	mx->rta_len = skb->tail - (u8*)mx;
 	if (mx->rta_len == RTA_LENGTH(0))
 		skb_trim(skb, (u8*)mx - skb->data);
-#endif
 	if (rt->u.dst.neighbour)
 		RTA_PUT(skb, RTA_GATEWAY, 16, &rt->u.dst.neighbour->primary_key);
 	if (rt->u.dst.dev)
@@ -1828,13 +1546,10 @@
 		ci.rta_expires = rt->rt6i_expires - jiffies;
 	else
 		ci.rta_expires = 0;
-	ci.rta_used = 0;
+	ci.rta_used = atomic_read(&rt->u.dst.refcnt);
 	ci.rta_clntref = atomic_read(&rt->u.dst.use);
 	ci.rta_error = rt->u.dst.error;
 	RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci);
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	rtm->rtm_optlen = skb->tail - o;
-#endif
 	nlh->nlmsg_len = skb->tail - b;
 	return skb->len;
 
@@ -1844,45 +1559,98 @@
 	return -1;
 }
 
-static void rt6_dump_node(struct fib6_node *fn, void *p_arg)
+static int rt6_dump_route(struct rt6_info *rt, void *p_arg)
 {
-	struct rt6_info *rt;
 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
 
-	if (arg->stop)
-		return;
+	return rt6_fill_node(arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
+			     NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq);
+}
 
-	for (rt = fn->leaf; rt; rt = rt->u.next) {
-		if (arg->count < arg->skip) {
-			arg->count++;
-			continue;
-		}
-		if (rt6_fill_node(arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
-				  NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq) <= 0) {
-			arg->stop = 1;
-			break;
+static int fib6_dump_node(struct fib6_walker_t *w)
+{
+	int res;
+	struct rt6_info *rt;
+
+	for (rt = w->leaf; rt; rt = rt->u.next) {
+		res = rt6_dump_route(rt, w->args);
+		if (res < 0) {
+			/* Frame is full, suspend walking */
+			w->leaf = rt;
+			return 1;
 		}
-		arg->count++;
+		BUG_TRAP(res!=0);
 	}
+	w->leaf = NULL;
+	return 0;
 }
 
+static int fib6_dump_done(struct netlink_callback *cb)
+{
+	struct fib6_walker_t *w = (void*)cb->args[0];
+
+	if (w) {
+		cb->args[0] = 0;
+		start_bh_atomic();
+		fib6_walker_unlink(w);
+		end_bh_atomic();
+		kfree(w);
+	}
+	if (cb->args[1]) {
+		cb->done = (void*)cb->args[1];
+		cb->args[1] = 0;
+	}
+	return cb->done(cb);
+}
 
 int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	struct rt6_rtnl_dump_arg arg;
+	struct fib6_walker_t *w;
+	int res;
 
 	arg.skb = skb;
 	arg.cb = cb;
-	arg.skip = cb->args[0];
-	arg.count = 0;
-	arg.stop = 0;
-	start_bh_atomic();
-	fib6_walk_tree(&ip6_routing_table, rt6_dump_node, &arg, RT6_FILTER_RTNODES);
-	if (arg.stop == 0)
-		rt6_dump_node(&ip6_routing_table, &arg);
-	end_bh_atomic();
-	cb->args[0] = arg.count;
-	return skb->len;
+
+	w = (void*)cb->args[0];
+	if (w == NULL) {
+		/* New dump:
+		 * 
+		 * 1. hook callback destructor.
+		 */
+		cb->args[1] = (long)cb->done;
+		cb->done = fib6_dump_done;
+
+		/*
+		 * 2. allocate and initialize walker.
+		 */
+		w = kmalloc(sizeof(*w), GFP_KERNEL);
+		if (w == NULL)
+			return -ENOMEM;
+		RT6_TRACE("dump<%p", w);
+		memset(w, 0, sizeof(*w));
+		w->root = &ip6_routing_table;
+		w->func = fib6_dump_node;
+		w->args = &arg;
+		cb->args[0] = (long)w;
+		start_bh_atomic();
+		res = fib6_walk(w);
+		end_bh_atomic();
+	} else {
+		w->args = &arg;
+		start_bh_atomic();
+		res = fib6_walk_continue(w);
+		end_bh_atomic();
+	}
+#if RT6_DEBUG >= 3
+	if (res <= 0 && skb->len == 0)
+		RT6_TRACE("%p>dump end\n", w);
+#endif
+	/* 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;
 }
 
 int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
@@ -1974,10 +1742,10 @@
 
 #ifdef CONFIG_PROC_FS
 
-
 #define RT6_INFO_LEN (32 + 4 + 32 + 4 + 32 + 40 + 5 + 1)
 
-struct rt6_proc_arg {
+struct rt6_proc_arg
+{
 	char *buffer;
 	int offset;
 	int length;
@@ -1985,109 +1753,18 @@
 	int len;
 };
 
-static void rt6_info_node(struct fib6_node *fn, void *p_arg)
+static int rt6_info_route(struct rt6_info *rt, void *p_arg)
 {
-	struct rt6_info *rt;
 	struct rt6_proc_arg *arg = (struct rt6_proc_arg *) p_arg;
-
-	for (rt = fn->leaf; rt; rt = rt->u.next) {
-		int i;
-
-		if (arg->skip < arg->offset / RT6_INFO_LEN) {
-			arg->skip++;
-			continue;
-		}
-
-		if (arg->len >= arg->length)
-			return;
-		
-		for (i=0; i<16; i++) {
-			sprintf(arg->buffer + arg->len, "%02x",
-				rt->rt6i_dst.addr.s6_addr[i]);
-			arg->len += 2;
-		}
-		arg->len += sprintf(arg->buffer + arg->len, " %02x ",
-				    rt->rt6i_dst.plen);
-
-		for (i=0; i<16; i++) {
-			sprintf(arg->buffer + arg->len, "%02x",
-				rt->rt6i_src.addr.s6_addr[i]);
-			arg->len += 2;
-		}
-		arg->len += sprintf(arg->buffer + arg->len, " %02x ",
-				    rt->rt6i_src.plen);
-		
-		if (rt->rt6i_nexthop) {
-			for (i=0; i<16; i++) {
-				sprintf(arg->buffer + arg->len, "%02x",
-					rt->rt6i_nexthop->primary_key[i]);
-				arg->len += 2;
-			}
-		} else {
-			sprintf(arg->buffer + arg->len,
-				"00000000000000000000000000000000");
-			arg->len += 32;
-		}
-		arg->len += sprintf(arg->buffer + arg->len,
-				    " %08x %08x %08x %08x %8s\n",
-				    rt->rt6i_metric, atomic_read(&rt->rt6i_use),
-				    atomic_read(&rt->rt6i_ref), rt->rt6i_flags, 
-				    rt->rt6i_dev ? rt->rt6i_dev->name : "");
-	}
-}
-
-static int rt6_proc_info(char *buffer, char **start, off_t offset, int length,
-			 int dummy)
-{
-	struct rt6_proc_arg arg;
-	arg.buffer = buffer;
-	arg.offset = offset;
-	arg.length = length;
-	arg.skip = 0;
-	arg.len = 0;
-
-	fib6_walk_tree(&ip6_routing_table, rt6_info_node, &arg,
-		       RT6_FILTER_RTNODES);
-
-	rt6_info_node(&ip6_routing_table, &arg);
-
-	*start = buffer;
-	if (offset)
-		*start += offset % RT6_INFO_LEN;
-
-	arg.len -= offset % RT6_INFO_LEN;
-
-	if(arg.len > length)
-		arg.len = length;
-	if(arg.len < 0)
-		arg.len = 0;
-
-	return arg.len;
-}
-
-#define PTR_SZ (sizeof(void *) * 2)
-#define FI_LINE_SZ (2 * (PTR_SZ) + 7 + 32 + 4 + 32 + 4)
-
-static void rt6_tree_node(struct fib6_node *fn, void *p_arg)
-{
-	struct rt6_proc_arg *arg = (struct rt6_proc_arg *) p_arg;
-	struct rt6_info *rt;
-	char f;
 	int i;
 
-	rt = fn->leaf;
-
-	if (arg->skip < arg->offset / FI_LINE_SZ) {
+	if (arg->skip < arg->offset / RT6_INFO_LEN) {
 		arg->skip++;
-		return;
+		return 0;
 	}
 
-	if (arg->len + FI_LINE_SZ >= arg->length)
-		return;
-
-	f = (fn->fn_flags & RTN_RTINFO) ? 'r' : 'n';
-	arg->len += sprintf(arg->buffer + arg->len, "%p %p %02x %c ",
-			    fn, fn->parent, fn->fn_bit, f);
+	if (arg->len >= arg->length)
+		return 0;
 
 	for (i=0; i<16; i++) {
 		sprintf(arg->buffer + arg->len, "%02x",
@@ -2096,18 +1773,41 @@
 	}
 	arg->len += sprintf(arg->buffer + arg->len, " %02x ",
 			    rt->rt6i_dst.plen);
-	
+
+#ifdef CONFIG_IPV6_SUBTREES
 	for (i=0; i<16; i++) {
 		sprintf(arg->buffer + arg->len, "%02x",
 			rt->rt6i_src.addr.s6_addr[i]);
 		arg->len += 2;
 	}
-	arg->len += sprintf(arg->buffer + arg->len, " %02x\n",
+	arg->len += sprintf(arg->buffer + arg->len, " %02x ",
 			    rt->rt6i_src.plen);
+#else
+	sprintf(arg->buffer + arg->len,
+		"00000000000000000000000000000000 00 ");
+	arg->len += 36;
+#endif
 
+	if (rt->rt6i_nexthop) {
+		for (i=0; i<16; i++) {
+			sprintf(arg->buffer + arg->len, "%02x",
+				rt->rt6i_nexthop->primary_key[i]);
+			arg->len += 2;
+		}
+	} else {
+		sprintf(arg->buffer + arg->len,
+			"00000000000000000000000000000000");
+		arg->len += 32;
+	}
+	arg->len += sprintf(arg->buffer + arg->len,
+			    " %08x %08x %08x %08x %8s\n",
+			    rt->rt6i_metric, atomic_read(&rt->u.dst.use),
+			    atomic_read(&rt->u.dst.refcnt), rt->rt6i_flags, 
+			    rt->rt6i_dev ? rt->rt6i_dev->name : "");
+	return 0;
 }
 
-static int rt6_proc_tree(char *buffer, char **start, off_t offset, int length,
+static int rt6_proc_info(char *buffer, char **start, off_t offset, int length,
 			 int dummy)
 {
 	struct rt6_proc_arg arg;
@@ -2117,7 +1817,7 @@
 	arg.skip = 0;
 	arg.len = 0;
 
-	fib6_walk_tree(&ip6_routing_table, rt6_tree_node, &arg, 0);
+	fib6_clean_tree(&ip6_routing_table, rt6_info_route, 0, &arg);
 
 	*start = buffer;
 	if (offset)
@@ -2125,15 +1825,14 @@
 
 	arg.len -= offset % RT6_INFO_LEN;
 
-	if(arg.len > length)
+	if (arg.len > length)
 		arg.len = length;
-	if(arg.len < 0)
+	if (arg.len < 0)
 		arg.len = 0;
 
 	return arg.len;
 }
 
-
 extern struct rt6_statistics rt6_stats;
 
 static int rt6_proc_stats(char *buffer, char **start, off_t offset, int length,
@@ -2141,10 +1840,11 @@
 {
 	int len;
 
-	len = sprintf(buffer, "%04x %04x %04x %04x %04x\n",
+	len = sprintf(buffer, "%04x %04x %04x %04x %04x %04x\n",
 		      rt6_stats.fib_nodes, rt6_stats.fib_route_nodes,
 		      rt6_stats.fib_rt_alloc, rt6_stats.fib_rt_entries,
-		      rt6_stats.fib_rt_cache);
+		      rt6_stats.fib_rt_cache,
+		      atomic_read(&ip6_dst_ops.entries));
 
 	len -= offset;
 
@@ -2164,12 +1864,6 @@
 	0, &proc_net_inode_operations,
 	rt6_proc_info
 };
-static struct proc_dir_entry proc_rt6_tree = {
-	PROC_NET_RT6_TREE, 7, "ip6_fib",
-	S_IFREG | S_IRUGO, 1, 0, 0,
-	0, &proc_net_inode_operations,
-	rt6_proc_tree
-};
 static struct proc_dir_entry proc_rt6_stats = {
 	PROC_NET_RT6_STATS, 9, "rt6_stats",
 	S_IFREG | S_IRUGO, 1, 0, 0,
@@ -2230,7 +1924,6 @@
 {
 #ifdef 	CONFIG_PROC_FS
 	proc_net_register(&proc_rt6_info);
-	proc_net_register(&proc_rt6_tree);
 	proc_net_register(&proc_rt6_stats);
 #endif
 #ifdef CONFIG_IPV6_NETLINK
@@ -2243,7 +1936,6 @@
 {
 #ifdef CONFIG_PROC_FS
 	proc_net_unregister(PROC_NET_RT6);
-	proc_net_unregister(PROC_NET_RT6_TREE);
 	proc_net_unregister(PROC_NET_RT6_STATS);
 #endif
 #ifdef CONFIG_IPV6_NETLINK
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/sit.c linux/net/ipv6/sit.c
--- v2.1.119/linux/net/ipv6/sit.c	Tue Mar 17 22:18:16 1998
+++ linux/net/ipv6/sit.c	Thu Aug 27 19:33:09 1998
@@ -6,7 +6,7 @@
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
  *
- *	$Id: sit.c,v 1.27 1998/03/08 05:56:57 davem Exp $
+ *	$Id: sit.c,v 1.28 1998/08/26 12:05:22 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -434,7 +434,7 @@
 		ip_rt_put(rt);
 		goto tx_error;
 	}
-	if (mtu >= 576) {
+	if (mtu >= IPV6_MIN_MTU) {
 		if (skb->dst && mtu < skb->dst->pmtu) {
 			struct rt6_info *rt6 = (struct rt6_info*)skb->dst;
 			if (mtu < rt6->u.dst.pmtu) {
@@ -475,6 +475,8 @@
 			tunnel->recursion--;
 			return 0;
 		}
+		if (skb->sk)
+			skb_set_owner_w(new_skb, skb->sk);
 		dev_kfree_skb(skb);
 		skb = new_skb;
 	}
@@ -491,7 +493,7 @@
 	iph 			=	skb->nh.iph;
 	iph->version		=	4;
 	iph->ihl		=	sizeof(struct iphdr)>>2;
-	if (mtu > 576)
+	if (mtu > IPV6_MIN_MTU)
 		iph->frag_off	=	__constant_htons(IP_DF);
 	else
 		iph->frag_off	=	0;
@@ -608,7 +610,7 @@
 
 static int ipip6_tunnel_change_mtu(struct device *dev, int new_mtu)
 {
-	if (new_mtu < 576 || new_mtu > 0xFFF8 - sizeof(struct iphdr))
+	if (new_mtu < IPV6_MIN_MTU || new_mtu > 0xFFF8 - sizeof(struct iphdr))
 		return -EINVAL;
 	dev->mtu = new_mtu;
 	return 0;
@@ -662,8 +664,8 @@
 	if (tdev) {
 		dev->hard_header_len = tdev->hard_header_len + sizeof(struct iphdr);
 		dev->mtu = tdev->mtu - sizeof(struct iphdr);
-		if (dev->mtu < 576)
-			dev->mtu = 576;
+		if (dev->mtu < IPV6_MIN_MTU)
+			dev->mtu = IPV6_MIN_MTU;
 	}
 	dev->iflink = tunnel->parms.link;
 
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.1.119/linux/net/ipv6/tcp_ipv6.c	Thu Aug 27 19:56:31 1998
+++ linux/net/ipv6/tcp_ipv6.c	Thu Aug 27 19:35:59 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.82 1998/06/11 03:15:52 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.89 1998/08/28 00:27:54 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -123,16 +123,33 @@
 	}
 	if(result == 0) {
 		if(tb == NULL) {
-			if(tcp_bucket_create(snum) == NULL)
+			if((tb = tcp_bucket_create(snum)) == NULL)
 				result = 1;
+			else if (sk->reuse && sk->state != TCP_LISTEN)
+				tb->flags |= TCPB_FLAG_FASTREUSE;
 		} else {
 			/* It could be pending garbage collection, this
 			 * kills the race and prevents it from disappearing
 			 * out from under us by the time we use it.  -DaveM
 			 */
-			if(tb->owners == NULL && !(tb->flags & TCPB_FLAG_LOCKED)) {
-				tb->flags = TCPB_FLAG_LOCKED;
-				tcp_dec_slow_timer(TCP_SLT_BUCKETGC);
+			if(tb->owners == NULL) {
+				if (!(tb->flags & TCPB_FLAG_LOCKED)) {
+					tb->flags = (TCPB_FLAG_LOCKED |
+						     ((sk->reuse &&
+						       sk->state != TCP_LISTEN) ?
+						      TCPB_FLAG_FASTREUSE : 0));
+					tcp_dec_slow_timer(TCP_SLT_BUCKETGC);
+				} else if (!(tb->flags & TCPB_FLAG_GOODSOCKNUM)) {
+					/* Someone is in between the bind
+					 * and the actual connect or listen.
+					 * See if it was a legitimate reuse
+					 * and we are as well, else punt.
+					 */
+					if (sk->reuse == 0 ||
+					    !(tb->flags & TCPB_FLAG_FASTREUSE))
+						result = 1;
+				} else
+					tb->flags &= ~TCPB_FLAG_GOODSOCKNUM;
 			}
 		}
 	}
@@ -358,7 +375,6 @@
 	struct dst_entry *dst;
 	struct sk_buff *buff;
 	int addr_type;
-	int mss;
 
 	if (sk->state != TCP_CLOSE) 
 		return(-EISCONN);
@@ -403,6 +419,7 @@
 	 */
 
 	if (addr_type == IPV6_ADDR_MAPPED) {
+		u32 exthdrlen = tp->ext_header_len;
 		struct sockaddr_in sin;
 		int err;
 
@@ -418,10 +435,10 @@
 		err = tcp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
 
 		if (err) {
+			tp->ext_header_len = exthdrlen;
 			sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
 			sk->backlog_rcv = tcp_v6_do_rcv;
 		} else {
-			/* Yuup... And it is not the only place... --ANK */
 			ipv6_addr_set(&np->saddr, 0, 0, __constant_htonl(0x0000FFFF),
 				      sk->saddr);
 			ipv6_addr_set(&np->rcv_saddr, 0, 0, __constant_htonl(0x0000FFFF),
@@ -441,18 +458,18 @@
 	fl.uli_u.ports.dport = usin->sin6_port;
 	fl.uli_u.ports.sport = sk->sport;
 
+	if (np->opt && np->opt->srcrt) {
+		struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+		fl.nl_u.ip6_u.daddr = rt0->addr;
+	}
+
 	dst = ip6_route_output(sk, &fl);
-	
+
 	if (dst->error) {
 		dst_release(dst);
 		return dst->error;
 	}
 
-	if (dst->pmtu < 576) {
-		dst_release(dst);
-		return -EINVAL;
-	}
-
 	if (fl.oif == 0 && addr_type&IPV6_ADDR_LINKLOCAL) {
 		/* Ough! This guy tries to connect to link local
 		 * address and did not specify interface.
@@ -462,11 +479,11 @@
 		sk->bound_dev_if = dst->dev->ifindex;
 	}
 
-	ip6_dst_store(sk, dst);
+	ip6_dst_store(sk, dst, NULL);
 
 	if (saddr == NULL) {
 		ifa = ipv6_get_saddr(dst, &np->daddr);
-	
+
 		if (ifa == NULL)
 			return -ENETUNREACH;
 		
@@ -477,6 +494,12 @@
 		ipv6_addr_copy(&np->saddr, saddr);
 	}
 
+	tp->ext_header_len = 0;
+	if (np->opt)
+		tp->ext_header_len = np->opt->opt_flen+np->opt->opt_nflen;
+	/* Reset mss clamp */
+	tp->mss_clamp = ~0;
+
 	buff = sock_wmalloc(sk, (MAX_HEADER + sk->prot->max_header),
 			    0, GFP_KERNEL);
 
@@ -498,15 +521,7 @@
 						   np->daddr.s6_addr32[3],
 						   sk->sport, sk->dport);
 
-	sk->mtu = dst->pmtu;
-	mss = sk->mtu - sizeof(struct ipv6hdr);
-#if 0
-	if (np->opt) {
-		/* Adjust mss */
-	}
-#endif
-
-	tcp_connect(sk, buff, mss);
+	tcp_connect(sk, buff, dst->pmtu);
 
 	return 0;
 }
@@ -555,10 +570,12 @@
 	return retval;
 }
 
-void tcp_v6_err(struct sk_buff *skb, int type, int code, unsigned char *header, __u32 info,
-		struct in6_addr *saddr, struct in6_addr *daddr,
-		struct inet6_protocol *protocol)
+void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr,
+		struct inet6_skb_parm *opt,
+		int type, int code, unsigned char *header, __u32 info)
 {
+	struct in6_addr *saddr = &hdr->saddr;
+	struct in6_addr *daddr = &hdr->daddr;
 	struct tcphdr *th = (struct tcphdr *)header;
 	struct ipv6_pinfo *np;
 	struct sock *sk;
@@ -567,7 +584,8 @@
 	struct tcp_opt *tp; 
 	__u32 seq; 
 
-	/* XXX: length check for tcphdr missing here */
+	if (header + 8 > skb->tail)
+		return;
 
 	sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source, skb->dev->ifindex);
 
@@ -588,15 +606,20 @@
 
 	np = &sk->net_pinfo.af_inet6;
 	if (type == ICMPV6_PKT_TOOBIG && sk->state != TCP_LISTEN) {
+		struct dst_entry *dst = NULL;
 		/* icmp should have updated the destination cache entry */
 
 		if (sk->dst_cache)
-			dst_check(&sk->dst_cache, np->dst_cookie);
+			dst = dst_check(&sk->dst_cache, np->dst_cookie);
 
-		if (sk->dst_cache == NULL) {
+		if (dst == NULL) {
 			struct flowi fl;
 			struct dst_entry *dst;
-			
+
+			/* BUGGG_FUTURE: Again, it is not clear how
+			   to handle rthdr case. Ignore this complexity
+			   for now.
+			 */
 			fl.proto = IPPROTO_TCP;
 			fl.nl_u.ip6_u.daddr = &np->daddr;
 			fl.nl_u.ip6_u.saddr = &np->saddr;
@@ -605,23 +628,19 @@
 			fl.uli_u.ports.sport = sk->sport;
 
 			dst = ip6_route_output(sk, &fl);
+		} else
+			dst = dst_clone(dst);
 
-			ip6_dst_store(sk, dst);
-		}
-
-		if (sk->dst_cache->error) {
-			sk->err_soft = sk->dst_cache->error;
-		} else {
-			/* FIXME: Reset sk->mss, taking into account TCP option
-			 *        bytes for timestamps. -DaveM
-			 */
-			sk->mtu = sk->dst_cache->pmtu;
-		}
-		if (atomic_read(&sk->sock_readers)) { /* remove later */
-			printk(KERN_DEBUG "tcp_v6_err: pmtu disc: socket locked.\n");
-			return;
-		}
-		tcp_simple_retransmit(sk);
+		if (dst->error) {
+			sk->err_soft = dst->error;
+		} else if (tp->pmtu_cookie > dst->pmtu
+			   && !atomic_read(&sk->sock_readers)) {
+			lock_sock(sk); 
+			tcp_sync_mss(sk, dst->pmtu);
+			tcp_simple_retransmit(sk);
+			release_sock(sk);
+		} /* else let the usual retransmit timer handle it */
+		dst_release(dst);
 		return;
 	}
 
@@ -680,6 +699,7 @@
 {
 	struct sk_buff * skb;
 	struct dst_entry *dst;
+	struct ipv6_txoptions *opt = NULL;
 	struct flowi fl;
 	int mss;
 
@@ -690,19 +710,26 @@
 	fl.uli_u.ports.dport = req->rmt_port;
 	fl.uli_u.ports.sport = sk->sport;
 
-	dst = ip6_route_output(sk, &fl);
-	if (dst->error) {
-		dst_release(dst);
-		return;
+	opt = sk->net_pinfo.af_inet6.opt;
+	if (opt == NULL &&
+	    sk->net_pinfo.af_inet6.rxopt.bits.srcrt == 2 &&
+	    req->af.v6_req.pktopts) {
+		struct sk_buff *pktopts = req->af.v6_req.pktopts;
+		struct inet6_skb_parm *rxopt = (struct inet6_skb_parm *)pktopts->cb;
+		if (rxopt->srcrt)
+			opt = ipv6_invert_rthdr(sk, (struct ipv6_rt_hdr*)(pktopts->nh.raw + rxopt->srcrt));
 	}
 
-	mss = dst->pmtu - sizeof(struct ipv6hdr) - sizeof(struct tcphdr);
-#if 0
-	/* Subtract option length... */
-	if (opt) {
-		mss -= opt->optlen;
+	if (opt && opt->srcrt) {
+		struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
+		fl.nl_u.ip6_u.daddr = rt0->addr;
 	}
-#endif
+
+	dst = ip6_route_output(sk, &fl);
+	if (dst->error)
+		goto done;
+
+	mss = dst->pmtu - sizeof(struct ipv6hdr) - sizeof(struct tcphdr);
 
 	skb = tcp_make_synack(sk, dst, req, mss);
 	if (skb) {
@@ -712,13 +739,22 @@
 					 &req->af.v6_req.loc_addr, &req->af.v6_req.rmt_addr,
 					 csum_partial((char *)th, skb->len, skb->csum));
 
-		ip6_xmit(sk, skb, &fl, req->af.v6_req.opt);
+		fl.nl_u.ip6_u.daddr = &req->af.v6_req.rmt_addr;
+		ip6_xmit(sk, skb, &fl, opt);
 	}
+
+done:
 	dst_release(dst);
+        if (opt && opt != sk->net_pinfo.af_inet6.opt)
+		sock_kfree_s(sk, opt, opt->tot_len);
 }
 
 static void tcp_v6_or_free(struct open_request *req)
 {
+	if (req->af.v6_req.pktopts) {
+		kfree_skb(req->af.v6_req.pktopts);
+		req->af.v6_req.pktopts = NULL;
+	}
 }
 
 static struct or_calltable or_ipv6 = {
@@ -727,14 +763,27 @@
 	tcp_v6_send_reset
 };
 
+static int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb)
+{
+	struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
+
+	if (sk->net_pinfo.af_inet6.rxopt.all) {
+		if ((opt->hop && sk->net_pinfo.af_inet6.rxopt.bits.hopopts) ||
+		    (opt->srcrt && sk->net_pinfo.af_inet6.rxopt.bits.srcrt) ||
+		    ((opt->dst1 || opt->dst0) && sk->net_pinfo.af_inet6.rxopt.bits.dstopts))
+			return 1;
+	}
+	return 0;
+}
+
+
 #define BACKLOG(sk) ((sk)->tp_pinfo.af_tcp.syn_backlog) /* lvalue! */
 #define BACKLOGMAX(sk) sysctl_max_syn_backlog
 
 /* FIXME: this is substantially similar to the ipv4 code.
  * Can some kind of merge be done? -- erics
  */
-static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr,
-							   __u32 isn)
+static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, __u32 isn)
 {
 	struct tcp_opt tp;
 	struct open_request *req;
@@ -747,7 +796,11 @@
 	}
 
 	if (skb->protocol == __constant_htons(ETH_P_IP))
-		return tcp_v4_conn_request(sk, skb, ptr, isn);
+		return tcp_v4_conn_request(sk, skb, isn);
+
+	/* FIXME: do the same check for anycast */
+	if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr))
+		goto drop; 
 
 	if (isn == 0) 
 		isn = tcp_v6_init_sequence(sk,skb);
@@ -756,8 +809,9 @@
 	 *	There are no SYN attacks on IPv6, yet...	
 	 */
 	if (BACKLOG(sk) >= BACKLOGMAX(sk)) {
-		printk(KERN_DEBUG "droping syn ack:%d max:%d\n",
-		       BACKLOG(sk), BACKLOGMAX(sk));
+		(void)(net_ratelimit() && 
+		       printk(KERN_INFO "droping syn ack:%d max:%d\n",
+			       BACKLOG(sk), BACKLOGMAX(sk)));
 		goto drop;		
 	}
 
@@ -773,13 +827,16 @@
 	req->rcv_isn = TCP_SKB_CB(skb)->seq;
 	req->snt_isn = isn;
 	tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
-	tp.in_mss = 536;
+	tp.mss_clamp = 65535;
 	tcp_parse_options(NULL, skb->h.th, &tp, 0);
-        req->mss = tp.in_mss;
-	if (tp.saw_tstamp) {
-		req->mss -= TCPOLEN_TSTAMP_ALIGNED;
+	if (tp.mss_clamp == 65535)
+		tp.mss_clamp = 576 - sizeof(struct ipv6hdr) - sizeof(struct iphdr);
+	if (sk->tp_pinfo.af_tcp.user_mss && sk->tp_pinfo.af_tcp.user_mss < tp.mss_clamp)
+		tp.mss_clamp = sk->tp_pinfo.af_tcp.user_mss;
+
+        req->mss = tp.mss_clamp;
+	if (tp.saw_tstamp)
                 req->ts_recent = tp.rcv_tsval;
-	}
         req->tstamp_ok = tp.tstamp_ok;
 	req->sack_ok = tp.sack_ok;
         req->snd_wscale = tp.snd_wscale;
@@ -787,7 +844,11 @@
 	req->rmt_port = skb->h.th->source;
 	ipv6_addr_copy(&req->af.v6_req.rmt_addr, &skb->nh.ipv6h->saddr);
 	ipv6_addr_copy(&req->af.v6_req.loc_addr, &skb->nh.ipv6h->daddr);
-	req->af.v6_req.opt = NULL;	/* FIXME: options */
+	req->af.v6_req.pktopts = NULL;
+	if (ipv6_opt_accepted(sk, skb)) {
+		atomic_inc(&skb->users);
+		req->af.v6_req.pktopts = skb;
+	}
 	req->af.v6_req.iif = sk->bound_dev_if;
 
 	/* So that link locals have meaning */
@@ -804,8 +865,6 @@
 	tcp_inc_slow_timer(TCP_SLT_SYNACK);
 	tcp_synq_queue(&sk->tp_pinfo.af_tcp, req);	
 
-	sk->data_ready(sk, 0);
-
 	return 0;
 
 drop:
@@ -832,8 +891,8 @@
 	struct flowi fl;
 	struct tcp_opt *newtp;
 	struct sock *newsk;
-	int mss;
-      
+	struct ipv6_txoptions *opt;
+
 	if (skb->protocol == __constant_htons(ETH_P_IP)) {
 		/*
 		 *	v6 mapped
@@ -856,21 +915,37 @@
 
 		newsk->tp_pinfo.af_tcp.af_specific = &ipv6_mapped;
 		newsk->backlog_rcv = tcp_v4_do_rcv;
+		newsk->net_pinfo.af_inet6.pktoptions = NULL;
+		newsk->net_pinfo.af_inet6.opt = NULL;
+
+		/* It is tricky place. Until this moment IPv4 tcp
+		   worked with IPv6 af_tcp.af_specific.
+		   Sync it now.
+		 */
+		tcp_sync_mss(newsk, newsk->tp_pinfo.af_tcp.pmtu_cookie);
 
 		return newsk;
 	}
 
+	opt = sk->net_pinfo.af_inet6.opt;
 
 	if (sk->ack_backlog > sk->max_ack_backlog)
-		return NULL; 
+		goto out;
+
+	if (sk->net_pinfo.af_inet6.rxopt.bits.srcrt == 2 &&
+	    opt == NULL && req->af.v6_req.pktopts) {
+		struct inet6_skb_parm *rxopt = (struct inet6_skb_parm *)req->af.v6_req.pktopts->cb;
+		if (rxopt->srcrt)
+			opt = ipv6_invert_rthdr(sk, (struct ipv6_rt_hdr*)(req->af.v6_req.pktopts->nh.raw+rxopt->srcrt));
+	}
 
 	if (dst == NULL) {
-		/*
-		 *	options / mss / route cache
-		 */
-	    
 		fl.proto = IPPROTO_TCP;
 		fl.nl_u.ip6_u.daddr = &req->af.v6_req.rmt_addr;
+		if (opt && opt->srcrt) {
+			struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
+			fl.nl_u.ip6_u.daddr = rt0->addr;
+		}
 		fl.nl_u.ip6_u.saddr = &req->af.v6_req.loc_addr;
 		fl.oif = sk->bound_dev_if;
 		fl.uli_u.ports.dport = req->rmt_port;
@@ -879,22 +954,17 @@
 		dst = ip6_route_output(sk, &fl);
 	}
 
-	if (dst->error || dst->pmtu < 576)
+	if (dst->error)
 		goto out;
-	
+
 	sk->tp_pinfo.af_tcp.syn_backlog--;
 	sk->ack_backlog++;
 
-	mss = dst->pmtu - sizeof(struct ipv6hdr);
-#if 0
-	/* Adjust mss by option size */
-#endif
-
-	newsk = tcp_create_openreq_child(sk, req, skb, mss);
+	newsk = tcp_create_openreq_child(sk, req, skb);
 	if (newsk == NULL)
 		goto out;
 
-	ip6_dst_store(newsk, dst);
+	ip6_dst_store(newsk, dst, NULL);
 
 	newtp = &(newsk->tp_pinfo.af_tcp);
 
@@ -903,18 +973,55 @@
 	ipv6_addr_copy(&np->saddr, &req->af.v6_req.loc_addr);
 	ipv6_addr_copy(&np->rcv_saddr, &req->af.v6_req.loc_addr);
 	newsk->bound_dev_if = req->af.v6_req.iif;
-	newsk->mtu = dst->pmtu;
+
+	/* Now IPv6 options... 
+
+	   First: no IPv4 options.
+	 */
 	newsk->opt = NULL;
 
+	/* Clone RX bits */
+	np->rxopt.all = sk->net_pinfo.af_inet6.rxopt.all;
+
+	/* Clone pktoptions received with SYN */
+	np->pktoptions = req->af.v6_req.pktopts;
+	if (np->pktoptions)
+		atomic_inc(&np->pktoptions->users);
+	np->opt = NULL;
+
+	/* Clone native IPv6 options from listening socket (if any)
+
+	   Yes, keeping reference count would be much more clever,
+	   but we make one more one thing there: reattach optmem
+	   to newsk.
+	 */
+	if (opt) {
+		np->opt = ipv6_dup_options(newsk, opt);
+		if (opt != sk->net_pinfo.af_inet6.opt)
+			sock_kfree_s(sk, opt, opt->tot_len);
+	}
+
+	newtp->ext_header_len = 0;
+	if (np->opt)
+		newtp->ext_header_len = np->opt->opt_nflen + np->opt->opt_flen;
+
+	tcp_sync_mss(newsk, dst->pmtu);
+	newtp->rcv_mss = newtp->mss_clamp;
+
 	newsk->daddr	= LOOPBACK4_IPV6;
 	newsk->saddr	= LOOPBACK4_IPV6;
 	newsk->rcv_saddr= LOOPBACK4_IPV6;
 
 	newsk->prot->hash(newsk);
 	add_to_prot_sklist(newsk);
+
+	sk->data_ready(sk, 0); /* Deliver SIGIO */ 
+
 	return newsk;
 
 out:
+	if (opt && opt != sk->net_pinfo.af_inet6.opt)
+		sock_kfree_s(sk, opt, opt->tot_len);
 	dst_release(dst);
 	return NULL;
 }
@@ -1020,8 +1127,8 @@
 	if (!req)
 		return;
 	/* Sequence number check required by RFC793 */
-	if (before(TCP_SKB_CB(skb)->seq, req->snt_isn) ||
-	    after(TCP_SKB_CB(skb)->seq, req->snt_isn+1))
+	if (before(TCP_SKB_CB(skb)->seq, req->rcv_isn) ||
+	    after(TCP_SKB_CB(skb)->seq, req->rcv_isn+1))
 		return;
 	if(req->sk)
 		sk->ack_backlog--;
@@ -1055,7 +1162,7 @@
 		}
 #if 0 /*def CONFIG_SYN_COOKIES */
 		 else {
-			sk = cookie_v6_check(sk, skb, (struct ipv6_options *) skb->cb);
+			sk = cookie_v6_check(sk, skb);
 		 }
 #endif
 	}
@@ -1064,6 +1171,8 @@
 
 static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
 {
+	int users = 0;
+
 	/* Imagine: socket is IPv6. IPv4 packet arrives,
 	   goes to IPv4 receive handler and backlogged.
 	   From backlog it always goes here. Kerboom...
@@ -1080,6 +1189,8 @@
 	 *	is currently called with bh processing disabled.
 	 */
 
+  	ipv6_statistics.Ip6InDelivers++;
+
 	/* XXX We need to think more about socket locking
 	 * XXX wrt. backlog queues, __release_sock(), etc.  -DaveM
 	 */
@@ -1092,9 +1203,29 @@
 	 */
 	skb_set_owner_r(skb, sk);
 
+	/* Do Stevens' IPV6_PKTOPTIONS.
+
+	   Yes, guys, it is the only place in our code, where we
+	   may make it not affecting IPv4.
+	   The rest of code is protocol independent,
+	   and I do not like idea to uglify IPv4.
+
+	   Actually, all the idea behind IPV6_PKTOPTIONS
+	   looks not very well thought. For now we latch
+	   options, received in the last packet, enqueued
+	   by tcp. Feel free to propose better solution.
+	                                       --ANK (980728)
+	 */
+	if (sk->net_pinfo.af_inet6.rxopt.all) {
+		users = atomic_read(&skb->users);
+		atomic_inc(&skb->users);
+	}
+
 	if (sk->state == TCP_ESTABLISHED) { /* Fast path */
 		if (tcp_rcv_established(sk, skb, skb->h.th, skb->len))
 			goto reset;
+		if (users)
+			goto ipv6_pktoptions;
 		release_sock(sk);
 		return 0;
 	}
@@ -1110,26 +1241,60 @@
 		sk = nsk;
 	}
 
-	if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->cb, skb->len))
+	if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len))
 		goto reset;
+	if (users)
+		goto ipv6_pktoptions;
 	release_sock(sk);
 	return 0;
 
 reset:
 	tcp_v6_send_reset(skb);
 discard:
+	if (users)
+		kfree_skb(skb);
 	kfree_skb(skb);
 	release_sock(sk);  
 	return 0;
+
+ipv6_pktoptions:
+	/* Do you ask, what is it?
+
+	   1. skb was enqueued by tcp.
+	   2. skb is added to tail of read queue, rather than out of order.
+	   3. socket is not in passive state.
+	   4. Finally, it really contains options, which user wants to receive.
+	 */
+	if (atomic_read(&skb->users) > users &&
+	    TCP_SKB_CB(skb)->end_seq == sk->tp_pinfo.af_tcp.rcv_nxt &&
+	    !((1<<sk->state)&(TCPF_CLOSE|TCPF_LISTEN))) {
+		if (ipv6_opt_accepted(sk, skb)) {
+			struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+			kfree_skb(skb);
+			skb = NULL;
+			if (skb2) {
+				skb_set_owner_r(skb2, sk);
+				skb = xchg(&sk->net_pinfo.af_inet6.pktoptions, skb2);
+			}
+		} else {
+			kfree_skb(skb);
+			skb = xchg(&sk->net_pinfo.af_inet6.pktoptions, NULL);
+		}
+	}
+
+	if (skb)
+		kfree_skb(skb);
+	release_sock(sk);
+	return 0;
 }
 
-int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
-	       struct in6_addr *saddr, struct in6_addr *daddr,
-	       struct ipv6_options *opt, unsigned short len,
-	       int redo, struct inet6_protocol *protocol)
+int tcp_v6_rcv(struct sk_buff *skb, unsigned long len)
 {
 	struct tcphdr *th;	
 	struct sock *sk;
+	struct device *dev = skb->dev;
+	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
 
 	th = skb->h.th;
 
@@ -1198,7 +1363,7 @@
 
 do_time_wait:
 	if(tcp_timewait_state_process((struct tcp_tw_bucket *)sk,
-				      skb, th, &(IPCB(skb)->opt), skb->len))
+				      skb, th, skb->len))
 		goto no_tcp_socket;
 	goto discard_it;
 }
@@ -1221,6 +1386,12 @@
 		fl.uli_u.ports.dport = sk->dport;
 		fl.uli_u.ports.sport = sk->sport;
 
+		if (np->opt && np->opt->srcrt) {
+			struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+			fl.nl_u.ip6_u.daddr = rt0->addr;
+		}
+
+
 		dst = ip6_route_output(sk, &fl);
 
 		if (dst->error) {
@@ -1228,7 +1399,7 @@
 			return dst->error;
 		}
 
-		ip6_dst_store(sk, dst);
+		ip6_dst_store(sk, dst, NULL);
 	}
 
 	return dst->error;
@@ -1258,6 +1429,11 @@
 	fl.uli_u.ports.sport = sk->sport;
 	fl.uli_u.ports.dport = sk->dport;
 
+	if (np->opt && np->opt->srcrt) {
+		struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+		fl.nl_u.ip6_u.daddr = rt0->addr;
+	}
+
 	if (sk->dst_cache)
 		dst = dst_check(&sk->dst_cache, np->dst_cookie);
 
@@ -1270,11 +1446,14 @@
 			return;
 		}
 
-		ip6_dst_store(sk, dst);
+		ip6_dst_store(sk, dst, NULL);
 	}
 
 	skb->dst = dst_clone(dst);
 
+	/* Restore final destination back after routing done */
+	fl.nl_u.ip6_u.daddr = &np->daddr;
+
 	ip6_xmit(sk, skb, &fl, np->opt);
 }
 
@@ -1295,6 +1474,8 @@
 	tcp_v6_conn_request,
 	tcp_v6_syn_recv_sock,
 	tcp_v6_get_sock,
+	sizeof(struct ipv6hdr),
+
 	ipv6_setsockopt,
 	ipv6_getsockopt,
 	v6_addr2sockaddr,
@@ -1312,6 +1493,8 @@
 	tcp_v6_conn_request,
 	tcp_v6_syn_recv_sock,
 	tcp_v6_get_sock,
+	sizeof(struct iphdr),
+
 	ipv6_setsockopt,
 	ipv6_getsockopt,
 	v6_addr2sockaddr,
@@ -1330,7 +1513,7 @@
 
 	tp->rto  = TCP_TIMEOUT_INIT;		/*TCP_WRITE_TIME*/
 	tp->mdev = TCP_TIMEOUT_INIT;
-	tp->in_mss = 536;
+	tp->mss_clamp = ~0;
 
 	/* See draft-stevens-tcpca-spec-01 for discussion of the
 	 * initialization of these values.
@@ -1338,17 +1521,17 @@
 	tp->snd_cwnd = (1 << TCP_CWND_SHIFT);
 	tp->snd_ssthresh = 0x7fffffff;
 
-	sk->priority = 1;
 	sk->state = TCP_CLOSE;
 	sk->max_ack_backlog = SOMAXCONN;
-	sk->mtu = 576;
-	sk->mss = 536;
+	tp->rcv_mss = 536; 
 
 	/* Init SYN queue. */
 	tcp_synq_init(tp);
 
 	sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
 
+	sk->write_space = tcp_write_space;
+
 	return 0;
 }
 
@@ -1376,12 +1559,6 @@
   	while((skb = __skb_dequeue(&tp->out_of_order_queue)) != NULL)
 		kfree_skb(skb);
 
-	/*
-	 *	Release destination entry
-	 */
-
-	dst_release(xchg(&sk->dst_cache,NULL));
-
 	/* Clean up a locked TCP bind bucket, this only happens if a
 	 * port is allocated for a socket, but it never fully connects.
 	 * In which case we will find num to be non-zero and daddr to
@@ -1390,7 +1567,7 @@
 	if(ipv6_addr_any(&(sk->net_pinfo.af_inet6.daddr)) && sk->num != 0)
 		tcp_bucket_unlock(sk);
 
-	return 0;
+	return inet6_destroy_sock(sk);
 }
 
 struct proto tcpv6_prot = {
diff -u --recursive --new-file v2.1.119/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.1.119/linux/net/ipv6/udp.c	Thu Aug 27 19:56:31 1998
+++ linux/net/ipv6/udp.c	Mon Aug 31 10:32:19 1998
@@ -7,7 +7,7 @@
  *
  *	Based on linux/ipv4/udp.c
  *
- *	$Id: udp.c,v 1.31 1998/07/15 05:05:45 davem Exp $
+ *	$Id: udp.c,v 1.33 1998/08/27 16:55:20 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -15,6 +15,7 @@
  *      2 of the License, or (at your option) any later version.
  */
 
+#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -59,6 +60,14 @@
 		if((sk2->num == snum) && (sk2 != sk)) {
 			unsigned char state = sk2->state;
 			int sk2_reuse = sk2->reuse;
+
+			/* Two sockets can be bound to the same port if they're
+			 * bound to different interfaces.
+			 */
+
+			if(sk2->bound_dev_if != sk->bound_dev_if)
+				continue;
+
 			if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
 				if((!sk2_reuse)			||
 				   (!sk_reuse)			||
@@ -139,7 +148,7 @@
 }
 
 static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
-				  struct in6_addr *daddr, u16 dport)
+				  struct in6_addr *daddr, u16 dport, int dif)
 {
 	struct sock *sk, *result = NULL;
 	unsigned short hnum = ntohs(dport);
@@ -166,7 +175,12 @@
 					continue;
 				score++;
 			}
-			if(score == 3) {
+			if(sk->bound_dev_if) {
+				if(sk->bound_dev_if != dif)
+					continue;
+				score++;
+			}
+			if(score == 4) {
 				result = sk;
 				break;
 			} else if(score > badness) {
@@ -257,20 +271,25 @@
 	 */
 
 	fl.proto = IPPROTO_UDP;
-	fl.nl_u.ip6_u.daddr = daddr;
+	fl.nl_u.ip6_u.daddr = &np->daddr;
 	fl.nl_u.ip6_u.saddr = NULL;
 	fl.oif = sk->bound_dev_if;
 	fl.uli_u.ports.dport = sk->dport;
 	fl.uli_u.ports.sport = sk->sport;
 
+	if (np->opt && np->opt->srcrt) {
+		struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+		fl.nl_u.ip6_u.daddr = rt0->addr;
+	}
+
 	dst = ip6_route_output(sk, &fl);
-       
+
 	if (dst->error) {
 		dst_release(dst);
 		return dst->error;
 	}
 
-	ip6_dst_store(sk, dst);
+	ip6_dst_store(sk, dst, fl.nl_u.ip6_u.daddr);
 
 	/* get the source adddress used in the apropriate device */
 
@@ -291,15 +310,50 @@
 
 static void udpv6_close(struct sock *sk, unsigned long timeout)
 {
-	lock_sock(sk);
+	/* See for explanation: raw_close in ipv4/raw.c */
 	sk->state = TCP_CLOSE;
-	ipv6_sock_mc_close(sk);
 	udp_v6_unhash(sk);
 	sk->dead = 1;
-	release_sock(sk);
 	destroy_sock(sk);
 }
 
+#ifdef CONFIG_FILTER
+#undef CONFIG_UDP_DELAY_CSUM
+#endif
+
+#ifdef CONFIG_UDP_DELAY_CSUM
+
+/* Please, read comments in net/checksum.h, asm/checksum.h
+
+   I commented out csum_partial_copy_to_user there because it did not
+   verify_area. Now I am even wondered, how clever was I that time 8)8)
+   If I did not it, I would step into this hole again.   --ANK
+ */
+
+#ifndef _HAVE_ARCH_COPY_AND_CSUM_TO_USER
+#if defined(__i386__)
+static __inline__
+unsigned int csum_and_copy_to_user (const char *src, char *dst,
+				    int len, int sum, int *err_ptr)
+{
+	int *src_err_ptr=NULL;
+
+	if (verify_area(VERIFY_WRITE, dst, len) == 0)
+		return csum_partial_copy_generic(src, dst, len, sum, src_err_ptr, err_ptr);
+
+	if (len)
+		*err_ptr = -EFAULT;
+
+	return sum;
+}
+#elif defined(__sparc__)
+#define csum_and_copy_to_user csum_partial_copy_to_user
+#else
+#undef CONFIG_UDP_DELAY_CSUM
+#endif
+#endif
+#endif
+
 /*
  * 	This should be easy, if there is something there we
  * 	return it, otherwise we block.
@@ -322,12 +376,12 @@
 	 *	From here the generic datagram does a lot of the work. Come
 	 *	the finished NET3, it will do _ALL_ the work!
 	 */
-	 	
+
 	skb = skb_recv_datagram(sk, flags, noblock, &err);
 	if (!skb)
 		goto out;
   
- 	copied = ntohs(((struct udphdr *)skb->h.raw)->len) - sizeof(struct udphdr);
+ 	copied = skb->len - sizeof(struct udphdr);
   	if (copied > len) {
   		copied = len;
   		msg->msg_flags |= MSG_TRUNC;
@@ -337,8 +391,41 @@
   	 *	FIXME : should use udp header size info value 
   	 */
   	 
+#ifndef CONFIG_UDP_DELAY_CSUM
 	err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), 
 				      msg->msg_iov, copied);
+#else
+	if (sk->no_check || skb->ip_summed==CHECKSUM_UNNECESSARY) {
+		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
+					      copied);
+	} else if (copied > msg->msg_iov[0].iov_len || (msg->msg_flags&MSG_TRUNC)) {
+		if (csum_fold(csum_partial(skb->h.raw, ntohs(skb->h.uh->len), skb->csum))) {
+			/* Error for blocking case is chosen to masquerade
+			   as some normal condition.
+			 */
+			err = (msg->msg_flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
+			udp_stats_in6.UdpInErrors++;
+			goto out_free;
+		}
+		err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
+					      copied);
+	} else {
+		unsigned int csum = csum_partial(skb->h.raw, sizeof(struct udphdr), skb->csum);
+
+		err = 0;
+		csum = csum_and_copy_to_user((char*)&skb->h.uh[1], msg->msg_iov[0].iov_base, copied, csum, &err);
+		if (err)
+			goto out_free;
+		if (csum_fold(csum)) {
+			/* Error for blocking case is chosen to masquerade
+			   as some normal condition.
+			 */
+			err = (msg->msg_flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
+			udp_stats_in6.UdpInErrors++;
+			goto out_free;
+		}
+	}
+#endif
 	if (err)
 		goto out_free;
 	
@@ -361,7 +448,7 @@
 			memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
 			       sizeof(struct in6_addr));
 
-			if (msg->msg_controllen)
+			if (sk->net_pinfo.af_inet6.rxopt.all)
 				datagram_recv_ctl(sk, msg, skb);
 		}
   	}
@@ -373,20 +460,27 @@
 	return err;
 }
 
-void udpv6_err(struct sk_buff *skb, int type, int code, unsigned char *buff, __u32 info,
-	       struct in6_addr *saddr, struct in6_addr *daddr,
-	       struct inet6_protocol *protocol)
-{
+void udpv6_err(struct sk_buff *skb, struct ipv6hdr *hdr,
+	       struct inet6_skb_parm *opt,
+	       int type, int code, unsigned char *buff, __u32 info)
+{
+	struct device *dev = skb->dev;
+	struct in6_addr *saddr = &hdr->saddr;
+	struct in6_addr *daddr = &hdr->daddr;
 	struct sock *sk;
 	struct udphdr *uh;
 	int err;
-	
+
+	if (buff + sizeof(struct udphdr) > skb->tail)
+		return;
+
 	uh = (struct udphdr *) buff;
 
-	sk = udp_v6_lookup(daddr, uh->dest, saddr, uh->source);
+	sk = udp_v6_lookup(daddr, uh->dest, saddr, uh->source, dev->ifindex);
    
 	if (sk == NULL) {
-		printk(KERN_DEBUG "icmp for unknown sock\n");
+		if (net_ratelimit())
+			printk(KERN_DEBUG "icmp for unknown sock\n");
 		return;
 	}
 
@@ -407,11 +501,10 @@
 	if (sock_queue_rcv_skb(sk,skb)<0) {
 		udp_stats_in6.UdpInErrors++;
 		ipv6_statistics.Ip6InDiscards++;
-		ipv6_statistics.Ip6InDelivers--;
-		skb->sk = NULL;
 		kfree_skb(skb);
 		return 0;
 	}
+  	ipv6_statistics.Ip6InDelivers++;
 	udp_stats_in6.UdpInDatagrams++;
 	return 0;
 }
@@ -430,7 +523,8 @@
 
 static struct sock *udp_v6_mcast_next(struct sock *sk,
 				      u16 loc_port, struct in6_addr *loc_addr,
-				      u16 rmt_port, struct in6_addr *rmt_addr)
+				      u16 rmt_port, struct in6_addr *rmt_addr,
+				      int dif)
 {
 	struct sock *s = sk;
 	unsigned short num = ntohs(loc_port);
@@ -446,6 +540,9 @@
 			   ipv6_addr_cmp(&np->daddr, rmt_addr))
 				continue;
 
+			if (s->bound_dev_if && s->bound_dev_if != dif)
+				continue;
+
 			if(!ipv6_addr_any(&np->rcv_saddr)) {
 				if(ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
 					return s;
@@ -468,16 +565,18 @@
 {
 	struct sock *sk, *sk2;
 	struct sk_buff *buff;
+	int dif;
 
 	sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
-	sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr);
+	dif = skb->dev->ifindex;
+	sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif);
 	if (!sk)
 		goto free_skb;
 
 	buff = NULL;
 	sk2 = sk;
 	while((sk2 = udp_v6_mcast_next(sk2->next, uh->dest, saddr,
-						  uh->source, daddr))) {
+						  uh->source, daddr, dif))) {
 		if (!buff) {
 			buff = skb_clone(skb, GFP_ATOMIC);
 			if (!buff)
@@ -486,59 +585,70 @@
 		if (sock_queue_rcv_skb(sk2, buff) >= 0)
 			buff = NULL;
 	}
-	if (buff) {
-		buff->sk = NULL;
+	if (buff)
 		kfree_skb(buff);
-	}
 	if (sock_queue_rcv_skb(sk, skb) < 0) {
-	free_skb:
-		skb->sk = NULL;
+free_skb:
 		kfree_skb(skb);
 	}
 }
 
-int udpv6_rcv(struct sk_buff *skb, struct device *dev,
-	      struct in6_addr *saddr, struct in6_addr *daddr,
-	      struct ipv6_options *opt, unsigned short len,
-	      int redo, struct inet6_protocol *protocol)
+int udpv6_rcv(struct sk_buff *skb, unsigned long len)
 {
 	struct sock *sk;
   	struct udphdr *uh;
-	int ulen;
+	struct device *dev = skb->dev;
+	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+	u32 ulen;
 
-	/*
-	 *	check if the address is ours...
-	 *	I believe that this is being done in IP layer
-	 */
-
-	uh = (struct udphdr *) skb->h.uh;
-  	
-  	ipv6_statistics.Ip6InDelivers++;
+	uh = skb->h.uh;
+	__skb_pull(skb, skb->h.raw - skb->data);
 
 	ulen = ntohs(uh->len);
-	
+
+	/* Check for jumbo payload */
+	if (ulen == 0 && skb->nh.ipv6h->payload_len == 0)
+		ulen = len;
+
 	if (ulen > len || len < sizeof(*uh)) {
-		printk(KERN_DEBUG "UDP: short packet: %d/%d\n", ulen, len);
+		if (net_ratelimit())
+			printk(KERN_DEBUG "UDP: short packet: %d/%ld\n", ulen, len);
 		udp_stats_in6.UdpInErrors++;
 		kfree_skb(skb);
 		return(0);
 	}
 
 	if (uh->check == 0) {
-		printk(KERN_DEBUG "IPv6: udp checksum is 0\n");
+		/* IPv6 draft-v2 section 8.1 says that we SHOULD log
+		   this error. Well, it is reasonable.
+		 */
+		if (net_ratelimit())
+			printk(KERN_INFO "IPv6: udp checksum is 0\n");
 		goto discard;
 	}
 
+	skb_trim(skb, ulen);
+
+#ifndef CONFIG_UDP_DELAY_CSUM
 	switch (skb->ip_summed) {
 	case CHECKSUM_NONE:
-		skb->csum = csum_partial((char*)uh, len, 0);
+		skb->csum = csum_partial((char*)uh, ulen, 0);
 	case CHECKSUM_HW:
-		if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, skb->csum)) {
+		if (csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum)) {
 			printk(KERN_DEBUG "IPv6: udp checksum error\n");
 			goto discard;
 		}
 	};
-	
+#else
+	if (skb->ip_summed==CHECKSUM_HW) {
+		if (csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum))
+			goto discard;
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	} else if (skb->ip_summed != CHECKSUM_UNNECESSARY)
+		skb->csum = ~csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, 0);
+#endif
+
 	len = ulen;
 
 	/* 
@@ -555,10 +665,16 @@
 	 * check socket cache ... must talk to Alan about his plans
 	 * for sock caches... i'll skip this for now.
 	 */
-
-	sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest);
-
+	
+	sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest, dev->ifindex);
+	
 	if (sk == NULL) {
+#ifdef CONFIG_UDP_DELAY_CSUM
+		if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
+		    csum_fold(csum_partial((char*)uh, len, skb->csum)))
+			goto discard;
+#endif
+		
 		udp_stats_in6.UdpNoPorts++;
 
 		icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev);
@@ -566,16 +682,13 @@
 		kfree_skb(skb);
 		return(0);
 	}
-
+	
 	/* deliver */
-
-	if (atomic_read(&sk->sock_readers))
-		__skb_queue_tail(&sk->back_log, skb);
-	else
-		udpv6_queue_rcv_skb(sk, skb);
+	
+	udpv6_queue_rcv_skb(sk, skb);
 	
 	return(0);
-
+	
 discard:
 	udp_stats_in6.UdpInErrors++;
 	kfree_skb(skb);
@@ -618,7 +731,7 @@
 	}
 
 	if (csum_partial_copy_fromiovecend(dst, udh->iov, offset,
-						     clen, &udh->wcheck))
+					   clen, &udh->wcheck))
 		return -EFAULT;
 
 	if (final) {
@@ -649,11 +762,11 @@
 
 static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen)
 {
-	struct ipv6_options opt_space;
+	struct ipv6_txoptions opt_space;
 	struct udpv6fakehdr udh;
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name;
-	struct ipv6_options *opt = NULL;
+	struct ipv6_txoptions *opt = NULL;
 	struct flowi fl;
 	int addr_len = msg->msg_namelen;
 	struct in6_addr *daddr;
@@ -661,22 +774,18 @@
 	int len = ulen + sizeof(struct udphdr);
 	int addr_type;
 	int hlimit = -1;
-
+	
 	int err;
 	
 	/* Rough check on arithmetic overflow,
 	   better check is made in ip6_build_xmit
-
-	   When jumbo header will be implemeted we will change it
-	   to something sort of (len will be size_t)
-	   ulen > SIZE_T_MAX - sizeof(struct udphdr)
-	 */
-	if (ulen < 0 || ulen > 0xFFFF - sizeof(struct udphdr))
+	   */
+	if (ulen < 0 || ulen > INT_MAX - sizeof(struct udphdr))
 		return -EMSGSIZE;
-
+	
 	if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
 		return(-EINVAL);
-
+	
 	if (sin6) {
 		if (sin6->sin6_family == AF_INET)
 			return udp_sendmsg(sk, msg, ulen);
@@ -692,14 +801,6 @@
 	       
 		udh.uh.dest = sin6->sin6_port;
 		daddr = &sin6->sin6_addr;
-
-		/* BUGGGG! If route is not cloned, this check always
-		   fails, hence dst_cache only slows down transmission --ANK
-		 */
-		if (sk->dst_cache && ipv6_addr_cmp(daddr, &np->daddr)) {
-			dst_release(sk->dst_cache);
-			sk->dst_cache = NULL;
-		}
 	} else {
 		if (sk->state != TCP_ESTABLISHED)
 			return(-ENOTCONN);
@@ -707,9 +808,9 @@
 		udh.uh.dest = sk->dport;
 		daddr = &sk->net_pinfo.af_inet6.daddr;
 	}
-
+	
 	addr_type = ipv6_addr_type(daddr);
-
+	
 	if (addr_type == IPV6_ADDR_MAPPED) {
 		struct sockaddr_in sin;
 		
@@ -720,24 +821,25 @@
 
 		return udp_sendmsg(sk, msg, ulen);
 	}
-
+	
 	udh.daddr = NULL;
 	fl.oif = sk->bound_dev_if;
 	
 	if (msg->msg_controllen) {
 		opt = &opt_space;
-		memset(opt, 0, sizeof(struct ipv6_options));
+		memset(opt, 0, sizeof(struct ipv6_txoptions));
 
 		err = datagram_send_ctl(msg, &fl.oif, &saddr, opt, &hlimit);
 		if (err < 0)
 			return err;
-		
-		if (opt->srcrt)
-			udh.daddr = daddr;
 	}
-	
+	if (opt == NULL || !(opt->opt_nflen|opt->opt_flen))
+		opt = np->opt;
+	if (opt && opt->srcrt)
+		udh.daddr = daddr;
+
 	udh.uh.source = sk->sport;
-	udh.uh.len = htons(len);
+	udh.uh.len = len < 0x1000 ? htons(len) : 0;
 	udh.uh.check = 0;
 	udh.iov = msg->msg_iov;
 	udh.wcheck = 0;
@@ -783,7 +885,7 @@
 	datagram_poll,			/* poll */
 	udp_ioctl,			/* ioctl */
 	NULL,				/* init */
-	NULL,				/* destroy */
+	inet6_destroy_sock,		/* destroy */
 	NULL,				/* shutdown */
 	ipv6_setsockopt,		/* setsockopt */
 	ipv6_getsockopt,		/* getsockopt */
diff -u --recursive --new-file v2.1.119/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.1.119/linux/net/ipx/af_ipx.c	Thu Jul 16 18:09:32 1998
+++ linux/net/ipx/af_ipx.c	Thu Aug 27 19:33:09 1998
@@ -1749,7 +1749,6 @@
 
 	sock_init_data(sock, sk);
 	sk->destruct	= NULL;
-	sk->mtu		= IPX_MTU;
 	sk->no_check 	= 1;		/* Checksum off by default */
 
 	MOD_INC_USE_COUNT;
diff -u --recursive --new-file v2.1.119/linux/net/ipx/af_spx.c linux/net/ipx/af_spx.c
--- v2.1.119/linux/net/ipx/af_spx.c	Sun Jun  7 11:16:40 1998
+++ linux/net/ipx/af_spx.c	Thu Aug 27 19:33:09 1998
@@ -103,7 +103,6 @@
 	spx_sock_init(sk);
 	sk->data_ready  = spx_rcv;
 	sk->destruct 	= NULL;
-        sk->mtu 	= IPX_MTU;
         sk->no_check 	= 1;
 
         MOD_INC_USE_COUNT;
diff -u --recursive --new-file v2.1.119/linux/net/netlink/af_netlink.c linux/net/netlink/af_netlink.c
--- v2.1.119/linux/net/netlink/af_netlink.c	Tue Jul 28 14:21:10 1998
+++ linux/net/netlink/af_netlink.c	Thu Aug 27 19:33:09 1998
@@ -98,7 +98,7 @@
 	return atomic_read(&sk->protinfo.af_netlink.locks);
 }
 
-static __inline__ struct sock *netlink_lookup(int protocol, pid_t pid)
+static __inline__ struct sock *netlink_lookup(int protocol, u32 pid)
 {
 	struct sock *sk;
 
@@ -116,10 +116,8 @@
 
 static void netlink_insert(struct sock *sk)
 {
-	cli();
 	sk->next = nl_table[sk->protocol];
 	nl_table[sk->protocol] = sk;
-	sti();
 }
 
 static void netlink_remove(struct sock *sk)
@@ -154,26 +152,10 @@
 	sock_init_data(sock,sk);
 	sk->destruct = NULL;
 	
-	sk->mtu=4096;
 	sk->protocol=protocol;
 	return 0;
 }
 
-static void netlink_destroy_timer(unsigned long data)
-{
-	struct sock *sk=(struct sock *)data;
-
-	if (!netlink_locked(sk) && !atomic_read(&sk->wmem_alloc)
-	    && !atomic_read(&sk->rmem_alloc)) {
-		sk_free(sk);
-		return;
-	}
-	
-	sk->timer.expires=jiffies+10*HZ;
-	add_timer(&sk->timer);
-	printk(KERN_DEBUG "netlink sk destroy delayed\n");
-}
-
 static int netlink_release(struct socket *sock, struct socket *peer)
 {
 	struct sock *sk = sock->sk;
@@ -223,11 +205,7 @@
 	}
 
 	if (atomic_read(&sk->rmem_alloc) || atomic_read(&sk->wmem_alloc)) {
-		sk->timer.data=(unsigned long)sk;
-		sk->timer.expires=jiffies+HZ;
-		sk->timer.function=netlink_destroy_timer;
-		add_timer(&sk->timer);
-		printk(KERN_DEBUG "impossible 333\n");
+		printk(KERN_DEBUG "netlink_release: impossible event. Please, report.\n");
 		return 0;
 	}
 
@@ -270,7 +248,7 @@
 		return -EINVAL;
 
 	/* Only superuser is allowed to listen multicasts */
-	if (nladdr->nl_groups && !suser())
+	if (nladdr->nl_groups && !capable(CAP_NET_ADMIN))
 		return -EPERM;
 
 	if (sk->protinfo.af_netlink.pid) {
@@ -315,7 +293,7 @@
 		return -EINVAL;
 
 	/* Only superuser is allowed to send multicasts */
-	if (!suser() && nladdr->nl_groups)
+	if (nladdr->nl_groups && !capable(CAP_NET_ADMIN))
 		return -EPERM;
 
 	sk->protinfo.af_netlink.dst_pid = nladdr->nl_pid;
@@ -344,11 +322,12 @@
 	return 0;
 }
 
-int netlink_unicast(struct sock *ssk, struct sk_buff *skb, pid_t pid, int nonblock)
+int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock)
 {
 	struct sock *sk;
 	int len = skb->len;
 	int protocol = ssk->protocol;
+	struct wait_queue wait = { current, NULL };
 
 retry:
 	for (sk = nl_table[protocol]; sk; sk = sk->next) {
@@ -366,17 +345,23 @@
 		}
 #endif
 
-		cli();
+		if (!nonblock) {
+			add_wait_queue(sk->sleep, &wait);
+			current->state = TASK_INTERRUPTIBLE;
+		}
+
 		if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf) {
 			if (nonblock) {
-				sti();
 				netlink_unlock(sk);
 				kfree_skb(skb);
 				return -EAGAIN;
 			}
-			interruptible_sleep_on(sk->sleep);
+
+			schedule();
+
+			current->state = TASK_RUNNING;
+			remove_wait_queue(sk->sleep, &wait);
 			netlink_unlock(sk);
-			sti();
 
 			if (signal_pending(current)) {
 				kfree_skb(skb);
@@ -384,8 +369,12 @@
 			}
 			goto retry;
 		}
-		sti();
-Nprintk("unicast_deliver %d\n", skb->len);
+
+		if (!nonblock) {
+			current->state = TASK_RUNNING;
+			remove_wait_queue(sk->sleep, &wait);
+		}
+
 		skb_orphan(skb);
 		skb_set_owner_r(skb, sk);
 		skb_queue_tail(&sk->receive_queue, skb);
@@ -417,8 +406,8 @@
 	return -1;
 }
 
-void netlink_broadcast(struct sock *ssk, struct sk_buff *skb, pid_t pid,
-		       unsigned group, int allocation)
+void netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid,
+		       u32 group, int allocation)
 {
 	struct sock *sk;
 	struct sk_buff *skb2 = NULL;
@@ -472,7 +461,7 @@
 	kfree_skb(skb);
 }
 
-void netlink_set_err(struct sock *ssk, pid_t pid, unsigned group, int code)
+void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code)
 {
 	struct sock *sk;
 	int protocol = ssk->protocol;
@@ -496,34 +485,28 @@
 {
 	struct sock *sk = sock->sk;
 	struct sockaddr_nl *addr=msg->msg_name;
-	pid_t dst_pid;
-	unsigned dst_groups;
+	u32 dst_pid;
+	u32 dst_groups;
 	struct sk_buff *skb;
-	int err;
 
 	if (msg->msg_flags&MSG_OOB)
 		return -EOPNOTSUPP;
 
-	if (msg->msg_flags&~MSG_DONTWAIT) {
-		printk("1 %08x\n", msg->msg_flags);
+	if (msg->msg_flags&~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
 		return -EINVAL;
-	}
 
 	if (msg->msg_namelen) {
-		if (addr->nl_family != AF_NETLINK) {
-			printk("2 %08x\n", addr->nl_family);
+		if (addr->nl_family != AF_NETLINK)
 			return -EINVAL;
-		}
 		dst_pid = addr->nl_pid;
 		dst_groups = addr->nl_groups;
-		if (dst_groups && !suser())
+		if (dst_groups && !capable(CAP_NET_ADMIN))
 			return -EPERM;
 	} else {
 		dst_pid = sk->protinfo.af_netlink.dst_pid;
 		dst_groups = sk->protinfo.af_netlink.dst_groups;
 	}
 
-
 	if (!sk->protinfo.af_netlink.pid)
 		netlink_autobind(sock);
 
@@ -536,17 +519,24 @@
 	NETLINK_CB(skb).dst_pid = dst_pid;
 	NETLINK_CB(skb).dst_groups = dst_groups;
 	memcpy(NETLINK_CREDS(skb), &scm->creds, sizeof(struct ucred));
-	memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
+
+	/* What can I do? Netlink is asynchronous, so that
+	   we will have to save current capabilities to
+	   check them, when this message will be delivered
+	   to corresponding kernel module.   --ANK (980802)
+	 */
+	NETLINK_CB(skb).eff_cap = current->cap_effective;
+
+	if (memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len)) {
+		kfree_skb(skb);
+		return -EFAULT;
+	}
 
 	if (dst_groups) {
 		atomic_inc(&skb->users);
 		netlink_broadcast(sk, skb, dst_pid, dst_groups, GFP_KERNEL);
 	}
-	err = netlink_unicast(sk, skb, dst_pid, msg->msg_flags&MSG_DONTWAIT);
-	if (err < 0) {
-		printk("3\n");
-	}
-	return err;
+	return netlink_unicast(sk, skb, dst_pid, msg->msg_flags&MSG_DONTWAIT);
 }
 
 static int netlink_recvmsg(struct socket *sock, struct msghdr *msg, int len,
@@ -594,7 +584,7 @@
 	if (sk->protinfo.af_netlink.cb
 	    && atomic_read(&sk->rmem_alloc) <= sk->rcvbuf/2)
 		netlink_dump(sk);
-	return err ? err : copied;
+	return err ? : copied;
 }
 
 /*
@@ -651,11 +641,11 @@
 	skb = sock_rmalloc(sk, NLMSG_GOODSIZE, 0, GFP_KERNEL);
 	if (!skb)
 		return -ENOBUFS;
-	
+
 	cb = sk->protinfo.af_netlink.cb;
 
 	len = cb->dump(skb, cb);
-	
+
 	if (len > 0) {
 		skb_queue_tail(&sk->receive_queue, skb);
 		sk->data_ready(sk, len);
@@ -667,7 +657,7 @@
 	memcpy(NLMSG_DATA(nlh), &len, sizeof(len));
 	skb_queue_tail(&sk->receive_queue, skb);
 	sk->data_ready(sk, skb->len);
-	
+
 	cb->done(cb);
 	sk->protinfo.af_netlink.cb = NULL;
 	netlink_destroy_callback(cb);
@@ -769,167 +759,6 @@
 
 #endif
 
-#if 0
-
-/* What a pity... It was good code, but at the moment it
-   results in unnecessary complications.
- */
-
-/*
- *	"High" level netlink interface. (ANK)
- *	
- *	Features:
- *		- standard message format.
- *		- pseudo-reliable delivery. Messages can be still lost, but
- *		  user level will know that they were lost and can
- *		  recover (f.e. gated could reread FIB and device list)
- *		- messages are batched.
- */
-
-/*
- *	Try to deliver queued messages.
- */
-
-static void nlmsg_delayed_flush(struct sock *sk)
-{
-	nlmsg_flush(sk, GFP_ATOMIC);
-}
-
-static void nlmsg_flush(struct sock *sk, int allocation)
-{
-	struct sk_buff *skb;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	while ((skb=skb_dequeue(&sk->write_queue)) != NULL) {
-		if (skb->users != 1) {
-			skb_queue_head(&sk->write_queue, skb);
-			break;
-		}
-		restore_flags(flags);
-		netlink_broadcast(sk, skb, 0, NETLINK_CB(skb).dst_groups, allocation);
-		cli();
-	}
-	start_bh_atomic();
-	restore_flags(flags);
-	if (skb) {
-		if (sk->timer.function)
-			del_timer(&sk->timer)
-		sk->timer.expires = jiffies + (sk->protinfo.af_netlink.delay ? : HZ/2);
-		sk->timer.function = (void (*)(unsigned long))nlmsg_delayed_flush;
-		sk->timer.data = (unsigned long)sk;
-		add_timer(&sk->timer);
-	}
-	end_bh_atomic();
-}
-
-/*
- *	Allocate room for new message. If it is impossible, return NULL.
- */
-
-void *nlmsg_broadcast(struct sock *sk, struct sk_buff **skbp,
-		      unsigned long type, int len,
-		      unsigned groups, int allocation)
-{
-	struct nlmsghdr *nlh;
-	struct sk_buff *skb;
-	int	rlen;
-	unsigned long flags;
-
-	rlen = NLMSG_SPACE(len);
-
-	save_flags(flags);
-	cli();
-	skb = sk->write_queue.tail;
-	if (skb == sk->write_queue.head)
-		skb = NULL;
-	if (skb == NULL || skb_tailroom(skb) < rlen || NETLINK_CB(skb).dst_groups != groups) {
-		restore_flags(flags);
-
-		if (skb)
-			nlmsg_flush(sk, allocation);
-
-		skb = sock_wmalloc(rlen > NLMSG_GOODSIZE ? rlen : NLMSG_GOODSIZE,
-				   sk, 0, allocation);
-
-		if (skb==NULL) {
-			printk (KERN_WARNING "nlmsg at unit %d overrunned\n", sk->protocol);
-			return NULL;
-		}
-
-		NETLINK_CB(skb).dst_groups = groups;
-		cli();
-		skb_queue_tail(&sk->write_queue, skb);
-	}
-	atomic_inc(&skb->users);
-	restore_flags(flags);
-
-	nlh = (struct nlmsghdr*)skb_put(skb, rlen);
-	nlh->nlmsg_type = type;
-	nlh->nlmsg_len = NLMSG_LENGTH(len);
-	nlh->nlmsg_seq = 0;
-	nlh->nlmsg_pid = 0;
-	*skbp = skb;
-	return nlh->nlmsg_data;
-}
-
-struct sk_buff* nlmsg_alloc(unsigned long type, int len,
-			    unsigned long seq, unsigned long pid, int allocation)
-{
-	struct nlmsghdr	*nlh;
-	struct sk_buff *skb;
-	int		rlen;
-
-	rlen = NLMSG_SPACE(len);
-
-	skb = alloc_skb(rlen, allocation);
-	if (skb==NULL)
-		return NULL;
-
-	nlh = (struct nlmsghdr*)skb_put(skb, rlen);
-	nlh->nlmsg_type = type;
-	nlh->nlmsg_len = NLMSG_LENGTH(len);
-	nlh->nlmsg_seq = seq;
-	nlh->nlmsg_pid = pid;
-	return skb;
-}
-
-void nlmsg_release(struct sk_buff *skb)
-{
-	atomic_dec(skb->users);
-}
-
-
-/*
- *	Kick message queue.
- *	Two modes:
- *		- synchronous (delay==0). Messages are delivered immediately.
- *		- delayed. Do not deliver, but start delivery timer.
- */
-
-void __nlmsg_transmit(struct sock *sk, int allocation)
-{
-	start_bh_atomic();
-	if (!sk->protinfo.af_netlink.delay) {
-		if (sk->timer.function) {
-			del_timer(&sk->timer);
-			sk->timer.function = NULL;
-		}
-		end_bh_atomic();
-		nlmsg_flush(sk, allocation);
-		return;
-	}
-	if (!sk->timer.function) {
-		sk->timer.expires = jiffies + sk->protinfo.af_netlink.delay;
-		sk->timer.function = (void (*)(unsigned long))nlmsg_delayed_flush;
-		sk->timer.data = (unsigned long)sk;
-		add_timer(&sk->timer);
-	}
-	end_bh_atomic();
-}
-
-#endif
 
 #ifdef CONFIG_PROC_FS
 static int netlink_read_proc(char *buffer, char **start, off_t offset,
diff -u --recursive --new-file v2.1.119/linux/net/netlink/netlink_dev.c linux/net/netlink/netlink_dev.c
--- v2.1.119/linux/net/netlink/netlink_dev.c	Wed Aug 26 11:37:45 1998
+++ linux/net/netlink/netlink_dev.c	Thu Aug 27 19:33:09 1998
@@ -144,6 +144,7 @@
 
 out:
 	open_map &= ~(1<<minor);
+	MOD_DEC_USE_COUNT;
 	return err;
 }
 
diff -u --recursive --new-file v2.1.119/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v2.1.119/linux/net/netrom/af_netrom.c	Fri May  8 23:14:57 1998
+++ linux/net/netrom/af_netrom.c	Thu Aug 27 19:33:09 1998
@@ -475,7 +475,6 @@
 
 	sock->ops    = &nr_proto_ops;
 	sk->protocol = protocol;
-	sk->mtu      = NETROM_MTU;	/* 236 */
 
 	skb_queue_head_init(&nr->ack_queue);
 	skb_queue_head_init(&nr->reseq_queue);
@@ -522,7 +521,6 @@
 	sk->sndbuf   = osk->sndbuf;
 	sk->debug    = osk->debug;
 	sk->state    = TCP_ESTABLISHED;
-	sk->mtu      = osk->mtu;
 	sk->sleep    = osk->sleep;
 	sk->zapped   = osk->zapped;
 
diff -u --recursive --new-file v2.1.119/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.119/linux/net/netsyms.c	Tue Jul 28 14:21:10 1998
+++ linux/net/netsyms.c	Thu Aug 27 19:33:09 1998
@@ -264,6 +264,7 @@
 EXPORT_SYMBOL(tcp_accept);
 EXPORT_SYMBOL(tcp_write_wakeup);
 EXPORT_SYMBOL(tcp_read_wakeup);
+EXPORT_SYMBOL(tcp_write_space);
 EXPORT_SYMBOL(tcp_poll);
 EXPORT_SYMBOL(tcp_ioctl);
 EXPORT_SYMBOL(tcp_shutdown);
diff -u --recursive --new-file v2.1.119/linux/net/rose/af_rose.c linux/net/rose/af_rose.c
--- v2.1.119/linux/net/rose/af_rose.c	Fri May  8 23:14:58 1998
+++ linux/net/rose/af_rose.c	Thu Aug 27 19:33:09 1998
@@ -552,7 +552,6 @@
 
 	sock->ops    = &rose_proto_ops;
 	sk->protocol = protocol;
-	sk->mtu      = ROSE_MTU;	/* 253 */
 
 	init_timer(&rose->timer);
 	init_timer(&rose->idletimer);
@@ -593,7 +592,6 @@
 	sk->sndbuf   = osk->sndbuf;
 	sk->debug    = osk->debug;
 	sk->state    = TCP_ESTABLISHED;
-	sk->mtu      = osk->mtu;
 	sk->sleep    = osk->sleep;
 	sk->zapped   = osk->zapped;
 
diff -u --recursive --new-file v2.1.119/linux/net/sched/cls_api.c linux/net/sched/cls_api.c
--- v2.1.119/linux/net/sched/cls_api.c	Thu May 14 19:47:45 1998
+++ linux/net/sched/cls_api.c	Thu Aug 27 19:33:09 1998
@@ -291,7 +291,7 @@
 			  struct tcf_proto *tp, unsigned long fh, int event)
 {
 	struct sk_buff *skb;
-	pid_t pid = oskb ? NETLINK_CB(oskb).pid : 0;
+	u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;
 
 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 	if (!skb)
diff -u --recursive --new-file v2.1.119/linux/net/sched/cls_rsvp.h linux/net/sched/cls_rsvp.h
--- v2.1.119/linux/net/sched/cls_rsvp.h	Tue Jun 23 10:01:31 1998
+++ linux/net/sched/cls_rsvp.h	Thu Aug 27 19:33:09 1998
@@ -537,7 +537,7 @@
 	if (s == NULL)
 		goto errout;
 	memset(s, 0, sizeof(*s));
-	memcpy(s->dst, dst, sizeof(*dst));
+	memcpy(s->dst, dst, sizeof(s->dst));
 	s->dpi = pinfo->dpi;
 	s->protocol = pinfo->protocol;
 	s->tunnelid = pinfo->tunnelid;
@@ -590,7 +590,6 @@
 static int rsvp_dump(struct tcf_proto *tp, unsigned long fh,
 		     struct sk_buff *skb, struct tcmsg *t)
 {
-	struct rsvp_head *head = tp->root;
 	struct rsvp_filter *f = (struct rsvp_filter*)fh;
 	struct rsvp_session *s;
 	unsigned char	 *b = skb->tail;
diff -u --recursive --new-file v2.1.119/linux/net/sched/sch_api.c linux/net/sched/sch_api.c
--- v2.1.119/linux/net/sched/sch_api.c	Tue Jul 21 00:15:33 1998
+++ linux/net/sched/sch_api.c	Thu Aug 27 19:33:09 1998
@@ -7,6 +7,10 @@
  *		2 of the License, or (at your option) any later version.
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Fixes:
+ *
+ * Rani Assaf <rani@magic.metawire.com> :980802: JIFFIES and CPU clock sources are repaired.
  */
 
 #include <linux/config.h>
@@ -506,7 +510,7 @@
 }
 
 static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q,
-			 pid_t pid, u32 seq, unsigned flags, int event)
+			 u32 pid, u32 seq, unsigned flags, int event)
 {
 	struct tcmsg *tcm;
 	struct nlmsghdr  *nlh;
@@ -538,7 +542,7 @@
 			 struct Qdisc *old, struct Qdisc *new)
 {
 	struct sk_buff *skb;
-	pid_t pid = oskb ? NETLINK_CB(oskb).pid : 0;
+	u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;
 
 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 	if (!skb)
@@ -715,7 +719,7 @@
 
 static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q,
 			  unsigned long cl,
-			  pid_t pid, u32 seq, unsigned flags, int event)
+			  u32 pid, u32 seq, unsigned flags, int event)
 {
 	struct tcmsg *tcm;
 	struct nlmsghdr  *nlh;
@@ -745,7 +749,7 @@
 			  struct Qdisc *q, unsigned long cl, int event)
 {
 	struct sk_buff *skb;
-	pid_t pid = oskb ? NETLINK_CB(oskb).pid : 0;
+	u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;
 
 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 	if (!skb)
@@ -850,7 +854,7 @@
 #endif
 
 #ifdef PSCHED_WATCHER
-u32 psched_time_mark;
+PSCHED_WATCHER psched_time_mark;
 
 static void psched_tick(unsigned long);
 
@@ -864,10 +868,10 @@
 	PSCHED_GET_TIME(dummy_stamp);
 	psched_timer.expires = jiffies + 4*HZ;
 #else
-	unsigned long jiffies = now;
+	unsigned long now = jiffies;
 	psched_time_base = ((u64)now)<<PSCHED_JSCALE;
 	psched_time_mark = now;
-	psched_timer.expires = jiffies + 60*60*HZ;
+	psched_timer.expires = now + 60*60*HZ;
 #endif
 	add_timer(&psched_timer);
 }
@@ -883,7 +887,7 @@
 	unsigned long stop;
 
 #if CPU == 586 || CPU == 686
-	if (!(boot_cpu_data.x86_capability & X86_FEATURE_TSC)
+	if (!(boot_cpu_data.x86_capability & X86_FEATURE_TSC))
 		return -1;
 #endif
 
@@ -895,7 +899,7 @@
 	PSCHED_GET_TIME(stamp);
 	do_gettimeofday(&tv);
 	while (jiffies < stop)
-		boundary();
+		barrier();
 	PSCHED_GET_TIME(stamp1);
 	do_gettimeofday(&tv1);
 	end_bh_atomic();
@@ -910,7 +914,7 @@
 	while ((delay>>=1) != 0)
 		psched_clock_scale++;
 	psched_us_per_tick = 1<<psched_clock_scale;
-	psched_clock_per_hz = (delay*(1000000/HZ))>>psched_clock_scale;
+	psched_clock_per_hz = (psched_tick_per_us*(1000000/HZ))>>psched_clock_scale;
 	return 0;
 }
 #endif
diff -u --recursive --new-file v2.1.119/linux/net/socket.c linux/net/socket.c
--- v2.1.119/linux/net/socket.c	Wed Aug 26 11:37:45 1998
+++ linux/net/socket.c	Thu Aug 27 19:33:09 1998
@@ -497,7 +497,6 @@
 {
 	struct fasync_struct *fa, *fna=NULL, **prev;
 	struct socket *sock;
-	unsigned long flags;
 	
 	if (on)
 	{
@@ -509,9 +508,8 @@
 	sock = socki_lookup(filp->f_dentry->d_inode);
 	
 	prev=&(sock->fasync_list);
-	
-	save_flags(flags);
-	cli();
+
+	lock_sock(sock->sk); 
 	
 	for (fa=*prev; fa!=NULL; prev=&fa->fa_next,fa=*prev)
 		if (fa->fa_file==filp)
@@ -523,7 +521,7 @@
 		{
 			fa->fa_fd=fd;
 			kfree_s(fna,sizeof(struct fasync_struct));
-			restore_flags(flags);
+			release_sock(sock->sk); 
 			return 0;
 		}
 		fna->fa_file=filp;
@@ -540,7 +538,8 @@
 			kfree_s(fa,sizeof(struct fasync_struct));
 		}
 	}
-	restore_flags(flags);
+
+	release_sock(sock->sk); 
 	return 0;
 }
 
@@ -1305,7 +1304,8 @@
 /*
  *	Perform a file control on a socket file descriptor.
  *
- *	FIXME: does this need an fd lock ?
+ *	Doesn't aquire a fd lock, because no network fcntl
+ *	function sleeps currently.
  */
 
 int sock_fcntl(struct file *filp, unsigned int cmd, unsigned long arg)
diff -u --recursive --new-file v2.1.119/linux/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c
--- v2.1.119/linux/net/sunrpc/clnt.c	Wed Aug 26 11:37:45 1998
+++ linux/net/sunrpc/clnt.c	Tue Sep  1 11:31:37 1998
@@ -413,6 +413,7 @@
 		return;
 	printk("RPC: buffer allocation failed for task %p\n", task); 
 
+	checksignals();
 	if (!signalled()) {
 		xprt_release(task);
 		task->tk_action = call_reserve;
diff -u --recursive --new-file v2.1.119/linux/net/sunrpc/sched.c linux/net/sunrpc/sched.c
--- v2.1.119/linux/net/sunrpc/sched.c	Wed Aug 26 11:37:45 1998
+++ linux/net/sunrpc/sched.c	Tue Sep  1 11:34:20 1998
@@ -416,8 +416,11 @@
 			current->timeout = 0;
 			sleep_on(&task->tk_wait);
 
-			/* When the task received a signal, remove from
-			 * any queues etc, and make runnable again. */
+			/*
+			 * When the task received a signal, remove from
+			 * any queues etc, and make runnable again.
+			 */
+			checksignals();
 			if (signalled())
 				__rpc_wake_up(task);
 
@@ -432,6 +435,7 @@
 		 * clean up after sleeping on some queue, we don't
 		 * break the loop here, but go around once more.
 		 */
+		checksignals();
 		if (!RPC_IS_ASYNC(task) && signalled()) {
 			dprintk("RPC: %4d got signal\n", task->tk_pid);
 			rpc_exit(task, -ERESTARTSYS);
@@ -551,6 +555,7 @@
 		if (flags & RPC_TASK_ASYNC)
 			return NULL;
 		current->timeout = jiffies + (HZ >> 4);
+		checksignals();
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
 	} while (!signalled());
@@ -797,6 +802,7 @@
 
 	dprintk("RPC: rpciod starting (pid %d)\n", rpciod_pid);
 	while (rpciod_users) {
+		checksignals();
 		if (signalled()) {
 			rpciod_killall();
 			flush_signals(current);
@@ -925,6 +931,7 @@
 	 */
 	while (rpciod_pid) {
 		dprintk("rpciod_down: waiting for pid %d to exit\n", rpciod_pid);
+		checksignals();
 		if (signalled()) {
 			dprintk("rpciod_down: caught signal\n");
 			break;
diff -u --recursive --new-file v2.1.119/linux/net/sunrpc/svcsock.c linux/net/sunrpc/svcsock.c
--- v2.1.119/linux/net/sunrpc/svcsock.c	Fri May  8 23:14:58 1998
+++ linux/net/sunrpc/svcsock.c	Tue Sep  1 11:33:10 1998
@@ -736,6 +736,7 @@
 	rqstp->rq_argbuf = rqstp->rq_defbuf;
 	rqstp->rq_resbuf = rqstp->rq_defbuf;
 
+	checksignals();
 	if (signalled())
 		return -EINTR;
 
@@ -754,6 +755,7 @@
 		 * We have to be able to interrupt this wait
 		 * to bring down the daemons ...
 		 */
+		checksignals();
 		current->state = TASK_INTERRUPTIBLE;
 		add_wait_queue(&rqstp->rq_wait, &wait);
 		end_bh_atomic();
diff -u --recursive --new-file v2.1.119/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.1.119/linux/net/unix/af_unix.c	Thu Aug 27 19:56:31 1998
+++ linux/net/unix/af_unix.c	Thu Aug 27 19:35:15 1998
@@ -8,6 +8,8 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  *
+ * Version:	$Id: af_unix.c,v 1.68 1998/08/26 13:18:35 davem Exp $
+ *
  * Fixes:
  *		Linus Torvalds	:	Assorted bug cures.
  *		Niibe Yutaka	:	async I/O support.
@@ -27,6 +29,10 @@
  *		Andreas Schwab	:	Replace inode by dentry for proper
  *					reference counting
  *		Kirk Petersen	:	Made this a module
+ *	    Christoph Rohland	:	Elegant non-blocking accept/connect algorithm.
+ *					Lots of bug fixes.
+ *	     Alexey Kuznetosv	:	Repaired (I hope) bugs introduces
+ *					by above two patches.
  *
  * Known differences from reference BSD that was tested:
  *
@@ -102,6 +108,7 @@
 #define UNIX_ABSTRACT(sk)	((sk)->protinfo.af_unix.addr->hash!=UNIX_HASH_SIZE)
 
 static void unix_destroy_socket(unix_socket *sk);
+static void unix_stream_write_space(struct sock *sk);
 
 extern __inline__ unsigned unix_hash_fold(unsigned hash)
 {
@@ -120,7 +127,7 @@
 
 extern __inline__ int unix_may_send(unix_socket *sk, unix_socket *osk)
 {
-	return (sk->type==osk->type);
+	return (unix_peer(osk) == NULL || unix_our_peer(sk, osk));
 }
 
 extern __inline__ void unix_lock(unix_socket *sk)
@@ -128,9 +135,9 @@
 	atomic_inc(&sk->sock_readers);
 }
 
-extern __inline__ int unix_unlock(unix_socket *sk)
+extern __inline__ void unix_unlock(unix_socket *sk)
 {
-	return atomic_dec_and_test(&sk->sock_readers);
+	atomic_dec(&sk->sock_readers);
 }
 
 extern __inline__ int unix_locked(unix_socket *sk)
@@ -257,7 +264,6 @@
 	if(!unix_locked(sk) && atomic_read(&sk->wmem_alloc) == 0)
 	{
 		sk_free(sk);
-		unix_remove_socket(sk);
 	
 		/* socket destroyed, decrement count		      */
 		MOD_DEC_USE_COUNT;
@@ -291,9 +297,6 @@
 
 	skpair=unix_peer(sk);
 
-	/* Try to flush out this socket. Throw out buffers at least */
-	unix_destroy_socket(sk);
-
 	if (skpair!=NULL)
 	{
 		if (sk->type==SOCK_STREAM && unix_our_peer(sk, skpair))
@@ -304,6 +307,9 @@
 		unix_unlock(skpair); /* It may now die */
 	}
 
+	/* Try to flush out this socket. Throw out buffers at least */
+	unix_destroy_socket(sk);
+
 	/*
 	 * Fixme: BSD difference: In BSD all sockets connected to use get
 	 *	  ECONNRESET and we die on the spot. In Linux we behave
@@ -311,6 +317,8 @@
 	 *	  dereference.
 	 *
 	 * Can't we simply set sock->err?
+	 *
+	 *	  What the above comment does talk about? --ANK(980817)
 	 */
 
 	unix_gc();		/* Garbage collect fds */	
@@ -321,13 +329,12 @@
 {
 	struct sk_buff *skb;
 
+	unix_remove_socket(sk);
+
 	while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
 	{
 		if(sk->state==TCP_LISTEN)
-		{
-			unix_unlock(sk);
 			unix_release_sock(skb->sk);
-		}
 		/* passed fds are erased in the kfree_skb hook	      */
 		kfree_skb(skb);
 	}
@@ -338,10 +345,9 @@
 		sk->protinfo.af_unix.dentry=NULL;
 	}
 	
-	if(unix_unlock(sk) && atomic_read(&sk->wmem_alloc) == 0)
+	if(!unix_locked(sk) && atomic_read(&sk->wmem_alloc) == 0)
 	{
 		sk_free(sk);
-		unix_remove_socket(sk);
 	
 		/* socket destroyed, decrement count		      */
 		MOD_DEC_USE_COUNT;
@@ -366,8 +372,6 @@
 	if (!sk->protinfo.af_unix.addr)
 		return -EINVAL;			/* No listens on an unbound socket */
 	sk->max_ack_backlog=backlog;
-	if (sk->ack_backlog < backlog)
-		sk->state_change(sk);
 	sk->state=TCP_LISTEN;
 	sock->flags |= SO_ACCEPTCON;
 	/* set credentials so connect can copy them */
@@ -380,61 +384,60 @@
 extern struct proto_ops unix_stream_ops;
 extern struct proto_ops unix_dgram_ops;
 
-static int unix_create1(struct socket *sock, struct sock **skp, int protocol)
+static struct sock * unix_create1(struct socket *sock, int stream)
 {
 	struct sock *sk;
 
-	if (protocol && protocol != PF_UNIX)
-		return -EPROTONOSUPPORT;
-
-	if (sock)
-	{
-		sock->state = SS_UNCONNECTED;
-
-		switch (sock->type)
-		{
-		case SOCK_STREAM:
-			sock->ops = &unix_stream_ops;
-			break;
-		/*
-		 *	Believe it or not BSD has AF_UNIX, SOCK_RAW though
-		 *	nothing uses it.
-		 */
-		case SOCK_RAW:
-			sock->type=SOCK_DGRAM;
-		case SOCK_DGRAM:
-			sock->ops = &unix_dgram_ops;
-			break;
-		default:
-			return -ESOCKTNOSUPPORT;
-		}
-	}
+	MOD_INC_USE_COUNT;
 	sk = sk_alloc(PF_UNIX, GFP_KERNEL, 1);
-	if (!sk)
-		return -ENOMEM;
+	if (!sk) {
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
 
 	sock_init_data(sock,sk);
 
+	if (stream)
+		sk->write_space = unix_stream_write_space; 
+
 	sk->destruct = unix_destruct_addr;
 	sk->protinfo.af_unix.family=PF_UNIX;
 	sk->protinfo.af_unix.dentry=NULL;
-	atomic_set(&sk->sock_readers, 1);	/* Us */
 	sk->protinfo.af_unix.readsem=MUTEX;	/* single task reading lock */
-	sk->mtu=4096;
 	sk->protinfo.af_unix.list=&unix_sockets_unbound;
 	unix_insert_socket(sk);
-	if (skp)
-		*skp =sk;
-	
-	/* socket created, increment count */
-	MOD_INC_USE_COUNT;
 
-	return 0;
+	return sk;
 }
 
 static int unix_create(struct socket *sock, int protocol)
 {
-	return unix_create1(sock, NULL, protocol);
+	int stream = 0;
+
+	if (protocol && protocol != PF_UNIX)
+		return -EPROTONOSUPPORT;
+
+	sock->state = SS_UNCONNECTED;
+
+	switch (sock->type) {
+	case SOCK_STREAM:
+		sock->ops = &unix_stream_ops;
+		stream = 1;
+		break;
+		/*
+		 *	Believe it or not BSD has AF_UNIX, SOCK_RAW though
+		 *	nothing uses it.
+		 */
+	case SOCK_RAW:
+		sock->type=SOCK_DGRAM;
+	case SOCK_DGRAM:
+		sock->ops = &unix_dgram_ops;
+		break;
+	default:
+		return -ESOCKTNOSUPPORT;
+	}
+
+	return unix_create1(sock, stream) ? 0 : -ENOMEM;
 }
 
 static int unix_release(struct socket *sock, struct socket *peer)
@@ -665,6 +668,22 @@
 	if (addr_len < 0)
 		return addr_len;
 
+	/* First of all allocate resources.
+	   If we will make it after state checks,
+	   we will have to recheck all again in any case.
+	 */
+
+	/*  Find listening sock */
+	other=unix_find_other(sunaddr, addr_len, sk->type, hash, &err);
+
+	/* create new sock for complete connection */
+	newsk = unix_create1(NULL, 1);
+
+	/* Allocate skb for sending to listening sock */
+	skb = NULL;
+	if (newsk)
+		skb = sock_wmalloc(newsk, 1, 0, GFP_KERNEL);
+
 	switch (sock->state) 
 	{
 		case SS_UNCONNECTED:
@@ -672,37 +691,25 @@
 			break;
 		case SS_CONNECTED:
 			/* Socket is already connected */
-			return -EISCONN;
+			err = -EISCONN;
+			goto out;
 		default:
-			return(-EINVAL);
+			err = -EINVAL;
+			goto out;
 	}
 
-	/*
-	 *	Now ready to connect
-	 */
-	 
-	sk->state=TCP_CLOSE;
-	
-	/*  Find listening sock */
-	other=unix_find_other(sunaddr, addr_len, sk->type, hash, &err);
-	if(other==NULL)
+	err = -EINVAL;
+	if (sk->state != TCP_CLOSE)
 		goto out;
 
-	/* create new sock for complete connection */
-	err = unix_create1(NULL, &newsk, PF_UNIX);
-	if (newsk == NULL)
+	/* Check that listener is in valid state. */
+	err = -ECONNREFUSED;
+	if (other == NULL || other->dead || other->state != TCP_LISTEN)
 		goto out;
 
-	/* Allocate skb for sending to listening sock */
-	skb=sock_alloc_send_skb(newsk, 0, 0, flags&O_NONBLOCK, &err);
-	if(skb==NULL)
-		/*
-		 * if it gives EAGAIN we should give back
-		 * EINPROGRESS. But this should not happen since the
-		 * socket should have some writespace left (it did not
-		 * allocate any memory until now)
-		 */
-		goto out_release;
+	err = -ENOMEM;
+	if (newsk == NULL || skb == NULL)
+		goto out;
 
 	UNIXCB(skb).attr = MSG_SYN;
 
@@ -715,7 +722,7 @@
 	sk->state=TCP_ESTABLISHED;
 	/* Set credentials */
 	sk->peercred = other->peercred;
-	
+
 	/* set up newly created sock */
 	unix_peer(newsk)=sk;
 	unix_lock(newsk);
@@ -738,12 +745,16 @@
 	other->ack_backlog++;
 	skb_queue_tail(&other->receive_queue,skb);
 	other->data_ready(other,0);		/* Wake up !	      */
-
+	unix_unlock(other);
 	return 0;
 
-out_release:
-	unix_destroy_socket(newsk);
 out:
+	if (skb)
+		kfree_skb(skb);
+	if (newsk)
+		unix_destroy_socket(newsk);
+	if (other)
+		unix_unlock(other);
 	return err;
 }
 
@@ -803,13 +814,14 @@
 			kfree_skb(skb);
 			continue;
 		}
-		break;
+		tsk = skb->sk;
+		sk->ack_backlog--;
+		kfree_skb(skb);
+		if (!tsk->dead) 
+			break;
+		unix_release_sock(tsk);
 	}
 
-	tsk=skb->sk;
-	sk->ack_backlog--;
-	unix_unlock(sk);	/* No longer locked to master	   */
-	kfree_skb(skb);
 
 	/* attach accepted sock to socket */
 	newsock->state=SS_CONNECTED;
@@ -1015,8 +1027,8 @@
 		size=len-sent;
 
 		/* Keep two messages in the pipe so it schedules better */
-		if (size > (sk->sndbuf - sizeof(struct sk_buff)) / 2)
-			size = (sk->sndbuf - sizeof(struct sk_buff)) / 2;
+		if (size > sk->sndbuf/2 - 16)
+			size = sk->sndbuf/2 - 16;
 
 		/*
 		 *	Keep to page sized kmalloc()'s as various people
@@ -1024,8 +1036,8 @@
 		 *	much.
 		 */
 
-		if (size > 3500)
-			limit = 3500;	/* Fall back to a page if we can't grab a big buffer this instant */
+		if (size > 4096-16)
+			limit = 4096-16; /* Fall back to a page if we can't grab a big buffer this instant */
 		else
 			limit = 0;	/* Otherwise just grab and wait */
 
@@ -1056,8 +1068,12 @@
 		if (scm->fp)
 			unix_attach_fds(scm, skb);
 
-		/* N.B. this could fail with -EFAULT */
-		memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size);
+		if (memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size)) {
+			kfree_skb(skb);
+			if (sent)
+				goto out;
+			return -EFAULT;
+		}
 
 		other=unix_peer(sk);
 
@@ -1247,8 +1263,12 @@
 		}
 
 		chunk = min(skb->len, size);
-		/* N.B. This could fail with -EFAULT */
-		memcpy_toiovec(msg->msg_iov, skb->data, chunk);
+		if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
+			skb_queue_head(&sk->receive_queue, skb);
+			if (copied == 0)
+				copied = -EFAULT;
+			break;
+		}
 		copied += chunk;
 		size -= chunk;
 
@@ -1299,28 +1319,20 @@
 	struct sock *sk = sock->sk;
 	unix_socket *other=unix_peer(sk);
 	
-	mode++;
+	mode = (mode+1)&(RCV_SHUTDOWN|SEND_SHUTDOWN);
 
-	if (mode&SEND_SHUTDOWN)
-	{
-		sk->shutdown|=SEND_SHUTDOWN;
+	if (mode) {
+		sk->shutdown |= mode;
 		sk->state_change(sk);
-		if(other && sk->type == SOCK_STREAM && other->state != TCP_LISTEN)
-		{
-			if (unix_our_peer(sk, other))
-				other->shutdown|=RCV_SHUTDOWN;
-			other->state_change(other);
-		}
-	}
-	other=unix_peer(sk);
-	if(mode&RCV_SHUTDOWN)
-	{
-		sk->shutdown|=RCV_SHUTDOWN;
-		sk->state_change(sk);
-		if(other && sk->type != SOCK_DGRAM && other->state != TCP_LISTEN)
-		{
-			if (unix_our_peer(sk, other))
-				other->shutdown|=SEND_SHUTDOWN;
+		if (other && sk->type == SOCK_STREAM &&
+		    unix_our_peer(sk, other)) {
+			int peer_mode = 0;
+
+			if (mode&RCV_SHUTDOWN)
+				peer_mode |= SEND_SHUTDOWN;
+			if (mode&SEND_SHUTDOWN)
+				peer_mode |= RCV_SHUTDOWN;
+			other->shutdown |= mode;
 			other->state_change(other);
 		}
 	}
@@ -1388,12 +1400,21 @@
 	 * we set writable also when the other side has shut down the
 	 * connection. This prevents stuck sockets.
 	 */
-	if (sk->sndbuf - atomic_read(&sk->wmem_alloc) >= MIN_WRITE_SPACE)
+	if (sk->sndbuf - (int)atomic_read(&sk->wmem_alloc) >= MIN_WRITE_SPACE)
 			mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
 
 	return mask;
 }
 
+static void unix_stream_write_space(struct sock *sk)
+{
+	if (sk->dead)  
+		return;
+	wake_up_interruptible(sk->sleep);
+	if (sk->sndbuf - (int)atomic_read(&sk->wmem_alloc) >= MIN_WRITE_SPACE)
+		sock_wake_async(sk->socket, 2);
+}
+
 #ifdef CONFIG_PROC_FS
 static int unix_read_proc(char *buffer, char **start, off_t offset,
 			  int length, int *eof, void *data)
@@ -1433,7 +1454,7 @@
 		}
 		buffer[len++]='\n';
 		
-		pos+=len;
+		pos = begin + len;
 		if(pos<offset)
 		{
 			len=0;
diff -u --recursive --new-file v2.1.119/linux/net/wanrouter/wanmain.c linux/net/wanrouter/wanmain.c
--- v2.1.119/linux/net/wanrouter/wanmain.c	Tue Aug 18 22:02:09 1998
+++ linux/net/wanrouter/wanmain.c	Fri Sep  4 12:10:19 1998
@@ -5,7 +5,7 @@
 *		the following common services for the WAN Link Drivers:
 *		 o WAN device managenment (registering, unregistering)
 *		 o Network interface management
-*		 o Physical connection management (dial-up, incoming calls)
+*		 o Physical connection management (dial-up, incomming calls)
 *		 o Logical connection management (switched virtual circuits)
 *		 o Protocol encapsulation/decapsulation
 *
@@ -25,7 +25,6 @@
 * Oct 15, 1997  Farhan Thawar   changed wan_encapsulate to add a pad byte of 0
 * Apr 20, 1998	Alan Cox	Fixed 2.1 symbols
 * May 17, 1998  K. Baranowski	Fixed SNAP encapsulation in wan_encapsulate
-* Aug 15, 1998	Arnaldo C. Melo	Fixed device_setup return value
 *****************************************************************************/
 
 #include <linux/stddef.h>	/* offsetof(), etc. */
@@ -373,8 +372,9 @@
 	struct proc_dir_entry* dent;
 	wan_device_t* wandev;
 
-	if (!capable(CAP_NET_ADMIN))
+	if (!capable(CAP_NET_ADMIN)){
 		return -EPERM;
+	}
 		
 	if ((cmd >> 8) != ROUTER_IOCTL)
 		return -EINVAL;
@@ -443,7 +443,7 @@
 
 	if (wandev->setup == NULL)	/* Nothing to do ? */
 		return 0;
-		
+	
 	conf = kmalloc(sizeof(wandev_conf_t), GFP_KERNEL);
 	if (conf == NULL)
 		return -ENOBUFS;
@@ -459,15 +459,17 @@
 
 	if (conf->data_size && conf->data)
 	{
-		if(conf->data_size > 1024 || conf->data_size < 0)
+		if(conf->data_size > 64000 || conf->data_size < 0){
 			goto bail;
+		}
 		data = kmalloc(conf->data_size, GFP_KERNEL);
 		if (data)
 		{
 			if(!copy_from_user(data, conf->data, conf->data_size))
 			{
 				conf->data=data;
-				err = wandev->setup(wandev,conf);
+				wandev->setup(wandev,conf);
+				err = 0;
 			}
 			else 
 				err = -ENOBUFS;
@@ -681,6 +683,7 @@
 	--wandev->ndev;
 	sti();			/****** critical section end ******/
 
+	printk("Unregistering '%s'\n", dev->name);
 	unregister_netdev(dev);
 	kfree(dev);
 	return 0;
diff -u --recursive --new-file v2.1.119/linux/net/wanrouter/wanproc.c linux/net/wanrouter/wanproc.c
--- v2.1.119/linux/net/wanrouter/wanproc.c	Wed Aug 26 11:37:45 1998
+++ linux/net/wanrouter/wanproc.c	Fri Sep  4 12:10:19 1998
@@ -29,6 +29,7 @@
 #include <asm/segment.h>	/* kernel <-> user copy */
 #include <asm/byteorder.h>	/* htons(), etc. */
 #include <asm/uaccess.h>	/* copy_to_user */
+#include <asm/io.h>
 #include <linux/wanrouter.h>	/* WAN router API definitions */
 
 
@@ -243,7 +244,7 @@
 
 /* Strings */
 static char conf_hdr[] =
-	"Device name    | port |IRQ|DMA|mem.addr|mem.size|"
+	"Device name    | port |IRQ|DMA| mem.addr |mem.size|"
 	"option1|option2|option3|option4\n";
 	
 static char stat_hdr[] =
@@ -386,16 +387,16 @@
 	wan_device_t* wandev;
 	strcpy(buf, conf_hdr);
 	for (wandev = router_devlist;
-	     wandev && (cnt < (PROC_BUFSZ - 80));
+	     wandev && (cnt < (PROC_BUFSZ - 120));
 	     wandev = wandev->next)
 	{
 		if (wandev->state) cnt += sprintf(&buf[cnt],
-			"%-15s|0x%-4X|%3u|%3u|0x%-6lX|0x%-6X|%7u|%7u|%7u|%7u\n",
+			"%-15s|0x%-4X|%3u|%3u| 0x%-8lX |0x%-6X|%7u|%7u|%7u|%7u\n",
 			wandev->name,
 			wandev->ioport,
 			wandev->irq,
 			wandev->dma,
-			wandev->maddr,
+			virt_to_phys(wandev->maddr),
 			wandev->msize,
 			wandev->hw_opt[0],
 			wandev->hw_opt[1],
diff -u --recursive --new-file v2.1.119/linux/net/x25/af_x25.c linux/net/x25/af_x25.c
--- v2.1.119/linux/net/x25/af_x25.c	Tue Jul 28 14:21:10 1998
+++ linux/net/x25/af_x25.c	Thu Aug 27 19:33:09 1998
@@ -468,7 +468,6 @@
 
 	sock->ops    = &x25_proto_ops;
 	sk->protocol = protocol;
-	sk->mtu      = X25_DEFAULT_PACKET_SIZE;	/* X25_PS128 */
 
 	x25->t21   = sysctl_x25_call_request_timeout;
 	x25->t22   = sysctl_x25_reset_request_timeout;
@@ -507,7 +506,6 @@
 	sk->sndbuf      = osk->sndbuf;
 	sk->debug       = osk->debug;
 	sk->state       = TCP_ESTABLISHED;
-	sk->mtu         = osk->mtu;
 	sk->sleep       = osk->sleep;
 	sk->zapped      = osk->zapped;
 
diff -u --recursive --new-file v2.1.119/linux/scripts/Configure linux/scripts/Configure
--- v2.1.119/linux/scripts/Configure	Wed Jun 24 22:54:14 1998
+++ linux/scripts/Configure	Fri Aug 28 10:37:13 1998
@@ -126,7 +126,7 @@
 	else
 		echo -n "$1"
 		[ -z "$3" ] && echo -n "(NEW) "
-		IFS='@' read ans </dev/tty || exit 1
+		IFS='@' read ans || exit 1
 		[ -z "$ans" ] && ans=$2
 	fi
 }
