diff -u --recursive --new-file v1.3.74/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.74/linux/Documentation/Configure.help	Fri Mar 15 16:03:10 1996
+++ linux/Documentation/Configure.help	Sat Mar 16 13:52:13 1996
@@ -240,6 +240,16 @@
   of the chipset, and permits faster I/O speeds to be set as well.
   See the README.ide and ali14xx.c files for more info.
 
+PROMISE DC4030 support (ALPHA)
+CONFIG_BLK_DEV_PROMISE
+  This driver is enabled at runtime using the "ide0=dc4030" kernel
+  boot parameter.  It enables support for the secondary IDE interface
+  of the chipset, and takes advantage of the caching features of the
+  card.  This driver is known to incur timeouts/retries during heavy
+  I/O to drives attached to the secondary interface.  CDROM and
+  TAPE devices are not supported yet.
+  See the README.ide and ali14xx.c files for more info.
+
 XT harddisk support
 CONFIG_BLK_DEV_XD
   Very old 8 bit hard disk controllers used in the IBM XT
diff -u --recursive --new-file v1.3.74/linux/Documentation/svga.txt linux/Documentation/svga.txt
--- v1.3.74/linux/Documentation/svga.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/svga.txt	Sat Mar 16 14:50:05 1996
@@ -0,0 +1,218 @@
+			Video Mode Selection Support 2.2
+	      (c) 1995, 1996 Martin Mares, <mj@k332.feld.cvut.cz>
+--------------------------------------------------------------------------------
+
+1. Intro
+~~~~~~~~
+   This small document describes the "Video Mode Selection" feature which
+allows to use various special video modes supported by the video BIOS. Due
+to usage of the BIOS, the selection is limited to the boot time (before the
+kernel decompression starts and works only on 80X86 machines.
+
+   IF YOU USE THIS FEATURE, I'LL BE MUCH PLEASED IF YOU SEND ME A MAIL
+DESCRIBING YOUR EXPERIENCE WITH IT. BUG REPORTS ARE ALSO WELCOME.
+
+   The video mode to be used is selected by a kernel paramater which can be
+specified in the kernel Makefile (the SVGA_MODE=... line) or by the "vga=..."
+option of LILO or by the "vidmode" utility (present in standard Linux utility
+packages). You can use the following settings of this parameter:
+
+   NORMAL_VGA - Standard 80x25 mode available on all display adapters.
+
+   EXTENDED_VGA	- Standard 8-pixel font mode: 80x43 on EGA, 80x50 on VGA.
+
+   ASK_VGA - Display a video mode menu upon startup (see below).
+
+   0..35 - Menu item number (when you have used the menu to view the list of
+      modes available on your adapter, you can specify the menu item you want
+      to use). 0..9 correspond to "0".."9", 10..35 to "a".."z". Warning: the
+      mode list displayed may vary as the kernel version changes, because the
+      modes are listed in a "first detected -- first displayed" manner. It's
+      better to use absolute mode numbers instead.
+
+   0x.... - Hexadecimal video mode ID (also displayed on the menu, see below
+      for exact meaning of the ID). Warning: rdev and LILO don't support
+      hexadecimal numbers -- you have to convert it to decimal manually.
+
+2. Menu
+~~~~~~~
+   The ASK_VGA mode causes the kernel to offer a video mode menu upon
+bootup. It displays a "Press <RETURN> to see video modes available, <SPACE>
+to continue or wait 30 secs" message. If you press <RETURN>, you enter the
+menu, if you press <SPACE> or wait 30 seconds, the kernel will boot up with
+the standard 80x25 mode set.
+
+   The menu looks like:
+
+Video adapter: <name-of-detected-video-adapter>
+Mode:    COLSxROWS:
+0  0F00  80x25
+1  0F01  80x50
+2  0F02  80x43
+3  0F03  80x26
+....
+Enter mode number: <flashing-cursor-here>
+
+   <name-of-detected-video-adapter> should contain a name of your video adapter
+or the chip in it or at least whether it's an EGA or VGA or VESA VGA (VGA with
+a VESA-compliant BIOS in it). If it doesn't match your configuration, tell me
+and I'll try to fix it somehow (you know, hardware detection is a real pain
+on PC's).
+
+   "0  0F00  80x25" tells that the first menu item (the menu items are numbered
+from "0" to "9" and from "a" to "z") is a 80x25 mode with ID=0x0f00 (see the
+next section for a description of the mode ID's).
+
+   <flashing-cursor-here> encourages you to write the item number or mode ID
+you wish to set and press <RETURN>. If the computer complains something about
+"Unknown mode ID", it tries to explain you that it isn't possible to set such
+a mode. It's also possible to press only <RETURN> which forces the current
+mode to be used.
+
+   The mode list may be a bit inaccurate on your machine (it isn't possible
+to autodetect all existing video cards and their mutations). Some of the
+modes may be unsettable, some of them might work incorrectly with Linux
+(the common case is mirroring of first few lines at the bottom of the screen
+because of BIOS bugs) or there can exist modes which are not displayed. If
+you think the list doesn't match your configuration, let me know and I'll try
+to add your configuration to the next version of the mode selector.
+
+   The modes displayed on the menu are partially sorted: The list starts with
+the standard modes (80x25 and 80x50) followed by "special" modes (80x28 and
+80x43), local modes (if the local modes feature is enabled), VESA modes and
+finally SVGA modes for the auto-detected adapter.
+
+   If you enter "scan" instead of item number / mode ID, the program will try
+to scan your video modes in a slightly aggresive, but much more accurate way.
+This should reveal all video modes supported by your BIOS. During this process,
+the screen will flash wildly and strange things will appear. If you are afraid
+this could damage your monitor, don't use this functions. After scanning, the
+mode ordering is a bit different: the auto-detected SVGA modes are not listed
+at all and the modes revealed by the scan are shown before the VESA modes.
+
+3. Mode ID's
+~~~~~~~~~~~~
+   Because of the complexity of all the video stuff, the video mode ID's
+used here are also a bit complex. A video mode ID is a 16-bit number usually
+expressed in a hexadecimal notation (starting with "0x"). The ID numbers
+can be divided to three regions:
+
+   0x0000 to 0x00ff - menu item references. 0x0000 is the first item.
+
+   0x0100 to 0x017f - standard BIOS modes. The ID is a BIOS video mode number
+	(as presented to INT 10, function 00) increased by 0x0100. You can
+	use any mode numbers even if not shown on the menu.
+
+   0x0200 to 0x04ff - VESA BIOS modes. The ID is a VESA mode ID increased by
+	0x0200. All VESA modes should be autodetected and shown on the menu.
+
+   0x0f00 to 0x0fff - special modes (they are set by various tricks -- usually
+	by modifying one of the standard modes). Currently available:
+	0x0f00	standard 80x25, don't reset mode if already set (=FFFF)
+	0x0f01	standard with 8-point font: 80x43 on EGA, 80x50 on VGA
+	0x0f02	VGA 80x43 (VGA switched to 350 scanlines with a 8-point font)
+	0x0f03	VGA 80x28 (standard VGA scans, but 14-point font)
+	0x0f04	leave current video mode
+
+   0x1000 to 0x7fff - modes specified by resolution. The code has a "0xRRCC"
+	form where RR is a number of rows and CC is a number of columns.
+	E.g., 0x1950 corresponds to a 80x25 mode, 0x2b84 to 132x43 etc.
+	This is the only fully portable way to refer to a non-standard mode.
+
+   0xff00 to 0xffff - aliases for backward compatibility:
+	0xffff	equivalent to 0x0f00 (standard 80x25)
+	0xfffe	equivalent to 0x0f01 (EGA 80x43 or VGA 80x50)
+
+   If you add 0x8000 to the mode ID, the program will try to recalculate
+vertical display timing according to mode parameters, which can be used to
+eliminate some annoying bugs of certain VGA BIOS'es -- mainly extra lines at
+the end of the display.
+
+4. Options
+~~~~~~~~~~
+   Some options can be set in the source text (in arch/i386/boot/video.S).
+All of them are simple #define's -- change them to #undef's when you want to
+switch them off. Currently supported:
+
+   CONFIG_VIDEO_SVGA - enables autodetection of SVGA cards. If your card is
+detected incorrectly, you can switch this off.
+
+   CONFIG_VIDEO_VESA - enables autodetection of VESA modes. If it doesn't work
+on your machine (or displays a "Error: Scanning of VESA modes failed" message),
+you can switch it off and report as a bug.
+
+   CONFIG_VIDEO_COMPACT - enables compacting of the video mode list. Duplicate
+entries (those with the same screen size) are deleted except for the first one
+(see the previous section for more information on mode ordering). However,
+it's possible that the first variant doesn't work, while some of the others do
+-- in this case turn this switch off to see the rest.
+
+   CONFIG_VIDEO_RETAIN - enables retaining of screen contents when switching
+video modes. Useful and probably harmless.
+
+   CONFIG_VIDEO_LOCAL - enables inclusion of "local modes" in the list. The
+local modes are added automatically to the beginning of the list not depending
+by hardware configuration. The local modes are listed in the source text after
+the "local_mode_table:" line. The comment before this line describes the format
+of the table (which also includes a video card name to be displayed on the
+top of the menu).
+
+5. Adding more cards
+~~~~~~~~~~~~~~~~~~~~
+   If you have a card not detected by the driver and you are a good programmer,
+feel free to add it to the source and send me a diff. It's very simple: You
+have to add a new entry to the svga_table consisting of a pointer to your mode
+table and a pointer to your detection routine. The order of entries in the
+svga_table defines the order of probing. Please use only reliable detection
+routines which are known to identify _only_ the card in question.
+
+   The detection routine is called with BP pointing to your mode table and
+ES containing 0xc000. If you want, you may alter BP allowing to select an
+appropriate mode table according to model ID detected. If the detection fails,
+return BP=0.
+
+   The mode table consists of lines containing a (BIOS mode number, rows,
+columns) triple and is finished by single zero byte followed by NUL-terminated
+adapter name.
+
+6. Still doesn't work?
+~~~~~~~~~~~~~~~~~~~~~~
+   When the mode detection doesn't work (e.g., the mode list is incorrect or
+the machine hangs instead of displaying the menu), try to switch off some of
+the configuration options listed in section 4. If it fails, you can still use
+your kernel with the video mode set directly via the kernel parameter.
+
+   In either case, please send me a bug report containing what _exactly_
+happens and how do the configuration switches affect the behaviour of the bug.
+
+   If you start Linux from the M$-DOS, you might also use some DOS tools for
+video mode setting. In this case, you must specify the 0x0f04 mode ("leave
+current settings") to Linux, because if you use anything other, the 80x25
+mode will be used automatically.
+
+   If you set some SVGA mode and there's one or more extra lines on the
+bottom of the display containing already scrolled-out lines, your VGA BIOS
+contains the most common video BIOS bug called "incorrect vertical display
+end setting". Adding 0x8000 to the mode ID might fix the problem. Unfortunately,
+this must be done manually -- no autodetection mechanisms are available.
+
+7. History
+~~~~~~~~~~
+1.0 (??-Nov-95)	First version supporting all adapters supported by the old
+		setup.S + Cirrus Logic 54XX. Present in some 1.3.4? kernels
+		and then removed due to instability on some machines.
+2.0 (28-Jan-96)	Rewritten from scratch. Cirrus Logic 64XX support added, almost
+		everything is configurable, the VESA support should be much more
+		stable, explicit mode numbering allowed, "scan" implemented etc.
+2.1 (30-Jan-96) VESA modes moved to 0x200-0x3ff. Mode selection by resolution
+		supported. Few bugs fixed. VESA modes are listed prior to
+		modes supplied by SVGA autodetection as they are more reliable.
+		CLGD autodetect works better. Doesn't depend on 80x25 being
+		active when started. Scanning fixed. 80x43 (any VGA) added.
+		Code cleaned up.
+2.2 (01-Feb-96)	EGA 80x43 fixed. VESA extended to 0x200-0x4ff (non-standard 02XX
+		VESA modes work now). Display end bug workaround supported.
+		Special modes renumbered to allow adding of the "recalculate"
+		flag, 0xffff and 0xfffe became aliases instead of real ID's.
+		Screen contents retained during mode changes.
+2.3 (15-Mar-96)	Changed to work with 1.3.74 kernel.
diff -u --recursive --new-file v1.3.74/linux/MAINTAINERS linux/MAINTAINERS
--- v1.3.74/linux/MAINTAINERS	Wed Mar 13 10:09:08 1996
+++ linux/MAINTAINERS	Sat Mar 16 13:52:13 1996
@@ -107,6 +107,12 @@
 L:	linux-ipx@vger.rutgers.edu [will change]
 S:	Maintained
 
+IDE DRIVER [GENERAL]
+P:	Mark Lord
+M:	mlord@pobox.com
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
 ISDN SUBSYSTEM
 P:	Fritz Elfert
 M:	fritz@wuemaus.franken.de
diff -u --recursive --new-file v1.3.74/linux/Makefile linux/Makefile
--- v1.3.74/linux/Makefile	Fri Mar 15 16:03:10 1996
+++ linux/Makefile	Sat Mar 16 13:52:10 1996
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 74
+SUBLEVEL = 75
 
 ARCH = i386
 
@@ -72,10 +72,11 @@
 # NFS_ROOT_NAME specifies the default name of the directory to mount
 # as root via NFS, if the kernel does not get the "root=" option from
 # the boot loader. The "%s" will be replaced by the IP-number of the
-# local system.
+# local system. Use empty string for default root path provided by BOOTP.
 #
 
-NFS_ROOT = -DNFS_ROOT="\"/tftpboot/%s\""
+#NFS_ROOT = -DNFS_ROOT="\"/tftpboot/%s\""
+NFS_ROOT = -DNFS_ROOT="\"\""
 
 #
 # INSTALL_PATH specifies where to place the updated kernel and system map
diff -u --recursive --new-file v1.3.74/linux/README linux/README
--- v1.3.74/linux/README	Wed Feb  7 15:11:19 1996
+++ linux/README	Fri Mar 15 11:01:45 1996
@@ -10,14 +10,17 @@
 bugs.  It is *strongly* recommended that you back up the previous kernel 
 before installing any new 1.3.xx release.
 
-If you need to use a proven and stable Linux kernel, please use either 
-1.0.9 or 1.2.xx.  All features which will be in the 1.3.xx releases will 
-be contained in 1.4.xx when the code base has stabilized again.
-
-If you decide to use 1.3, it is recommended that you join the kernel mailing 
-list.  To do this, e-mail majordomo@vger.rutgers.edu, and put in the body 
-of the message "subscribe linux-kernel" or "subscribe linux-kernel-digest" 
-for a daily digest of the mailing list (it is a high-traffic list.)
+If you need to use a proven and stable Linux kernel, please
+use 1.0.9 or 1.2.13.  All features found in the 1.3.xx releases
+will be contained in 2.0 when the code base has stabilized again.
+
+If you decide to use 1.3, it might be a good idea to follow the kernel
+channel, available as newsgroup (under linux.dev.kernel) and as mailing
+list.  To subscribe to the latter, e-mail majordomo@vger.rutgers.edu,
+and put in the body of the message "subscribe linux-kernel" or
+"subscribe linux-kernel-digest" for a daily digest of the mailing list
+(it is a high-traffic list). Note that getting subscribed or unsubscribed
+sometimes takes days or even weeks.
 
 WHAT IS LINUX?
 
@@ -42,8 +45,8 @@
    Project) books.  This README is not meant to be documentation on the
    system: there are much better sources available.
 
- - There are various readme's in the kernel doc/ subdirectory: these are
-   mainly used for kernel developers and some very kernel-specific
+ - There are various readme's in the kernel Documentation/ subdirectory:
+   these are mainly used for kernel developers and some very kernel-specific
    installation notes for some drivers for example.
 
 INSTALLING the kernel:
diff -u --recursive --new-file v1.3.74/linux/arch/alpha/Makefile linux/arch/alpha/Makefile
--- v1.3.74/linux/arch/alpha/Makefile	Tue Dec 26 04:45:34 1995
+++ linux/arch/alpha/Makefile	Sat Mar 16 13:52:12 1996
@@ -42,6 +42,12 @@
 msb my-special-boot:
 	@$(MAKEBOOT) msb
 
+bootimage:
+	@$(MAKEBOOT) bootimage
+
+srmboot:
+	@$(MAKEBOOT) srmboot
+
 archclean:
 	@$(MAKEBOOT) clean
 
diff -u --recursive --new-file v1.3.74/linux/arch/alpha/boot/Makefile linux/arch/alpha/boot/Makefile
--- v1.3.74/linux/arch/alpha/boot/Makefile	Thu Nov  9 11:23:46 1995
+++ linux/arch/alpha/boot/Makefile	Sat Mar 16 13:52:12 1996
@@ -34,6 +34,17 @@
 	( cat tools/lxboot tools/bootlx vmlinux.nh ) > /dev/rz0a
 	disklabel -rw rz0 'linux' tools/lxboot tools/bootlx
 
+bootimage:	tools/mkbb tools/lxboot tools/bootlx vmlinux.nh
+	( cat tools/lxboot tools/bootlx vmlinux.nh ) > bootimage
+	tools/mkbb bootimage tools/lxboot
+
+srmboot:	bootdevice bootimage
+	dd if=bootimage of=$(BOOTDEV) bs=512 seek=1 skip=1
+	tools/mkbb $(BOOTDEV) tools/lxboot
+
+bootdevice:
+	@test "$(BOOTDEV)" != ""  || (echo You must specify BOOTDEV ; exit -1)
+
 vmlinux.gz: vmlinux
 	gzip -fv vmlinux
 
@@ -56,6 +67,9 @@
 tools/build: tools/build.c
 	$(HOSTCC) tools/build.c -o tools/build
 
+tools/mkbb: tools/mkbb.c
+	$(HOSTCC) tools/mkbb.c -o tools/mkbb
+
 bootloader: $(OBJECTS)
 	$(LD) $(LINKFLAGS) \
 		$(OBJECTS) \
@@ -64,6 +78,7 @@
 		(rm -f bootloader && exit 1)
 
 clean:
-	rm -f $(TARGETS) bootloader tools/build
+	rm -f $(TARGETS) bootloader bootimage vmlinux.nh tools/build \
+		tools/mkbb tools/bootlx tools/lxboot
 
 dep:
diff -u --recursive --new-file v1.3.74/linux/arch/alpha/boot/head.S linux/arch/alpha/boot/head.S
--- v1.3.74/linux/arch/alpha/boot/head.S	Mon Jan 16 07:17:34 1995
+++ linux/arch/alpha/boot/head.S	Sat Mar 16 13:52:12 1996
@@ -129,3 +129,11 @@
 	ret	$31,($26)
 .end    dispatch
 
+	.align 3
+	.globl	tbi
+	.ent	tbi
+tbi:
+	.long PAL_tbi
+	ret	($26)
+	.end tbi
+
diff -u --recursive --new-file v1.3.74/linux/arch/alpha/boot/main.c linux/arch/alpha/boot/main.c
--- v1.3.74/linux/arch/alpha/boot/main.c	Sun Dec 17 11:43:08 1995
+++ linux/arch/alpha/boot/main.c	Sat Mar 16 13:52:12 1996
@@ -13,6 +13,7 @@
 #include <asm/system.h>
 #include <asm/console.h>
 #include <asm/hwrpb.h>
+#include <asm/pgtable.h>
 
 #include <stdarg.h>
 
diff -u --recursive --new-file v1.3.74/linux/arch/alpha/boot/tools/mkbb.c linux/arch/alpha/boot/tools/mkbb.c
--- v1.3.74/linux/arch/alpha/boot/tools/mkbb.c	Thu Jan  1 02:00:00 1970
+++ linux/arch/alpha/boot/tools/mkbb.c	Sat Mar 16 13:52:12 1996
@@ -0,0 +1,151 @@
+/* This utility makes a bootblock suitable for the SRM console/miniloader */
+
+/* Usage:
+ *	mkbb <device> <lxboot>
+ *
+ * Where <device> is the name of the device to install the bootblock on,
+ * and <lxboot> is the name of a bootblock to merge in.  This bootblock
+ * contains the offset and size of the bootloader.  It must be exactly
+ * 512 bytes long.
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/* Minimal definition of disklabel, so we don't have to include
+ * asm/disklabel.h (confuses make)
+ */
+#ifndef MAXPARTITIONS
+#define MAXPARTITIONS   8                       /* max. # of partitions */
+#endif
+
+#ifndef u8
+#define u8 unsigned char
+#endif
+
+#ifndef u16
+#define u16 unsigned short
+#endif
+
+#ifndef u32
+#define u32 unsigned int
+#endif
+
+struct disklabel {
+    u32	d_magic;				/* must be DISKLABELMAGIC */
+    u16	d_type, d_subtype;
+    u8	d_typename[16];
+    u8	d_packname[16];
+    u32	d_secsize;
+    u32	d_nsectors;
+    u32	d_ntracks;
+    u32	d_ncylinders;
+    u32	d_secpercyl;
+    u32	d_secprtunit;
+    u16	d_sparespertrack;
+    u16	d_sparespercyl;
+    u32	d_acylinders;
+    u16	d_rpm, d_interleave, d_trackskew, d_cylskew;
+    u32	d_headswitch, d_trkseek, d_flags;
+    u32	d_drivedata[5];
+    u32	d_spare[5];
+    u32	d_magic2;				/* must be DISKLABELMAGIC */
+    u16	d_checksum;
+    u16	d_npartitions;
+    u32	d_bbsize, d_sbsize;
+    struct d_partition {
+	u32	p_size;
+	u32	p_offset;
+	u32	p_fsize;
+	u8	p_fstype;
+	u8	p_frag;
+	u16	p_cpg;
+    } d_partitions[MAXPARTITIONS];
+};
+
+
+typedef union __bootblock {
+    struct {
+        char			__pad1[64];
+        struct disklabel	__label;
+    } __u1;
+    struct {
+	unsigned long		__pad2[63];
+	unsigned long		__checksum;
+    } __u2;
+    char		bootblock_bytes[512];
+    unsigned long	bootblock_quadwords[64];
+} bootblock;
+
+#define	bootblock_label		__u1.__label
+#define bootblock_checksum	__u2.__checksum
+
+main(int argc, char ** argv)
+{
+    bootblock		bootblock_from_disk;
+    bootblock		bootloader_image;
+    int			dev, fd;
+    int			i;
+    int			nread;
+
+    /* Make sure of the arg count */
+    if(argc != 3) {
+	fprintf(stderr, "Usage: %s device lxboot\n", argv[0]);
+	exit(0);
+    }
+
+    /* First, open the device and make sure it's accessible */
+    dev = open(argv[1], O_RDWR);
+    if(dev < 0) {
+	perror(argv[1]);
+	exit(0);
+    }
+
+    /* Now open the lxboot and make sure it's reasonable */
+    fd = open(argv[2], O_RDONLY);
+    if(fd < 0) {
+	perror(argv[2]);
+	close(dev);
+	exit(0);
+    }
+
+    /* Read in the lxboot */
+    nread = read(fd, &bootloader_image, sizeof(bootblock));
+    if(nread != sizeof(bootblock)) {
+	perror("lxboot read");
+	fprintf(stderr, "expected %d, got %d\n", sizeof(bootblock), nread);
+	exit(0);
+    }
+
+    /* Read in the bootblock from disk. */
+    nread = read(dev, &bootblock_from_disk, sizeof(bootblock));
+    if(nread != sizeof(bootblock)) {
+	perror("bootblock read");
+	fprintf(stderr, "expected %d, got %d\n", sizeof(bootblock), nread);
+	exit(0);
+    }
+
+    /* Swap the bootblock's disklabel into the bootloader */
+    bootloader_image.bootblock_label = bootblock_from_disk.bootblock_label;
+
+    /* Calculate the bootblock checksum */
+    bootloader_image.bootblock_checksum = 0;
+    for(i = 0; i < 63; i++) {
+	bootloader_image.bootblock_checksum += 
+			bootloader_image.bootblock_quadwords[i];
+    }
+
+    /* Write the whole thing out! */
+    lseek(dev, 0L, SEEK_SET);
+    if(write(dev, &bootloader_image, sizeof(bootblock)) != sizeof(bootblock)) {
+	perror("bootblock write");
+	exit(0);
+    }
+
+    close(fd);
+    close(dev);
+    exit(0);
+}
+
+
diff -u --recursive --new-file v1.3.74/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v1.3.74/linux/arch/alpha/config.in	Sat Mar  2 10:43:20 1996
+++ linux/arch/alpha/config.in	Sat Mar 16 13:52:12 1996
@@ -56,8 +56,14 @@
 	define_bool CONFIG_ALPHA_EV5 y
 	define_bool CONFIG_ALPHA_ALCOR y
 fi
-# This needs to be defined for all EV4 and EV45 CPUs:
+
+if [ "$CONFIG_ALPHA_JENSEN" = "y" ]
+then
+	bool 'Using SRM as bootloader' CONFIG_ALPHA_SRM
+fi
+
 define_bool CONFIG_ALPHA_NEED_ROUNDING_EMULATION y
+
 bool 'Echo console messages on /dev/ttyS1' CONFIG_SERIAL_ECHO
 if [ "$CONFIG_PCI" = "y" ]; then
   bool 'TGA Console Support' CONFIG_TGA_CONSOLE
diff -u --recursive --new-file v1.3.74/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c
--- v1.3.74/linux/arch/alpha/kernel/irq.c	Wed Mar 13 10:09:09 1996
+++ linux/arch/alpha/kernel/irq.c	Sat Mar 16 13:52:12 1996
@@ -281,7 +281,7 @@
 #endif
 }
 
-static inline void handle_irq(int irq, void *dev_id, struct pt_regs * regs)
+static inline void handle_irq(int irq, struct pt_regs * regs)
 {
 	struct irqaction * action = irq_action[irq];
 
@@ -340,6 +340,8 @@
 	unmask_irq(ack);
 }
 
+#ifdef CONFIG_PCI
+
 /*
  * Handle ISA interrupt via the PICs.
  */
@@ -469,6 +471,8 @@
 	}
 	restore_flags(flags);
 }
+
+#endif /* CONFIG_PCI */
 
 /*
  * Jensen is special: the vector is 0x8X0 for EISA interrupt X, and
diff -u --recursive --new-file v1.3.74/linux/arch/alpha/kernel/setup.c linux/arch/alpha/kernel/setup.c
--- v1.3.74/linux/arch/alpha/kernel/setup.c	Sat Mar  2 10:43:21 1996
+++ linux/arch/alpha/kernel/setup.c	Sat Mar 16 13:52:12 1996
@@ -128,8 +128,22 @@
 
 	ROOT_DEV = to_kdev_t(0x0802);		/* sda2 */
 	command_line[COMMAND_LINE_SIZE - 1] = '\0';
-	strcpy(command_line, COMMAND_LINE);
-	strcpy(saved_command_line, COMMAND_LINE);
+
+	/* Hack for Jensen... since we're restricted to 8 or 16 
+	 * chars for boot flags depending on the boot mode,
+	 * we need some shorthand.  This should do for 
+	 * installation.  Later we'll add other abbreviaitions
+	 * as well...
+	 */
+	if(strcmp(COMMAND_LINE, "INSTALL") == 0) {
+		strcpy(command_line, "root=/dev/fd0 load_ramdisk=1");
+		strcpy(saved_command_line, command_line);
+	}
+	else {
+		strcpy(command_line, COMMAND_LINE);
+		strcpy(saved_command_line, COMMAND_LINE);
+	}
+	printk("Command line: %s\n", command_line);
 
 	*cmdline_p = command_line;
 	*memory_start_p = (unsigned long) &_end;
diff -u --recursive --new-file v1.3.74/linux/arch/i386/boot/Makefile linux/arch/i386/boot/Makefile
--- v1.3.74/linux/arch/i386/boot/Makefile	Wed Mar 13 10:09:10 1996
+++ linux/arch/i386/boot/Makefile	Sat Mar 16 13:52:09 1996
@@ -65,7 +65,7 @@
 setup.o: setup.s
 	$(AS86) -o $@ $<
 
-setup.s: setup.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile
+setup.s: setup.S video.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile
 	$(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
 
 bootsect: bootsect.o
diff -u --recursive --new-file v1.3.74/linux/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- v1.3.74/linux/arch/i386/boot/setup.S	Wed Mar 13 10:09:11 1996
+++ linux/arch/i386/boot/setup.S	Sat Mar 16 13:52:10 1996
@@ -23,6 +23,9 @@
 ! High load stuff, initrd support and position independency
 ! by Hans Lermen & Werner Almesberger, February 1996
 ! <lermen@elserv.ffm.fgan.de>, <almesber@lrc.epfl.ch>
+!
+! Video handling moved to video.S by Martin Mares, March 1996
+! <mj@k332.feld.cvut.cz>
 
 ! NOTE! These had better be the same as in bootsect.s!
 #define __ASSEMBLY__
@@ -31,10 +34,6 @@
 #include <linux/version.h>
 #include <linux/compile.h>
 
-#ifndef SVGA_MODE
-#define SVGA_MODE ASK_VGA
-#endif
-
 ! Signature words to ensure LILO loaded us right
 #define SIG1	0xAA55
 #define SIG2	0x5A5A
@@ -122,13 +121,18 @@
 prtstr:	lodsb
 	and	al,al
 	jz	fin
-	call	prnt1
+	call	prtchr
 	jmp	prtstr
 fin:	ret
 
+! Space printing
+
+prtsp2:	call	prtspc		! Print double space
+prtspc:	mov	al,#0x20	! Print single space (fall-thru!)
+
 ! Part of above routine, this one just prints ascii al
 
-prnt1:	push	ax
+prtchr:	push	ax
 	push	cx
 	xor	bh,bh
 	mov	cx,#0x01
@@ -139,7 +143,7 @@
 	ret
 
 beep:	mov	al,#0x07
-	jmp	prnt1
+	jmp	prtchr
 	
 no_sig_mess:	.ascii	"No setup signature found ..."
 		db	0x00
@@ -216,49 +220,16 @@
 	int	0x15
 	mov	[2],ax
 
-! set the keyboard repeat rate to the max
+! Set the keyboard repeat rate to the max
 
 	mov	ax,#0x0305
 	xor	bx,bx		! clear bx
 	int	0x16
 
-! check for EGA/VGA and some config parameters
+! Check for video adapter and its parameters and allow the
+! user to browse video modes.
 
-	mov	ah,#0x12
-	mov	bl,#0x10
-	int	0x10
-	mov	[8],ax
-	mov	[10],bx
-	mov	[12],cx
-	mov	ax,#0x5019
-	cmp	bl,#0x10
-	je	novga
-	mov	ax,#0x1a00	! Added check for EGA/VGA discrimination
-	int	0x10
-	mov	bx,ax
-	mov	ax,#0x5019
-	movb	[15],#0		! by default, no VGA
-	cmp	bl,#0x1a	! 1a means VGA, anything else EGA or lower
-	jne	novga
-	movb	[15],#1		! we've detected a VGA
-	call	chsvga
-novga:	mov	[14],al
-	mov	ah,#0x03	! read cursor pos
-	xor	bh,bh		! clear bh
-	int	0x10		! save it in known place, con_init fetches
-	mov	[0],dx		! it from 0x90000.
-	
-! Get video-card data:
-	
-	mov	ah,#0x0f
-	int	0x10
-	mov	[4],bx		! bh = display page
-	mov	[6],ax		! al = video mode, ah = window width
-	xor	ax,ax
-	mov	es,ax		! Access low memory
-	seg es
-	mov	ax,[0x485]	! POINTS - Height of character matrix
-	mov	[16],ax
+	call	video
 
 ! Get hd0 data
 
@@ -307,7 +278,7 @@
 	stosb
 is_disk1:
 
-! check for PS/2 pointing device
+! Check for PS/2 pointing device
 
 	mov	ax,cs		! aka #SETUPSEG
 	sub	ax,#DELTA_INITSEG ! aka #INITSEG
@@ -361,7 +332,7 @@
 done_apm_bios:
 #endif
 
-! now we want to move to protected mode ...
+! Now we want to move to protected mode ...
 
 	seg cs
 	cmp	realmode_swtch,#0
@@ -516,7 +487,7 @@
 	mov	al,#0xFB		! mask all irq's but irq2 which
 	out	#0x21,al		! is cascaded
 
-! well, that certainly wasn't fun :-(. Hopefully it works, and we don't
+! Well, that certainly wasn't fun :-(. Hopefully it works, and we don't
 ! need no steenking BIOS anyway (except for the initial loading :-).
 ! The BIOS-routine wants lots of unnecessary data, and it's less
 ! "interesting" anyway. This is how REAL programmers do it.
@@ -530,12 +501,11 @@
 ! Note that the short jump isn't strictly needed, although there are
 ! reasons why it might be a good idea. It won't hurt in any case.
 !
-	xor	ax,ax
-	inc	ax		! protected mode (PE) bit
+	mov	ax,#1		! protected mode (PE) bit
 	lmsw	ax		! This is it!
 	jmp	flush_instr
 flush_instr:
-	mov	bx,#0		! Flag to indicate a boot
+	xor	bx,bx		! Flag to indicate a boot
 
 ! NOTE: For high loaded big kernels we need a
 !	jmpi    0x100000,KERNEL_CS
@@ -664,45 +634,6 @@
 	test	al,#2		! is input buffer full?
 	jnz	empty_8042	! yes - loop
 	ret
-!
-! Read a key and return the (US-)ascii code in al, scan code in ah
-!
-getkey:
-	xor	ah,ah
-	int	0x16
-	ret
-
-!
-! Read a key with a timeout of 30 seconds. The cmos clock is used to get
-! the time.
-!
-getkt:
-	call	gettime
-	add	al,#30		! wait 30 seconds
-	cmp	al,#60
-	jl	lminute
-	sub	al,#60
-lminute:
-	mov	cl,al
-again:	mov	ah,#0x01
-	int	0x16
-	jnz	getkey		! key pressed, so get it
-	call	gettime
-	cmp	al,cl
-	jne	again
-	mov	al,#0x20	! timeout, return default char `space'
-	ret
-
-!
-! Flush the keyboard buffer
-!
-flush:	mov	ah,#0x01
-	int	0x16
-	jz	empty
-	xor	ah,ah
-	int	0x16
-	jmp	flush
-empty:	ret
 
 !
 ! Read the cmos clock. Return the seconds in al
@@ -721,510 +652,16 @@
 	ret
 
 !
-! Delay is needed after doing i/o
+! Delay is needed after doing I/O
 !
 delay:
 	.word	0x00eb			! jmp $+2
 	ret
 
-! Routine trying to recognize type of SVGA-board present (if any)
-! and if it recognize one gives the choices of resolution it offers.
-! If one is found the resolution chosen is given by al,ah (rows,cols).
-
-chsvga:	cld
-	push	ds
-	push	cs
-	mov	ax,[0x01fa]
-	pop	ds
-	mov	modesave,ax
-	mov 	ax,#0xc000
-	mov	es,ax
-	mov	ax,modesave
-	cmp	ax,#NORMAL_VGA
-	je	defvga
-	cmp	ax,#EXTENDED_VGA
-	je	vga50
-	cmp	ax,#ASK_VGA
-	jne	svga
-	lea	si,msg1
-	call	prtstr
-	call	flush
-nokey:	call	getkt
-	cmp	al,#0x0d		! enter ?
-	je	svga			! yes - svga selection
-	cmp	al,#0x20		! space ?
-	je	defvga			! no - repeat
-	call 	beep
-	jmp	nokey
-defvga:	mov	ax,#0x5019
-	pop	ds
-	ret
-/* extended vga mode: 80x50 */
-vga50:
-	mov	ax,#0x1112
-	xor	bl,bl
-	int	0x10		! use 8x8 font set (50 lines on VGA)
-	mov	ax,#0x1200
-	mov	bl,#0x20
-	int	0x10		! use alternate print screen
-	mov	ax,#0x1201
-	mov	bl,#0x34
-	int	0x10		! turn off cursor emulation
-	mov	ah,#0x01
-	mov	cx,#0x0607
-	int	0x10		! turn on cursor (scan lines 6 to 7)
-	pop	ds
-	mov	ax,#0x5032	! return 80x50
-	ret
-/* extended vga mode: 80x28 */
-vga28:
-	pop	ax		! clean the stack
-	mov	ax,#0x1111
-	xor	bl,bl
-	int	0x10		! use 9x14 fontset (28 lines on VGA)
-	mov	ah, #0x01
-	mov	cx,#0x0b0c
-	int	0x10		! turn on cursor (scan lines 11 to 12)
-	pop	ds
-	mov	ax,#0x501c	! return 80x28
-	ret
-/* svga modes */
-!
-!	test for presence of an S3 VGA chip. The algorithm was taken
-!	from the SuperProbe package of XFree86 1.2.1
-!	report bugs to Christoph.Niemann@linux.org
-!
-svga:   cld
-	mov	cx,#0x0f35	! we store some constants in cl/ch
-	mov	dx,#0x03d4
-	movb	al,#0x38
-	call	inidx
-	mov	bh,al		! store current value of CRT-register 0x38
-	mov	ax,#0x0038
-	call	outidx		! disable writing to special regs
-	movb	al,cl		! check whether we can write special reg 0x35
-	call	inidx
-	movb	bl,al		! save the current value of CRT reg 0x35
-	andb	al,#0xf0	! clear bits 0-3
-	movb	ah,al
-	movb	al,cl		! and write it to CRT reg 0x35
-	call	outidx
-	call	inidx		! now read it back
-	andb	al,ch		! clear the upper 4 bits
-	jz	s3_2		! the first test failed. But we have a
-	movb	ah,bl		! second chance
-	mov	al,cl
-	call	outidx
-	jmp	s3_1		! do the other tests
-s3_2:	mov	ax,cx		! load ah with 0xf and al with 0x35
-	orb	ah,bl		! set the upper 4 bits of ah with the orig value
-	call	outidx		! write ...
-	call	inidx		! ... and reread 
-	andb	al,cl		! turn off the upper 4 bits
-	push	ax
-	movb	ah,bl		! restore old value in register 0x35
-	movb	al,cl
-	call	outidx
-	pop	ax
-	cmp	al,ch		! setting lower 4 bits was successful => bad
-	je	no_s3		! writing is allowed => this is not an S3
-s3_1:	mov	ax,#0x4838	! allow writing to special regs by putting
-	call	outidx		! magic number into CRT-register 0x38
-	movb	al,cl		! check whether we can write special reg 0x35
-	call	inidx
-	movb	bl,al
-	andb	al,#0xf0
-	movb	ah,al
-	movb	al,cl
-	call	outidx
-	call	inidx
-	andb	al,ch
-	jnz	no_s3		! no, we can't write => no S3
-	mov	ax,cx
-	orb	ah,bl
-	call	outidx
-	call	inidx
-	andb	al,ch
-	push	ax
-	movb	ah,bl		! restore old value in register 0x35
-	movb	al,cl
-	call	outidx
-	pop	ax
-	cmp	al,ch
-	jne	no_s31		! writing not possible => no S3
-	movb	al,#0x30
-	call	inidx		! now get the S3 id ...
-	lea	di,idS3
-	mov	cx,#0x10
-	repne
-	scasb
-	je	no_s31
-	lea 	si,dsc_S3	! table of descriptions of video modes for BIOS
-	lea	di,mo_S3	! table of sizes of video modes for my BIOS
-	movb	ah,bh
-	movb	al,#0x38
-	call	outidx		! restore old value of CRT register 0x38
-	br	selmod		! go ask for video mode
-no_s3:	movb	al,#0x35	! restore CRT register 0x35
-	movb	ah,bl
-	call	outidx
-no_s31:	movb	ah,bh
-	movb	al,#0x38
-	call	outidx		! restore old value of CRT register 0x38
-
-	lea 	si,idati		! Check ATI 'clues'
-	mov	di,#0x31
-	mov 	cx,#0x09
-	repe
-	cmpsb
-	jne	noati
-	lea	si,dscati
-	lea	di,moati
-	br	selmod
-noati:	mov	ax,#0x200f		! Check Ahead 'clues'
-	mov	dx,#0x3ce
-	out	dx,ax
-	inc	dx
-	in	al,dx
-	cmp	al,#0x20
-	je	isahed
-	cmp	al,#0x21
-	jne	noahed
-isahed:	lea	si,dscahead
-	lea	di,moahead
-	br	selmod
-noahed:	mov	dx,#0x3c3		! Check Chips & Tech. 'clues'
-	in	al,dx
-	or	al,#0x10
-	out	dx,al
-	mov	dx,#0x104		
-	in	al,dx
-	mov	bl,al
-	mov	dx,#0x3c3
-	in	al,dx
-	and	al,#0xef
-	out	dx,al
-	cmp	bl,[idcandt]
-	jne	nocant
-	lea	si,dsccandt
-	lea	di,mocandt
-	br	selmod
-nocant:	mov	dx,#0x3d4		! Check Cirrus 'clues'
-	mov	al,#0x0c
-	out	dx,al
-	inc	dx
-	in	al,dx
-	mov	bl,al
-	xor	al,al
-	out	dx,al
-	dec	dx
-	mov	al,#0x1f
-	out	dx,al
-	inc	dx
-	in	al,dx
-	mov	bh,al
-	xor	ah,ah
-	shl	al,#4
-	mov	cx,ax
-	mov	al,bh
-	shr	al,#4
-	add	cx,ax
-	shl	cx,#8
-	add	cx,#6
-	mov	ax,cx
-	mov	dx,#0x3c4
-	out	dx,ax
-	inc	dx
-	in	al,dx
-	and	al,al
-	jnz	nocirr
-	mov	al,bh
-	out	dx,al
-	in	al,dx
-	cmp	al,#0x01
-	jne	nocirr
-	call	rst3d4	
-	lea	si,dsccirrus
-	lea	di,mocirrus
-	br	selmod
-rst3d4:	mov	dx,#0x3d4
-	mov	al,bl
-	xor	ah,ah
-	shl	ax,#8
-	add	ax,#0x0c
-	out	dx,ax
-	ret	
-nocirr:	call	rst3d4			! Check Everex 'clues'
-	mov	ax,#0x7000
-	xor	bx,bx
-	int	0x10
-	cmp	al,#0x70
-	jne	noevrx
-	shr	dx,#4
-	cmp	dx,#0x678
-	je	istrid
-	cmp	dx,#0x236
-	je	istrid
-	lea	si,dsceverex
-	lea	di,moeverex
-	br	selmod
-istrid:	lea	cx,ev2tri
-	jmp	cx
-noevrx:	lea	si,idgenoa		! Check Genoa 'clues'
-	xor 	ax,ax
-	seg es
-	mov	al,[0x37]
-	mov	di,ax
-	mov	cx,#0x04
-	dec	si
-	dec	di
-l1:	inc	si
-	inc	di
-	mov	al,(si)
-	test	al,al
-	jz	l2
-	seg es
-	cmp	al,(di)
-l2:	loope 	l1
-	cmp	cx,#0x00
-	jne	nogen
-	lea	si,dscgenoa
-	lea	di,mogenoa
-	br	selmod
-nogen:	cld
-	lea	si,idoakvga
-	mov	di,#0x08
-	mov	cx,#0x08
-	repe
-	cmpsb
-	jne	nooak
-	lea	si,dscoakvga
-	lea	di,mooakvga
-	br	selmod
-nooak:	cld
-	lea	si,idparadise		! Check Paradise 'clues'
-	mov	di,#0x7d
-	mov	cx,#0x04
-	repe
-	cmpsb
-	jne	nopara
-	lea	si,dscparadise
-	lea	di,moparadise
-	br	selmod
-nopara:	mov	dx,#0x3c4		! Check Trident 'clues'
-	mov	al,#0x0e
-	out	dx,al
-	inc	dx
-	in	al,dx
-	xchg	ah,al
-	xor	al,al
-	out	dx,al
-	in	al,dx
-	xchg	al,ah
-	mov	bl,al		! Strange thing ... in the book this wasn't
-	and	bl,#0x02	! necessary but it worked on my card which
-	jz	setb2		! is a trident. Without it the screen goes
-	and	al,#0xfd	! blurred ...
-	jmp	clrb2		!
-setb2:	or	al,#0x02	!
-clrb2:	out	dx,al
-	and	ah,#0x0f
-	cmp	ah,#0x02
-	jne	notrid
-ev2tri:	lea	si,dsctrident
-	lea	di,motrident
-	jmp	selmod
-notrid:	mov	dx,#0x3cd		! Check Tseng 'clues'
-	in	al,dx			! Could things be this simple ! :-)
-	mov	bl,al
-	mov	al,#0x55
-	out	dx,al
-	in	al,dx
-	mov	ah,al
-	mov	al,bl
-	out	dx,al
-	cmp	ah,#0x55
- 	jne	notsen
-	lea	si,dsctseng
-	lea	di,motseng
-	jmp	selmod
-notsen:	mov	dx,#0x3cc		! Check Video7 'clues'
-	in	al,dx
-	mov	dx,#0x3b4
-	and	al,#0x01
-	jz	even7
-	mov	dx,#0x3d4
-even7:	mov	al,#0x0c
-	out	dx,al
-	inc	dx
-	in	al,dx
-	mov	bl,al
-	mov	al,#0x55
-	out	dx,al
-	in	al,dx
-	dec	dx
-	mov	al,#0x1f
-	out	dx,al
-	inc	dx
-	in	al,dx
-	mov	bh,al
-	dec	dx
-	mov	al,#0x0c
-	out	dx,al
-	inc	dx
-	mov	al,bl
-	out	dx,al
-	mov	al,#0x55
-	xor	al,#0xea
-	cmp	al,bh
-	jne	novid7
-	lea	si,dscvideo7
-	lea	di,movideo7
-	jmp	selmod
-novid7:	lea	si,dsunknown
-	lea	di,mounknown
-selmod:	xor	cx,cx
-	mov	cl,(di)
-	mov	ax,modesave
-	cmp	ax,#ASK_VGA
-	je	askmod
-	cmp	ax,#NORMAL_VGA
-	je	askmod
-	cmp	al,cl
-	jl	gotmode
-	push	si
-	lea	si,msg4
-	call	prtstr
-	pop	si
-askmod:	push	si
-	lea	si,msg2
-	call	prtstr
-	pop	si
-	push	si
-	push	cx
-tbl:	pop	bx
-	push	bx
-	mov	al,bl
-	sub	al,cl
-	call	modepr
-	lodsw
-	xchg	al,ah
-	call	dprnt
-	xchg	ah,al
-	push	ax
-	mov	al,#0x78
-	call	prnt1
-	pop	ax
-	call	dprnt
-	push	si
-	lea	si,crlf		! print CR+LF
-	call	prtstr
-	pop	si
-	loop	tbl
-	pop	cx
-	lea	si,msg3
-	call	prtstr
-	pop	si
-	add	cl,#0x30
-	jmp	nonum
-nonumb:	call	beep
-nonum:	call	getkey
-	cmp	al,#0x30	! ascii `0'
-	jb	nonumb
-	cmp	al,#0x3a	! ascii `9'
-	jbe	number
-	cmp	al,#0x61	! ascii `a'
-	jb	nonumb
-	cmp	al,#0x7a	! ascii `z'
-	ja	nonumb
-	sub	al,#0x27
-	cmp	al,cl
-	jae	nonumb
-	sub	al,#0x30
-	jmp	gotmode
-number: cmp	al,cl
-	jae	nonumb
-	sub	al,#0x30
-gotmode:	xor	ah,ah
-	or	al,al
-	beq	vga50
-	push	ax
-	dec	ax
-	beq	vga28
-	add	di,ax
-	mov	al,(di)
-	int 	0x10
-	pop	ax
-	shl	ax,#1
-	add	si,ax
-	lodsw
-	pop	ds
-	ret
-
-! Routine to write al into a VGA-register that is
-! accessed via an index register
-!
-! dx contains the address of the index register
-! al contains the index
-! ah contains the value to write to the data register (dx + 1)
-!
-! no registers are changed
-
-outidx:	out	dx,al
-	push	ax
-	mov	al,ah
-	inc	dx
-	out	dx,al
-	dec	dx
-	pop	ax
-	ret
-inidx:	out	dx,al
-	inc	dx
-	in	al,dx
-	dec	dx
-	ret
-
-! Routine to print a decimal value on screen, the value to be
-! printed is put in al (i.e 0-255). 
-
-dprnt:	push	ax
-	push	cx
-	xor	ah,ah		! Clear ah
-	mov	cl,#0x0a
-	idiv	cl
-	cmp	al,#0x09
-	jbe	lt100
-	call	dprnt
-	jmp	skip10
-lt100:	add	al,#0x30
-	call	prnt1
-skip10:	mov	al,ah
-	add	al,#0x30
-	call	prnt1	
-	pop	cx
-	pop	ax
-	ret
-
 !
-! Routine to print the mode number key on screen. Mode numbers
-! 0-9 print the ascii values `0' to '9', 10-35 are represented by
-! the letters `a' to `z'. This routine prints some spaces around the
-! mode no.
+! Descriptor tables
 !
 
-modepr:	push	ax
-	cmp	al,#0x0a
-	jb	digit		! Here is no check for number > 35
-	add	al,#0x27
-digit:	add	al,#0x30
-	mov	modenr, al
-	push 	si
-	lea	si, modestring
-	call	prtstr
-	pop	si
-	pop	ax
-	ret
-
 gdt:
 	.word	0,0,0,0		! dummy
 
@@ -1248,68 +685,25 @@
 	.word	0x800		! gdt limit=2048, 256 GDT entries
 	.word	512+gdt,0x9	! gdt base = 0X9xxxx
 
-msg1:		.ascii	"Press <RETURN> to see SVGA-modes available, <SPACE> to continue or wait 30 secs."
-		db	0x0d, 0x0a, 0x0a, 0x00
-msg2:		.ascii	"Mode:  COLSxROWS:"
-		db	0x0d, 0x0a, 0x0a, 0x00
-msg3:		db	0x0d, 0x0a
-		.ascii	"Choose mode by pressing the corresponding number or letter."
-crlf:		db	0x0d, 0x0a, 0x00
-msg4:		.ascii	"You passed an undefined mode number to setup. Please choose a new mode."
-		db	0x0d, 0x0a, 0x0a, 0x07, 0x00
-modestring:	.ascii	"   "
-modenr:		db	0x00	! mode number
-		.ascii	":    "
-		db	0x00
-		
-idati:		.ascii	"761295520"
-idcandt:	.byte	0xa5
-idgenoa:	.byte	0x77, 0x00, 0x99, 0x66
-idparadise:	.ascii	"VGA="
-idoakvga:	.ascii  "OAK VGA "
-idS3:		.byte	0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95
-		.byte	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0
-
-! Manufacturer:	  Numofmodes+2:	Mode:
-! Number of modes is the number of chip-specific svga modes plus the extended
-! modes available on any vga (currently 2)
-
-moati:		.byte	0x06,	0x23, 0x33, 0x22, 0x21
-moahead:	.byte	0x07,	0x22, 0x23, 0x24, 0x2f, 0x34
-mocandt:	.byte	0x04,	0x60, 0x61
-mocirrus:	.byte	0x06,	0x1f, 0x20, 0x22, 0x31
-moeverex:	.byte	0x0c,	0x03, 0x04, 0x07, 0x08, 0x0a, 0x0b, 0x16, 0x18, 0x21, 0x40
-mogenoa:	.byte	0x0c,	0x58, 0x5a, 0x60, 0x61, 0x62, 0x63, 0x64, 0x72, 0x74, 0x78
-moparadise:	.byte	0x04,	0x55, 0x54
-motrident:	.byte	0x09,	0x50, 0x51, 0x52, 0x57, 0x58, 0x59, 0x5a
-motseng:	.byte	0x07,	0x26, 0x2a, 0x23, 0x24, 0x22
-movideo7:	.byte	0x08,	0x40, 0x43, 0x44, 0x41, 0x42, 0x45
-mooakvga:	.byte   0x08,   0x00, 0x07, 0x4e, 0x4f, 0x50, 0x51
-mo_S3:		.byte	0x04,	0x54, 0x55
-mounknown:	.byte	0x02
-
-!			msb = Cols lsb = Rows:
-! The first two modes are standard vga modes available on any vga.
-! mode 0 is 80x50 and mode 1 is 80x28
-
-dscati:		.word	0x5032, 0x501c, 0x8419, 0x842c, 0x641e, 0x6419
-dscahead:	.word	0x5032, 0x501c, 0x842c, 0x8419, 0x841c, 0xa032, 0x5042
-dsccandt:	.word	0x5032, 0x501c, 0x8419, 0x8432
-dsccirrus:	.word	0x5032, 0x501c, 0x8419, 0x842c, 0x841e, 0x6425
-dsceverex:	.word	0x5032, 0x501c, 0x5022, 0x503c, 0x642b, 0x644b, 0x8419, 0x842c, 0x501e, 0x641b, 0xa040, 0x841e
-dscgenoa:	.word	0x5032, 0x501c, 0x5020, 0x642a, 0x8419, 0x841d, 0x8420, 0x842c, 0x843c, 0x503c, 0x5042, 0x644b
-dscparadise:	.word	0x5032, 0x501c, 0x8419, 0x842c
-dsctrident:	.word 	0x5032, 0x501c, 0x501e, 0x502b, 0x503c, 0x8419, 0x841e, 0x842b, 0x843c
-dsctseng:	.word	0x5032, 0x501c, 0x503c, 0x6428, 0x8419, 0x841c, 0x842c
-dscvideo7:	.word	0x5032, 0x501c, 0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c
-dscoakvga:	.word   0x5032, 0x501c, 0x2819, 0x5019, 0x503c, 0x843c, 0x8419, 0x842b
-dsc_S3:		.word	0x5032, 0x501c, 0x842b, 0x8419
-dsunknown:	.word	0x5032, 0x501c
-modesave:	.word	SVGA_MODE
+!
+! Include video setup & detection code
+!
+
+#include "video.S"
+
+!
+! Setup signature -- must be last
+!
 
-! This must be last
 setup_sig1:	.word	SIG1
 setup_sig2:	.word	SIG2
+
+!
+! After this point, there is some free space which is used by the video mode
+! handling code to store the temporary mode table (not used by the kernel).
+!
+
+modelist:
 
 .text
 endtext:
diff -u --recursive --new-file v1.3.74/linux/arch/i386/boot/video.S linux/arch/i386/boot/video.S
--- v1.3.74/linux/arch/i386/boot/video.S	Thu Jan  1 02:00:00 1970
+++ linux/arch/i386/boot/video.S	Sat Mar 16 14:47:59 1996
@@ -0,0 +1,1632 @@
+!
+!	Display adapter & video mode setup, version 2.3 (15-Mar-96)
+!
+!	Copyright (C) 1995, 1996 Martin Mares <mj@k332.feld.cvut.cz>
+!	Based on the original setup.S code (C) Linus Torvalds
+!
+
+! Enable autodetection of SVGA adapters and modes
+#define CONFIG_VIDEO_SVGA
+
+! Enable autodetection of VESA modes
+#define CONFIG_VIDEO_VESA
+
+! Enable compacting of mode table
+#define CONFIG_VIDEO_COMPACT
+
+! Retain screen contents when switching modes
+#define CONFIG_VIDEO_RETAIN
+
+! Enable local mode list
+#undef CONFIG_VIDEO_LOCAL
+
+! This code uses an extended set of video mode numbers. These include:
+! Aliases for standard modes
+!	NORMAL_VGA (-1)
+!	EXTENDED_VGA (-2)
+!	ASK_VGA (-3)
+! Video modes numbered by menu position -- NOT RECOMMENDED because of lack
+! of compatibility when extending the table. These are between 0x00 and 0xff.
+#define VIDEO_FIRST_MENU 0x0000
+! Standard BIOS video modes (BIOS number + 0x0100)
+#define VIDEO_FIRST_BIOS 0x0100
+! VESA BIOS video modes (VESA number + 0x0200)
+#define VIDEO_FIRST_VESA 0x0200
+! Special video modes
+#define VIDEO_FIRST_SPECIAL 0x0f00
+#define VIDEO_80x25 0x0f00
+#define VIDEO_8POINT 0x0f01
+#define VIDEO_80x43 0x0f02
+#define VIDEO_80x28 0x0f03
+#define VIDEO_CURRENT_MODE 0x0f04
+#define VIDEO_LAST_SPECIAL 0x0f05
+! Video modes given by resolution
+#define VIDEO_FIRST_RESOLUTION 0x1000
+
+! The "recalculate timings" flag
+#define VIDEO_RECALC 0x8000
+
+! Positions of various video parameters passed to the kernel
+#define PARAM_CURSOR_POS	0
+#define PARAM_VIDEO_PAGE	4
+#define PARAM_VIDEO_MODE	6
+#define PARAM_VIDEO_COLS	7
+#define PARAM_VIDEO_EGA_BX	10
+#define PARAM_VIDEO_LINES	14
+#define PARAM_HAVE_VGA		15
+#define PARAM_FONT_POINTS	16
+
+! Define DO_STORE according to CONFIG_VIDEO_RETAIN
+#ifdef CONFIG_VIDEO_RETAIN
+#define DO_STORE call store_screen
+#else
+#define DO_STORE
+#endif
+
+!
+! This is the main entry point called by setup.S
+!
+
+video:	push	ds		! We use different segments
+	push	ds		! FS contains original DS
+	pop	fs
+	push	cs		! DS is equal to CS
+	pop	ds
+	push	cs		! ES is equal to CS
+	pop	es
+	xor	ax,ax
+	mov	gs,ax		! GS is zero
+	cld
+	call	basic_detect	! Basic adapter type testing (EGA/VGA/MDA/CGA)
+	seg	fs		! User-selected video mode
+	mov	ax,[0x01fa]
+	cmp	ax,#ASK_VGA	! Bring up the menu
+	jz	vid2
+	call	mode_set	! Set the mode
+	jc	vid1
+	lea	si,badmdt	! Invalid mode ID
+	call	prtstr
+vid2:	call	mode_menu
+vid1:
+#ifdef CONFIG_VIDEO_RETAIN
+	call	restore_screen	! Restore screen contents
+#endif
+	call	mode_params	! Store mode parameters
+	pop	ds		! Restore original DS
+	ret
+
+!
+! Detect if we have CGA, MDA, EGA or VGA and pass it to the kernel.
+!
+
+basic_detect:
+	seg	fs		! Default is no VGA
+	movb	[PARAM_HAVE_VGA],#0
+
+	mov	ah,#0x12	! Check EGA/VGA
+	mov	bl,#0x10
+	int	0x10
+	seg	fs
+	mov	[PARAM_VIDEO_EGA_BX],bx	! Used for identification of EGA in the kernel
+	cmp	bl,#0x10	! No, this is a CGA/MDA/HGA card.
+	je	basret
+	incb	[adapter]
+
+	mov	ax,#0x1a00	! Check for EGA/VGA discrimination
+	int	0x10
+	cmp	al,#0x1a	! 1a means VGA, anything else EGA
+	jne	basret
+	seg	fs
+	incb	[PARAM_HAVE_VGA]	! We've detected a VGA
+	incb	[adapter]
+
+basret:	ret
+
+!
+! Store the video mode parameters for later usage by the kernel.
+! This is done by asking the BIOS except for the rows/columns
+! parameters in the default 80x25 mode -- these are set directly,
+! because some very obscure BIOSes supply insane values.
+!
+
+mode_params:
+	mov	ah,#0x03	! Read cursor position
+	xor	bh,bh
+	int	0x10
+	seg	fs
+	mov	[PARAM_CURSOR_POS],dx
+
+	mov	ah,#0x0f	! Read page/mode/width
+	int	0x10
+	seg	fs
+	mov	[PARAM_VIDEO_PAGE],bx
+	seg	fs
+	mov	[PARAM_VIDEO_MODE],ax	! Video mode and screen width
+	cmp	al,#7			! MDA/HGA => segment differs
+	jnz	mopar0
+	mov	[video_segment],#0xb000
+mopar0:	seg	gs			! Font size
+	mov	ax,[0x485]
+	seg	fs
+	mov	[PARAM_FONT_POINTS],ax	! (valid only on EGA/VGA)
+
+	cmpb	[def_mode],#0	! Default mode -- force sane values
+	jz	mopar1
+	seg	fs
+	movb	[PARAM_VIDEO_COLS],#80
+mopar2:	seg	fs
+	movb	[PARAM_VIDEO_LINES],#25
+	ret
+
+mopar1:	cmpb	[adapter],#0	! If we are on CGA/MDA/HGA, the screen must
+	jz	mopar2		! have 25 lines.
+	seg	gs		! On EGA/VGA, use the EGA+ BIOS variable
+	mov	al,[0x484]	! containing maximal line number.
+	inc	al
+	seg	fs
+	movb	[PARAM_VIDEO_LINES],al
+	ret
+
+!
+! The video mode menu
+!
+
+mode_menu:
+	lea	si,keymsg	! "Return/Space/Timeout" message
+	call	prtstr
+	call	flush
+nokey:	call	getkt
+	cmp	al,#0x0d	! ENTER ?
+	je	listm		! yes - manual mode selection
+	cmp	al,#0x20	! SPACE ?
+	je	defmd1		! no - repeat
+	call 	beep
+	jmp	nokey
+defmd1:	ret			! No mode selected => use the 80x25 default
+
+listm:	call	mode_table	! We need a mode table to be listed
+listm0:	lea	si,name_bann	! Print adapter name
+	call	prtstr
+	mov	si,[card_name]
+	or	si,si
+	jnz	an2
+	mov	al,[adapter]
+	lea	si,old_name
+	or	al,al
+	jz	an1
+	lea	si,ega_name
+	dec	al
+	jz	an1
+	lea	si,vga_name
+	jmp	an1
+an2:	call	prtstr
+	lea	si,svga_name
+an1:	call	prtstr
+	lea	si,listhdr	! Table header
+	call	prtstr
+	mov	dl,#0x30	! DL holds mode number
+	lea	si,modelist
+lm1:	cmp	(si),#ASK_VGA	! End?
+	jz	lm2
+	mov	al,dl		! Menu selection number
+	call	prtchr
+	call	prtsp2
+	lodsw
+	call	prthw		! Mode ID
+	call	prtsp2
+	mov	al,(si+1)
+	call	prtdec		! Rows
+	mov	al,#0x78	! 'x'
+	call	prtchr
+	lodsw
+	call	prtdec		! Columns
+	mov	al,#0x0d	! New line
+	call	prtchr
+	mov	al,#0x0a
+	call	prtchr
+	inc	dl		! Next character
+	cmp	dl,#0x3a
+	jnz	lm1
+	mov	dl,#0x61
+	jmp	lm1
+
+lm2:	lea	si,prompt	! Mode prompt
+	call	prtstr
+	lea	di,edit_buf	! Editor buffer
+lm3:	call	getkey
+	cmp	al,#0x0d	! Enter?
+	jz	lment
+	cmp	al,#0x08	! Backspace?
+	jz	lmbs
+	cmp	al,#0x20	! Printable?
+	jc	lm3
+	cmp	di,#edit_buf+4	! Enough space?
+	jz	lm3
+	stosb
+	call	prtchr
+	jmp	lm3
+
+lmbs:	cmp	di,#edit_buf	! Backspace
+	jz	lm3
+	dec	di
+	mov	al,#0x08
+	call	prtchr
+	call	prtspc
+	mov	al,#0x08
+	call	prtchr
+	jmp	lm3
+
+lment:	movb	(di),#0
+	lea	si,crlft
+	call	prtstr
+	lea	si,edit_buf
+	cmpb	(si),#0		! Empty string => use default mode
+	jz	lmdef
+	cmpb	(si+1),#0	! One character => menu selection
+	jz	mnusel
+	cmp	(si),#0x6373	! "scan" => mode scanning
+	jnz	lmhx
+	cmp	(si+2),#0x6e61
+	jz	lmscan
+lmhx:	xor	bx,bx		! Else => mode ID in hex
+lmhex:	lodsb
+	or	al,al
+	jz	lmuse1
+	sub	al,#0x30
+	jc	lmbad
+	cmp	al,#10
+	jc	lmhx1
+	sub	al,#7
+	and	al,#0xdf
+	cmp	al,#10
+	jc	lmbad
+	cmp	al,#16
+	jnc	lmbad
+lmhx1:	shl	bx,#4
+	or	bl,al
+	jmp	lmhex
+lmuse1:	mov	ax,bx
+	jmp	lmuse
+
+mnusel:	lodsb			! Menu selection
+	xor	ah,ah
+	sub	al,#0x30
+	jc	lmbad
+	cmp	al,#10
+	jc	lmuse
+	cmp	al,#0x61-0x30
+	jc	lmbad
+	sub	al,#0x61-0x30-10
+	cmp	al,#36
+	jnc	lmbad
+lmuse:	call	mode_set
+	jc	lmdef
+lmbad:	lea	si,unknt
+	call	prtstr
+	br	lm2
+
+lmscan:	cmpb	[adapter],#0	! Scanning supported only on EGA/VGA
+	jz	lmbad
+	mov	[mt_end],#0	! Scanning of modes: done as new autodetection
+	movb	[scanning],#1
+	call	mode_table
+	br	listm0
+
+lmdef:	ret
+
+!
+! Aliases for backward compatibility.
+!
+
+setalias:
+	mov	ax,#VIDEO_80x25
+	inc	bx
+	jz	mode_set
+	mov	al,#VIDEO_8POINT-VIDEO_FIRST_SPECIAL
+	inc	bx
+	jnz	setbad
+
+	! Fall-thru !
+
+!
+! Setting of user mode (AX=mode ID) => CF=success
+!
+
+mode_set:
+	mov	bx,ax
+	cmp	ah,#0xff
+	jz	setalias
+	test	ah,#VIDEO_RECALC>>8
+	jnz	setrec
+	cmp	ah,#VIDEO_FIRST_RESOLUTION>>8
+	jnc	setres
+	cmp	ah,#VIDEO_FIRST_SPECIAL>>8
+	jz	setspc
+	cmp	ah,#VIDEO_FIRST_VESA>>8
+	jnc	setvesa
+	or	ah,ah
+	jz	setmenu
+	dec	ah
+	jz	setbios
+setbad:	clc
+	movb	[do_restore],#0	! The screen needn't be restored
+	ret
+
+setvesa:
+	DO_STORE
+	sub	bh,#VIDEO_FIRST_VESA>>8
+	mov	ax,#0x4f02	! VESA BIOS mode set call
+	int	0x10
+	cmp	ax,#0x004f	! AL=4f if implemented, AH=0 if OK
+	jnz	setbad
+	stc
+	ret
+
+setbios:
+	DO_STORE
+	int	0x10		! Standard BIOS mode set call
+	push	bx
+	mov	ah,#0x0f	! Check if really set
+	int	0x10
+	pop	bx
+	cmp	al,bl
+	jnz	setbad
+	stc
+	ret
+
+setspc:	xor	bh,bh		! Set special mode
+	cmp	bl,#VIDEO_LAST_SPECIAL-VIDEO_FIRST_SPECIAL
+	jnc	setbad
+	add	bx,bx
+	.word	0xa7ff, spec_inits	! JMP [BX+spec_inits]
+
+setmenu:
+	push	bx		! Set mode chosen from menu
+	call	mode_table	! Build the mode table
+	pop	ax
+	shl	ax,#2
+	add	si,ax
+	cmp	si,di
+	jnc	setbad
+	mov	ax,(si)		! Fetch mode ID
+_m_s:	jmp	mode_set
+
+setres:
+	push	bx		! Set mode chosen by its resolution
+	call	mode_table
+	pop	bx
+	xchg	bh,bl
+setr1:	lodsw
+	cmp	ax,#ASK_VGA	! End of the list?
+	jz	setbad
+	lodsw
+	cmp	ax,bx
+	jnz	setr1
+	mov	ax,(si-4)	! Fetch mode ID
+	jmp	_m_s
+
+!
+! Recalculate vertical display end registers -- this fixes various
+! inconsistencies of extended modes on many adapters. Called when
+! the VIDEO_RECALC flag is set in the mode ID.
+!
+
+setrec:	sub	ah,#VIDEO_RECALC>>8	! Set the base mode
+	call	mode_set
+	jnc	rct3
+	seg	gs			! Font size in pixels
+	mov	ax,[0x485]
+	seg	gs			! Number of rows
+	mov	bl,[0x484]
+	inc	bl
+	mul	bl			! Number of visible
+	dec	ax			! scan lines - 1
+	mov	dx,#0x3d4
+	mov	bx,ax
+	mov	al,#0x12		! Lower 8 bits
+	mov	ah,bl
+	out	dx,ax
+	mov	al,#0x07		! Bits 8 and 9 in the overflow register
+	call	inidx
+	xchg	ah,al
+	and	ah,#0xbd
+	shr	bh,#1
+	jnc	rct1
+	or	ah,#0x02
+rct1:	shr	bh,#1
+	jnc	rct2
+	or	ah,#0x40
+rct2:	mov	al,#0x07
+	out	dx,ax
+	stc
+rct3:	ret
+
+!
+! Table of routines for setting of the special modes.
+!
+
+spec_inits:
+	.word	set_80x25
+	.word	set_8pixel
+	.word	set_80x43
+	.word	set_80x28
+	.word	set_current
+
+!
+! Set the 80x25 mode. If already set, do nothing.
+!
+
+set_80x25:
+	incb	[def_mode]	! Signal "we use default mode"
+use_80x25:
+	mov	ah,#0x0f	! Get current mode ID
+	int	0x10
+	cmp	ax,#0x5007	! Mode 7 (80x25 mono) is the only one available
+	jz	st80		! on CGA/MDA/HGA and is also available on EGAM
+	cmp	ax,#0x5003	! Unknown mode => force 80x25 color
+	jnz	force3
+st80:	cmpb	[adapter],#0	! CGA/MDA/HGA => mode 3/7 is always 80x25
+	jz	set80
+	seg	gs		! This is EGA+ -- beware of 80x50 etc.
+	mov	al,[0x0484]
+	or	al,al		! Some buggy BIOS'es set 0 rows
+	jz	set80
+	cmp	al,#24		! It's hopefully correct
+	jz	set80
+force3:	DO_STORE
+	mov	ax,#0x0003	! Forced set
+	int	0x10
+set80:	stc
+	ret
+
+!
+! Set the 80x50/80x43 8-pixel mode. Simple BIOS calls.
+!
+
+set_8pixel:
+	DO_STORE
+	call	use_80x25	! The base is 80x25
+set_8pt:
+	mov	ax,#0x1112	! Use 8x8 font
+	xor	bl,bl
+	int	0x10
+	mov	ax,#0x1200	! Use alternate print screen
+	mov	bl,#0x20
+	int	0x10
+	mov	ax,#0x1201	! Turn off cursor emulation
+	mov	bl,#0x34
+	int	0x10
+	mov	ah,#0x01	! Define cursor (scan lines 6 to 7)
+	mov	cx,#0x0607
+	int	0x10
+set_current:
+	stc
+	ret
+
+!
+! Set the 80x28 mode. This mode works on all VGA's, because it's a standard
+! 80x25 mode with 14-point fonts instead of 16-point.
+!
+
+set_80x28:
+	DO_STORE
+	call	use_80x25	! The base is 80x25
+	mov	ax,#0x1111	! Use 9x14 font
+	xor	bl,bl
+	int	0x10
+	mov	ah,#0x01	! Define cursor (scan lines 11 to 12)
+	mov	cx,#0x0b0c
+	int	0x10
+	stc
+	ret
+
+!
+! Set the 80x43 mode. This mode is works on all VGA's.
+! It's a 350-scanline mode with 8-pixel font.
+!
+
+set_80x43:
+	DO_STORE
+	mov	ax,#0x1201	! Set 350 scans
+	mov	bl,#0x30
+	int	0x10
+	mov	ax,#0x0003	! Reset video mode
+	int	0x10
+	jmp	set_8pt		! Use 8-pixel font
+
+#ifdef CONFIG_VIDEO_RETAIN
+
+!
+! Store screen contents to temporary buffer.
+!
+
+store_screen:
+	cmpb	[do_restore],#0		! Already stored?
+	jnz	stsr
+	push	ax
+	push	bx
+	incb	[do_restore]		! Screen will be restored later
+	mov	al,[def_mode]		! "Default mode" flag overriden
+	push	ax
+	movb	[def_mode],#0
+	call	mode_params		! Obtain and store basic parameters
+	pop	ax
+	mov	[def_mode],al
+	seg	fs			! of the current mode.
+	mov	ax,[PARAM_CURSOR_POS]
+	lea	di,modelist+8192
+	stosw
+	seg	fs
+	mov	ah,[PARAM_VIDEO_LINES]
+	seg	fs
+	mov	al,[PARAM_VIDEO_COLS]
+	stosw
+	mul	ah
+	mov	cx,ax			! CX=number of characters to store
+
+	push	ds			! Store the screen
+	mov	ds,[video_segment]
+	xor	si,si
+	rep
+	movsw
+	pop	ds
+	pop	bx
+	pop	ax
+stsr:	ret
+
+!
+! Restore screen contents from temporary buffer.
+!
+
+restore_screen:
+	cmpb	[do_restore],#0		! Has the screen been stored?
+	jz	res1
+	call	mode_params		! Get parameters of current mode
+	seg	fs
+	mov	cl,[PARAM_VIDEO_LINES]
+	seg	fs
+	mov	ch,[PARAM_VIDEO_COLS]
+	lea	si,modelist+8192	! Screen buffer
+	lodsw				! Set cursor position
+	mov	dx,ax
+	cmp	dh,cl
+	jc	res2
+	mov	dh,cl
+	dec	dh
+res2:	cmp	dl,ch
+	jc	res3
+	mov	dl,ch
+	dec	dl
+res3:	mov	ah,#0x02
+	mov	bh,#0x00
+	int	0x10
+	lodsw				! Display size
+	mov	dl,ah			! DL=number of lines
+	mov	ah,#0			! BX=physical length of orig. line
+	mov	bx,ax
+	cmp	dl,cl			! Too many?
+	jc	res4
+	push	ax
+	mov	al,dl
+	sub	al,cl
+	mul	bl
+	add	si,ax
+	add	si,ax
+	pop	ax
+	mov	dl,cl
+res4:	cmp	al,ch			! Too wide?
+	jc	res5
+	mov	al,ch			! AX=width of src. line
+res5:	mov	cl,#0
+	xchg	cl,ch
+	mov	bp,cx			! BP=width of dest. line
+	push	es
+	mov	es,[video_segment]
+	xor	di,di			! Move the data
+	add	bx,bx			! Convert BX and BP to _bytes_
+	add	bp,bp
+res6:	push	si
+	push	di
+	mov	cx,ax
+	rep
+	movsw
+	pop	di
+	pop	si
+	add	di,bp
+	add	si,bx
+	dec	dl
+	jnz	res6
+	pop	es			! Done
+res1:	ret
+
+#endif /* CONFIG_VIDEO_RETAIN */
+
+!
+! Build the table of video modes (stored after the setup.S code at the
+! `modelist' label. Each video mode record looks like:
+!	.word	MODE-ID		(our special mode ID (see above))
+!	.byte	rows		(number of rows)
+!	.byte	columns		(number of columns)
+! Returns address of the end of the table in DI, the end is marked
+! with a ASK_VGA ID.
+!
+
+mode_table:
+	mov	di,[mt_end]	! Already filled?
+	or	di,di
+	jnz	mtab1x
+	lea	di,modelist	! Store standard modes:
+
+	mov	eax,#VIDEO_80x25 + 0x50190000	! The 80x25 mode (ALL)
+	stosd
+	mov	al,[adapter]	! CGA/MDA/HGA -- no more modes
+	or	al,al
+	jz	mtabe
+	dec	al
+	jnz	mtabv
+	mov	eax,#VIDEO_8POINT + 0x502b0000	! The 80x43 EGA mode
+	stosd
+	jmp	mtabe
+mtab1x:	jmp	mtab1
+
+mtabv:	mov	eax,#VIDEO_8POINT + 0x50320000	! The 80x50 mode (VGA only)
+	stosd
+	mov	eax,#VIDEO_80x43 + 0x502b0000	! The 80x43 mode (VGA only)
+	stosd
+	mov	eax,#VIDEO_80x28 + 0x501c0000	! The 80x28 mode (VGA only)
+	stosd
+
+	cmpb	[scanning],#0			! Mode scan requested?
+	jz	mscan1
+	call	mode_scan
+mscan1:
+
+#ifdef CONFIG_VIDEO_LOCAL
+	call	local_modes
+#endif
+#ifdef CONFIG_VIDEO_VESA
+	call	vesa_modes			! Detect VESA VGA modes
+#endif
+#ifdef CONFIG_VIDEO_SVGA
+	cmpb	[scanning],#0			! Bypass when scanning
+	jnz	mscan2
+	call	svga_modes			! Detect SVGA cards & modes
+mscan2:
+#endif
+
+mtabe:
+
+#ifdef CONFIG_VIDEO_COMPACT
+	lea	si,modelist	! Compact video mode list if requested.
+	mov	dx,di
+	mov	di,si
+cmt1:	cmp	si,dx		! Scan all modes
+	jz	cmt2
+	lea	bx,modelist	! Find in previous entries
+	mov	cx,(si+2)
+cmt3:	cmp	si,bx
+	jz	cmt4
+	cmp	cx,(bx+2)	! Found => don't copy this entry
+	jz	cmt5
+	add	bx,#4
+	jmp	cmt3
+
+cmt4:	movsd			! Copy entry
+	jmp	cmt1
+
+cmt5:	add	si,#4		! Skip entry
+	jmp	cmt1
+
+cmt2:
+#endif	/* CONFIG_VIDEO_COMPACT */
+
+	mov	(di),#ASK_VGA	! End marker
+	mov	[mt_end],di
+mtab1:	lea	si,modelist	! Returning: SI=mode list, DI=list end
+ret0:	ret
+
+!
+! Detect VESA modes.
+!
+
+#ifdef CONFIG_VIDEO_VESA
+
+vesa_modes:
+	cmpb	[adapter],#2	! VGA only
+	jnz	ret0
+	mov	bp,di		! BP=original mode table end
+	add	di,#0x400	! Buffer space
+	mov	ax,#0x4f00	! VESA Get card info call
+	int	#0x10
+	mov	di,bp
+	cmp	ax,#0x004f	! Successful?
+	jnz	ret0
+	cmp	(di+0x400),#0x4556
+	jnz	ret0
+	cmp	(di+0x402),#0x4153
+	jnz	ret0
+	mov	[card_name],#vesa_name	! Set name to "VESA VGA"
+	push	gs
+	lgs	si,(di+0x40e)	! GS:SI=mode list
+	mov	cx,#128		! Iteration limit
+vesa1:	seg	gs		! Get next mode in the list
+	lodsw
+	cmp	ax,#0xffff	! End of the table?
+	jz	vesar
+	cmp	ax,#0x0080	! Check validity of mode ID
+	jc	vesa2
+	or	ah,ah		! Valid ID's are 0x0000-0x007f
+	jz	vesae		! and 0x0100-0x02ff.
+	cmp	ax,#0x0300
+	jnc	vesae
+vesa2:	push	cx
+	mov	cx,ax		! Get mode information structure
+	mov	ax,#0x4f01
+	int	0x10
+	mov	bx,cx		! BX=mode number
+	add	bh,#VIDEO_FIRST_VESA>>8
+	pop	cx
+	cmp	ax,#0x004f
+	jnz	vesae
+	mov	al,(di)		! Check capabilities. We require
+	and	al,#0x19	! a color text mode.
+	cmp	al,#0x09
+	jnz	vesan
+	cmp	(di+8),#0xb800	! Standard video memory address required
+	jnz	vesan
+	testb	(di),#2		! Mode characteristics supplied?
+	mov	(di),bx		! Store mode number
+	jz	vesa3
+	xor	dx,dx
+	mov	bx,(di+0x12)	! Width
+	or	bh,bh
+	jnz	vesan
+	mov	(di+3),bl
+	mov	ax,(di+0x14)	! Height
+	or	ah,ah
+	jnz	vesan
+	mov	(di+2),al
+	mul	bl
+	cmp	ax,#8193	! Small enough for Linux console driver?
+	jnc	vesan
+	jmp	vesaok
+
+vesa3:	sub	bx,#0x8108	! This mode has no detailed info specified,
+	jc	vesan		! so it must be a standard VESA mode.
+	cmp	bx,#5
+	jnc	vesan
+	mov	ax,(bx+vesa_text_mode_table)
+	mov	(di+2),ax
+vesaok:	add	di,#4		! The mode is valid. Store it.
+vesan:	loop	vesa1		! Next mode. Limit exceeded => error
+vesae:	lea	si,vesaer
+	call	prtstr
+	mov	di,bp		! Discard already found modes.
+vesar:	pop	gs
+	ret
+
+!
+! Dimensions of standard VESA text modes
+!
+
+vesa_text_mode_table:
+	db	60, 80		! 0108
+	db	25, 132		! 0109
+	db	43, 132		! 010A
+	db	50, 132		! 010B
+	db	60, 132		! 010C
+
+#endif	/* CONFIG_VIDEO_VESA */
+
+!
+! Scan for video modes. A bit dirty, but should work.
+!
+
+mode_scan:
+	mov	cx,#0x0100	! Start with mode 0
+scm1:	mov	ah,#0		! Test the mode
+	mov	al,cl
+	int	0x10
+	mov	ah,#0x0f
+	int	0x10
+	cmp	al,cl
+	jnz	scm2		! Mode not set
+	mov	dx,#0x3c0	! Test if it's a text mode
+	mov	al,#0x10		! Mode bits
+	call	inidx
+	and	al,#0x03
+	jnz	scm2
+	mov	dx,#0x3d4		! Cursor location
+	mov	al,#0x0f
+	call	inidx
+	or	al,al
+	jnz	scm2
+	mov	ax,cx		! OK, store the mode
+	stosw
+	seg	gs		! Number of rows
+	mov	al,[0x484]
+	inc	al
+	stosb
+	seg	gs		! Number of columns
+	mov	ax,[0x44a]
+	stosb
+scm2:	inc	cl
+	jns	scm1
+	mov	ax,#0x0003	! Return back to mode 3
+	int	0x10
+	ret
+
+tstidx:	out	dx,ax		! OUT DX,AX and inidx
+inidx:	out	dx,al		! Read from indexed VGA register
+	inc	dx		! AL=index, DX=index reg port -> AL=data
+	in	al,dx
+	dec	dx
+	ret
+
+!
+! Try to detect type of SVGA card and supply (usually approximate) video
+! mode table for it.
+!
+
+#ifdef CONFIG_VIDEO_SVGA
+
+svga_modes:
+	lea	si,svga_table	! Test all known SVGA adapters
+dosvga:	lodsw
+	mov	bp,ax		! Default mode table
+	or	ax,ax
+	jz	didsv1
+	lodsw			! Pointer to test routine
+	push	si
+	push	di
+	push	es
+	mov	bx,#0xc000
+	mov	es,bx
+	call	ax		! Call test routine
+	pop	es
+	pop	di
+	pop	si
+	or	bp,bp
+	jz	dosvga
+	mov	si,bp		! Found, copy the modes
+	mov	ah,#0x01
+cpsvga:	lodsb
+	or	al,al
+	jz	didsv
+	stosw
+	movsw
+	jmp	cpsvga
+
+didsv:	mov	[card_name],si	! Store pointer to card name
+didsv1:	ret
+
+!
+! Table of all known SVGA cards. For each card, we store a pointer to
+! a table of video modes supported by the card and a pointer to a routine
+! used for testing of presence of the card. The video mode table is always
+! followed by the name of the card or the chipset.
+!
+
+svga_table:
+	.word	s3_md, s3_test
+	.word	ati_md, ati_test
+	.word	chips_md, chips_test
+	.word	cirrus5_md, cirrus5_test
+	.word	cirrus6_md, cirrus6_test
+	.word	cirrus1_md, cirrus1_test
+	.word	ahead_md, ahead_test
+	.word	everex_md, everex_test
+	.word	genoa_md, genoa_test
+	.word	oak_md, oak_test
+	.word	paradise_md, paradise_test
+	.word	trident_md, trident_test
+	.word	tseng_md, tseng_test
+	.word	video7_md, video7_test
+	.word	0
+
+!
+! Test routines and mode tables:
+!
+
+! S3 - The test algorithm was taken from the SuperProbe package
+! for XFree86 1.2.1. Report bugs to Christoph.Niemann@linux.org
+
+s3_test:
+	mov	cx,#0x0f35	! we store some constants in cl/ch
+	mov	dx,#0x03d4
+	movb	al,#0x38
+	call	inidx
+	mov	bh,al		! store current value of CRT-register 0x38
+	mov	ax,#0x0038
+	call	outidx		! disable writing to special regs
+	movb	al,cl		! check whether we can write special reg 0x35
+	call	inidx
+	movb	bl,al		! save the current value of CRT reg 0x35
+	andb	al,#0xf0	! clear bits 0-3
+	movb	ah,al
+	movb	al,cl		! and write it to CRT reg 0x35
+	call	outidx
+	call	inidx		! now read it back
+	andb	al,ch		! clear the upper 4 bits
+	jz	s3_2		! the first test failed. But we have a
+	movb	ah,bl		! second chance
+	mov	al,cl
+	call	outidx
+	jmp	s3_1		! do the other tests
+s3_2:	mov	ax,cx		! load ah with 0xf and al with 0x35
+	orb	ah,bl		! set the upper 4 bits of ah with the orig value
+	call	outidx		! write ...
+	call	inidx		! ... and reread 
+	andb	al,cl		! turn off the upper 4 bits
+	push	ax
+	movb	ah,bl		! restore old value in register 0x35
+	movb	al,cl
+	call	outidx
+	pop	ax
+	cmp	al,ch		! setting lower 4 bits was successful => bad
+	je	no_s3		! writing is allowed => this is not an S3
+s3_1:	mov	ax,#0x4838	! allow writing to special regs by putting
+	call	outidx		! magic number into CRT-register 0x38
+	movb	al,cl		! check whether we can write special reg 0x35
+	call	inidx
+	movb	bl,al
+	andb	al,#0xf0
+	movb	ah,al
+	movb	al,cl
+	call	outidx
+	call	inidx
+	andb	al,ch
+	jnz	no_s3		! no, we can't write => no S3
+	mov	ax,cx
+	orb	ah,bl
+	call	outidx
+	call	inidx
+	andb	al,ch
+	push	ax
+	movb	ah,bl		! restore old value in register 0x35
+	movb	al,cl
+	call	outidx
+	pop	ax
+	cmp	al,ch
+	jne	no_s31		! writing not possible => no S3
+	movb	al,#0x30
+	call	inidx		! now get the S3 id ...
+	lea	di,idS3
+	mov	cx,#0x10
+	repne
+	scasb
+	je	no_s31
+	movb	ah,bh
+	movb	al,#0x38
+	jmp	s3rest
+no_s3:	movb	al,#0x35	! restore CRT register 0x35
+	movb	ah,bl
+	call	outidx
+no_s31:	xor	bp,bp		! Detection failed
+s3rest:	movb	ah,bh
+	movb	al,#0x38	! restore old value of CRT register 0x38
+outidx:	out	dx,al		! Write to indexed VGA register
+	push	ax		! AL=index, AH=data, DX=index reg port
+	mov	al,ah
+	inc	dx
+	out	dx,al
+	dec	dx
+	pop	ax
+	ret
+
+idS3:	.byte	0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95
+	.byte	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0
+
+s3_md:	.byte	0x54, 0x2b, 0x84
+	.byte	0x55, 0x19, 0x84
+	.byte	0
+	.ascii	"S3"
+	.byte	0
+
+! ATI cards.
+
+ati_test:
+	lea 	si,idati
+	mov	di,#0x31
+	mov 	cx,#0x09
+	repe
+	cmpsb
+	je	atiok
+	xor	bp,bp
+atiok:	ret
+
+idati:	.ascii	"761295520"
+
+ati_md:	.byte	0x23, 0x19, 0x84
+	.byte	0x33, 0x2c, 0x84
+	.byte	0x22, 0x1e, 0x64
+	.byte	0x21, 0x19, 0x64
+	.byte	0x58, 0x21, 0x50
+	.byte	0x5b, 0x1e, 0x50
+	.byte	0
+	.ascii	"ATI"
+	.byte	0
+
+! AHEAD
+
+ahead_test:
+	mov	ax,#0x200f
+	mov	dx,#0x3ce
+	out	dx,ax
+	inc	dx
+	in	al,dx
+	cmp	al,#0x20
+	je	isahed
+	cmp	al,#0x21
+	je	isahed
+	xor	bp,bp
+isahed:	ret
+
+ahead_md:
+	.byte	0x22, 0x2c, 0x84
+	.byte	0x23, 0x19, 0x84
+	.byte	0x24, 0x1c, 0x84
+	.byte	0x2f, 0x32, 0xa0
+	.byte	0x32, 0x22, 0x50
+	.byte	0x34, 0x42, 0x50
+	.byte	0
+	.ascii	"Ahead"
+	.byte	0
+
+! Chips & Tech.
+
+chips_test:
+	mov	dx,#0x3c3
+	in	al,dx
+	or	al,#0x10
+	out	dx,al
+	mov	dx,#0x104		
+	in	al,dx
+	mov	bl,al
+	mov	dx,#0x3c3
+	in	al,dx
+	and	al,#0xef
+	out	dx,al
+	cmp	bl,#0xa5
+	je	cantok
+	xor	bp,bp
+cantok:	ret
+
+chips_md:
+	.byte	0x60, 0x19, 0x84
+	.byte	0x61, 0x32, 0x84
+	.byte	0
+	.ascii	"Chips & Technologies"
+	.byte	0
+
+! Cirrus Logic 5X0
+
+cirrus1_test:
+	mov	dx,#0x3d4
+	mov	al,#0x0c
+	out	dx,al
+	inc	dx
+	in	al,dx
+	mov	bl,al
+	xor	al,al
+	out	dx,al
+	dec	dx
+	mov	al,#0x1f
+	out	dx,al
+	inc	dx
+	in	al,dx
+	mov	bh,al
+	xor	ah,ah
+	shl	al,#4
+	mov	cx,ax
+	mov	al,bh
+	shr	al,#4
+	add	cx,ax
+	shl	cx,#8
+	add	cx,#6
+	mov	ax,cx
+	mov	dx,#0x3c4
+	out	dx,ax
+	inc	dx
+	in	al,dx
+	and	al,al
+	jnz	nocirr
+	mov	al,bh
+	out	dx,al
+	in	al,dx
+	cmp	al,#0x01
+	je	iscirr
+nocirr:	xor	bp,bp
+iscirr: mov	dx,#0x3d4
+	mov	al,bl
+	xor	ah,ah
+	shl	ax,#8
+	add	ax,#0x0c
+	out	dx,ax
+	ret
+
+cirrus1_md:
+	.byte	0x1f, 0x19, 0x84
+	.byte	0x20, 0x2c, 0x84
+	.byte	0x22, 0x1e, 0x84
+	.byte	0x31, 0x25, 0x64
+	.byte	0
+	.ascii	"Cirrus Logic 5X0"
+	.byte	0
+
+! Cirrus Logic 54XX
+
+cirrus5_test:
+	mov	dx,#0x3c4
+	mov	al,#6
+	call	inidx
+	mov	bl,al			! BL=backup
+	mov	ax,#6
+	call	tstidx
+	cmp	al,#0x0f
+	jne	c5fail
+	mov	ax,#0x1206
+	call	tstidx
+	cmp	al,#0x12
+	jne	c5fail
+	mov	al,#0x1e
+	call	inidx
+	mov	bh,al
+	mov	ah,bh
+	and	ah,#0xc0
+	mov	al,#0x1e
+	call	tstidx
+	and	al,#0x3f
+	jne	c5xx
+	mov	al,#0x1e
+	mov	ah,bh
+	or	ah,#0x3f
+	call	tstidx
+	xor	al,#0x3f
+	and	al,#0x3f
+c5xx:	pushf
+	mov	al,#0x1e
+	mov	ah,bh
+	out	dx,ax
+	popf
+	je	c5done
+c5fail:	xor	bp,bp
+c5done:	mov	al,#6
+	mov	ah,bl
+	out	dx,ax
+	ret
+
+cirrus5_md:
+	.byte	0x14, 0x19, 0x84
+	.byte	0x54, 0x2b, 0x84
+	.byte	0
+	.ascii	"Cirrus Logic 54XX"
+	.byte	0
+
+! Cirrus Logic 64XX -- no known extra modes, but must be identified, because
+! it's misidentified by the Ahead test.
+
+cirrus6_test:
+	mov	dx,#0x3ce
+	mov	al,#0x0a
+	call	inidx
+	mov	bl,al			! BL=backup
+	mov	ax,#0xce0a
+	call	tstidx
+	or	al,al
+	jne	c2fail
+	mov	ax,#0xec0a
+	call	tstidx
+	cmp	al,#0x01
+	jne	c2fail
+	mov	al,#0xaa
+	call	inidx	! 4X, 5X, 7X and 8X are valid 64XX chip ID's
+	shr	al,#4
+	sub	al,#4
+	jz	c6done
+	dec	al
+	jz	c6done
+	sub	al,#2
+	jz	c6done
+	dec	al
+	jz	c6done
+c2fail:	xor	bp,bp
+c6done:	mov	al,#0x0a
+	mov	ah,bl
+	out	dx,ax
+	ret
+
+cirrus6_md:
+	.byte	0
+	.ascii	"Cirrus Logic 64XX"
+	.byte	0
+
+! Everex / Trident
+
+everex_test:
+	mov	ax,#0x7000
+	xor	bx,bx
+	int	0x10
+	cmp	al,#0x70
+	jne	noevrx
+	shr	dx,#4
+	cmp	dx,#0x678
+	je	evtrid
+	cmp	dx,#0x236
+	jne	evrxok
+evtrid:	lea	bp,trident_md
+evrxok:	ret
+
+noevrx:	xor	bp,bp
+	ret
+
+everex_md:
+	.byte	0x03, 0x22, 0x50
+	.byte	0x04, 0x3c, 0x50
+	.byte	0x07, 0x2b, 0x64
+	.byte	0x08, 0x4b, 0x64
+	.byte	0x0a, 0x19, 0x84
+	.byte	0x0b, 0x2c, 0x84
+	.byte	0x16, 0x1e, 0x50
+	.byte	0x18, 0x1b, 0x64
+	.byte	0x21, 0x40, 0xa0
+	.byte	0x40, 0x1e, 0x84
+	.byte	0
+	.ascii	"Everex/Trident"
+	.byte	0
+
+! Genoa.
+
+genoa_test:
+	lea	si,idgenoa		! Check Genoa 'clues'
+	xor 	ax,ax
+	seg es
+	mov	al,[0x37]
+	mov	di,ax
+	mov	cx,#0x04
+	dec	si
+	dec	di
+l1:	inc	si
+	inc	di
+	mov	al,(si)
+	test	al,al
+	jz	l2
+	seg es
+	cmp	al,(di)
+l2:	loope 	l1
+	or	cx,cx
+	je	isgen
+	xor	bp,bp
+isgen:	ret
+
+idgenoa: .byte	0x77, 0x00, 0x99, 0x66
+
+genoa_md:
+	.byte	0x58, 0x20, 0x50
+	.byte	0x5a, 0x2a, 0x64
+	.byte	0x60, 0x19, 0x84
+	.byte	0x61, 0x1d, 0x84
+	.byte	0x62, 0x20, 0x84
+	.byte	0x63, 0x2c, 0x84
+	.byte	0x64, 0x3c, 0x84
+	.byte	0x6b, 0x4f, 0x64
+	.byte	0x72, 0x3c, 0x50
+	.byte	0x74, 0x42, 0x50
+	.byte	0x78, 0x4b, 0x64
+	.byte	0
+	.ascii	"Genoa"
+	.byte	0
+
+! OAK
+
+oak_test:
+	lea	si,idoakvga
+	mov	di,#0x08
+	mov	cx,#0x08
+	repe
+	cmpsb
+	je	isoak
+	xor	bp,bp
+isoak:	ret
+
+idoakvga: .ascii  "OAK VGA "
+
+oak_md: .byte	0x4e, 0x3c, 0x50
+	.byte	0x4f, 0x3c, 0x84
+	.byte	0x50, 0x19, 0x84
+	.byte	0x51, 0x2b, 0x84
+	.byte	0
+	.ascii	"OAK"
+	.byte	0
+
+! WD Paradise.
+
+paradise_test:
+	lea	si,idparadise
+	mov	di,#0x7d
+	mov	cx,#0x04
+	repe
+	cmpsb
+	je	ispara
+	xor	bp,bp
+ispara:	ret
+
+idparadise:	.ascii	"VGA="
+
+paradise_md:
+	.byte	0x41, 0x22, 0x50
+	.byte	0x47, 0x1c, 0x84
+	.byte	0x55, 0x19, 0x84
+	.byte	0x54, 0x2c, 0x84
+	.byte	0
+	.ascii	"Paradise"
+	.byte	0
+
+! Trident.
+
+trident_test:
+	mov	dx,#0x3c4
+	mov	al,#0x0e
+	out	dx,al
+	inc	dx
+	in	al,dx
+	xchg	ah,al
+	xor	al,al
+	out	dx,al
+	in	al,dx
+	xchg	al,ah
+	mov	bl,al		! Strange thing ... in the book this wasn't
+	and	bl,#0x02	! necessary but it worked on my card which
+	jz	setb2		! is a trident. Without it the screen goes
+	and	al,#0xfd	! blurred ...
+	jmp	clrb2		!
+setb2:	or	al,#0x02	!
+clrb2:	out	dx,al
+	and	ah,#0x0f
+	cmp	ah,#0x02
+	je	istrid
+	xor	bp,bp
+istrid:	ret
+
+trident_md:
+	.byte	0x50, 0x1e, 0x50
+	.byte	0x51, 0x2b, 0x50
+	.byte	0x52, 0x3c, 0x50
+	.byte	0x57, 0x19, 0x84
+	.byte	0x58, 0x1e, 0x84
+	.byte	0x59, 0x2b, 0x84
+	.byte	0x5a, 0x3c, 0x84
+	.byte	0
+	.ascii	"Trident"
+	.byte	0
+
+! Tseng.
+
+tseng_test:
+	mov	dx,#0x3cd
+	in	al,dx			! Could things be this simple ! :-)
+	mov	bl,al
+	mov	al,#0x55
+	out	dx,al
+	in	al,dx
+	mov	ah,al
+	mov	al,bl
+	out	dx,al
+	cmp	ah,#0x55
+ 	je	istsen
+	xor	bp,bp
+istsen:	ret
+
+tseng_md:
+	.byte	0x26, 0x3c, 0x50
+	.byte	0x2a, 0x28, 0x64
+	.byte	0x23, 0x19, 0x84
+	.byte	0x24, 0x1c, 0x84
+	.byte	0x22, 0x2c, 0x84
+	.byte	0
+	.ascii	"Tseng"
+	.byte	0
+
+! Video7.
+
+video7_test:
+	mov	dx,#0x3cc
+	in	al,dx
+	mov	dx,#0x3b4
+	and	al,#0x01
+	jz	even7
+	mov	dx,#0x3d4
+even7:	mov	al,#0x0c
+	out	dx,al
+	inc	dx
+	in	al,dx
+	mov	bl,al
+	mov	al,#0x55
+	out	dx,al
+	in	al,dx
+	dec	dx
+	mov	al,#0x1f
+	out	dx,al
+	inc	dx
+	in	al,dx
+	mov	bh,al
+	dec	dx
+	mov	al,#0x0c
+	out	dx,al
+	inc	dx
+	mov	al,bl
+	out	dx,al
+	mov	al,#0x55
+	xor	al,#0xea
+	cmp	al,bh
+	je	isvid7
+	xor	bp,bp
+isvid7:	ret
+
+video7_md:
+	.byte	0x40, 0x2b, 0x50
+	.byte	0x43, 0x3c, 0x50
+	.byte	0x44, 0x3c, 0x64
+	.byte	0x41, 0x19, 0x84
+	.byte	0x42, 0x2c, 0x84
+	.byte	0x45, 0x1c, 0x84
+	.byte	0
+	.ascii	"Video 7"
+	.byte	0
+
+#endif	/* CONFIG_VIDEO_SVGA */
+
+!
+! User-defined local mode table (VGA only)
+!
+
+#ifdef CONFIG_VIDEO_LOCAL
+
+local_modes:
+	lea	si,local_mode_table
+locm1:	lodsw
+	or	ax,ax
+	jz	locm2
+	stosw
+	movsw
+	jmp	locm1
+locm2:	ret
+
+! This is the table of local video modes which can be supplied manually
+! by the user. Each entry consists of mode ID (word) and dimensions
+! (byte for column count and another byte for row count). These modes
+! are placed before all SVGA and VESA modes and override them if table
+! compacting is enabled. The table must end with a zero word followed
+! by NUL-terminated video adapter name.
+
+local_mode_table:
+	.word	0x0100		! Example: 40x25
+	.byte	25,40
+	.word	0
+	.ascii	"Local"
+	.byte	0
+
+#endif	/* CONFIG_VIDEO_LOCAL */
+
+!
+! Read a key and return the ASCII code in al, scan code in ah
+!
+
+getkey:	xor	ah,ah
+	int	0x16
+	ret
+
+!
+! Read a key with a timeout of 30 seconds. The hardware clock is used to get
+! the time.
+!
+
+getkt:	call	gettime
+	add	al,#30		! Wait 30 seconds
+	cmp	al,#60
+	jl	lminute
+	sub	al,#60
+lminute:
+	mov	cl,al
+again:	mov	ah,#0x01
+	int	0x16
+	jnz	getkey		! key pressed, so get it
+	call	gettime
+	cmp	al,cl
+	jne	again
+	mov	al,#0x20	! timeout, return default char `space'
+	ret
+
+!
+! Flush the keyboard buffer
+!
+
+flush:	mov	ah,#0x01
+	int	0x16
+	jz	empty
+	xor	ah,ah
+	int	0x16
+	jmp	flush
+empty:	ret
+
+!
+! Print hexadecimal number.
+!
+
+prthw:	push	ax
+	mov	al,ah
+	call	prthb
+	pop	ax
+prthb:	push	ax
+	shr	al,#4
+	call	prthn
+	pop	ax
+	and	al,#0x0f
+prthn:	cmp	al,#0x0a
+	jc	prth1
+	add	al,#0x07
+prth1:	add	al,#0x30
+	br	prtchr
+
+!
+! Print decimal number (AL).
+!
+
+prtdec:	push	ax
+	push	cx
+	xor	ah,ah		! Clear ah
+	mov	cl,#0x0a
+	idiv	cl
+	cmp	al,#0x09
+	jbe	lt100
+	call	prtdec
+	jmp	skip10
+lt100:	add	al,#0x30
+	call	prtchr
+skip10:	mov	al,ah
+	add	al,#0x30
+	call	prtchr	
+	pop	cx
+	pop	ax
+	ret
+
+! Variables:
+
+adapter:	.byte	0	! Video adapter: 0=CGA/MDA/HGA,1=EGA,2=VGA
+def_mode:	.byte	0	! "Default mode selected" flag
+mt_end:		.word	0	! End of video mode table if built
+edit_buf:	.space	6	! Line editor buffer
+card_name:	.word	0	! Pointer to adapter name
+scanning:	.byte	0	! Performing mode scan
+do_restore:	.byte	0	! Screen contents altered during mode change
+video_segment:	.word	0xb800	! Video memory segment
+
+! Messages:
+
+keymsg:		.ascii	"Press <RETURN> to see video modes available, "
+		.ascii	"<SPACE> to continue or wait 30 secs"
+		db	0x0d, 0x0a, 0
+listhdr:	db	0x0d, 0x0a
+		.ascii	"Mode:    COLSxROWS:"
+crlft:		db	0x0d, 0x0a, 0
+prompt:		db	0x0d, 0x0a
+		.ascii	"Enter mode number: "
+		db	0
+unknt:		.ascii	"Unknown mode ID. Try again."
+		db	0
+badmdt:		.ascii	"You passed an undefined mode number to setup."
+		db	0x0d, 0x0a, 0
+vesaer:		.ascii	"Error: Scanning of VESA modes failed. Please "
+		.ascii	"report to <mj@k332.feld.cvut.cz>."
+		db	0x0d, 0x0a, 0
+old_name:	.ascii	"CGA/MDA/HGA"
+		db	0
+ega_name:	.ascii	"EGA"
+		db	0
+svga_name:	.ascii	" "
+vga_name:	.ascii	"VGA"
+		db	0
+vesa_name:	.ascii	"VESA"
+		db	0
+name_bann:	.ascii	"Video adapter: "
+		db	0
diff -u --recursive --new-file v1.3.74/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v1.3.74/linux/arch/i386/defconfig	Sun Mar 10 09:49:46 1996
+++ linux/arch/i386/defconfig	Sat Mar 16 13:52:13 1996
@@ -38,6 +38,7 @@
 # CONFIG_BLK_DEV_HD_IDE is not set
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDE_PCMCIA is not set
 CONFIG_BLK_DEV_CMD640=y
 CONFIG_BLK_DEV_RZ1000=y
 # CONFIG_BLK_DEV_TRITON is not set
diff -u --recursive --new-file v1.3.74/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v1.3.74/linux/arch/i386/kernel/irq.c	Wed Mar 13 10:09:11 1996
+++ linux/arch/i386/kernel/irq.c	Sat Mar 16 13:52:10 1996
@@ -182,7 +182,7 @@
 /*
  * On SMP boards, irq13 is used for interprocessor interrupts (IPI's).
  */
-static struct irqaction irq13 = { smp_message_irq, SA_INTERRUPT, 0, "IPI", NULL, NULL }:
+static struct irqaction irq13 = { smp_message_irq, SA_INTERRUPT, 0, "IPI", NULL, NULL };
 
 #else
 
diff -u --recursive --new-file v1.3.74/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v1.3.74/linux/arch/i386/kernel/process.c	Wed Mar 13 10:09:11 1996
+++ linux/arch/i386/kernel/process.c	Sat Mar 16 13:52:10 1996
@@ -154,7 +154,14 @@
                 while(0x80000000 & smp_process_available);
 	        cli();
                 while(set_bit(31,&smp_process_available))
-                	while(test_bit(31,&smp_process_available));
+                	while(test_bit(31,&smp_process_available))
+                {
+                	/*
+                	 *	Oops.. This is kind of important in some cases...
+                	 */
+                	if(clear_bit(smp_processor_id(), &smp_invalidate_needed))
+                		local_invalidate();
+                }
                 if (0==(0x7fffffff & smp_process_available)){
                         clear_bit(31,&smp_process_available);
                         sti();
diff -u --recursive --new-file v1.3.74/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v1.3.74/linux/arch/i386/kernel/smp.c	Wed Mar 13 10:09:11 1996
+++ linux/arch/i386/kernel/smp.c	Sat Mar 16 13:52:10 1996
@@ -36,6 +36,18 @@
 #include <asm/pgtable.h>
 #include <asm/smp.h>
 
+/*
+ *	Why isnt this somewhere standard ??
+ */
+ 
+extern __inline int max(int a,int b)
+{
+	if(a>b)
+		return a;
+	return b;
+}
+
+
 int smp_found_config=0;					/* Have we found an SMP box 				*/
 
 unsigned long cpu_present_map = 0;			/* Bitmask of existing CPU's 				*/
@@ -46,7 +58,8 @@
 volatile unsigned long smp_invalidate_needed;		/* Used for the invalidate map thats also checked in the spinlock */
 struct cpuinfo_x86 cpu_data[NR_CPUS];			/* Per cpu bogomips and other parameters 		*/
 static unsigned int num_processors = 1;			/* Internal processor count				*/
-static unsigned long io_apic_addr = 0;			/* Address of the I/O apic (not yet used) 		*/
+int smp_top_cpu = 0;					/* Highest used APIC id 				*/
+static unsigned long io_apic_addr = 0xFEC00000;		/* Address of the I/O apic (not yet used) 		*/
 unsigned char boot_cpu_id = 0;				/* Processor that is doing the boot up 			*/
 static unsigned char *kstack_base,*kstack_end;		/* Kernel stack list pointers 				*/
 static int smp_activated = 0;				/* Tripped once we need to start cross invalidating 	*/
@@ -319,25 +332,19 @@
 				switch(mpf->mpf_feature1)
 				{
 					case 1:
-						printk("ISA");
+					case 5:
+						printk("ISA\n");
 						break;
 					case 2:
-						printk("EISA with no IRQ8 chaining");
+						printk("EISA with no IRQ8 chaining\n");
 						break;
+					case 6:
 					case 3:
-						printk("EISA");
+						printk("EISA\n");
 						break;
 					case 4:
-						printk("MCA");
-						break;
-					case 5:
-						printk("ISA\nBus#1 is PCI");
-						break;
-					case 6:
-						printk("EISA\nBus #1 is PCI");
-						break;
 					case 7:
-						printk("MCA\nBus #1 is PCI");
+						printk("MCA\n");
 						break;
 					case 0:
 						break;
@@ -346,14 +353,63 @@
 							mpf->mpf_feature1);
 						return;
 				}
+				if(mpf->mpf_feature1>4)
+					printk("Bus #1 is PCI\n");
 				/*
-				 *	Read the physical hardware table. If there isn't one
-				 *	the processors present are 0 and 1.
+				 *	Read the physical hardware table.
 				 */
 				if(mpf->mpf_physptr)
 					smp_read_mpc((void *)mpf->mpf_physptr);
 				else
-					cpu_present_map=3;
+				{
+					unsigned long cfg;
+
+					/*
+					 *	If no table present, determine
+					 *	what the CPU mapping is.
+					 */
+
+/*
+ *
+ *	HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
+ *
+ */
+					/*
+					 *	Standard page mapping
+					 *	functions don't work yet.
+					 *	We know that page 0 is not
+					 *	used.  Steal it for now!
+					 */
+			 
+					cfg=pg0[0];
+					pg0[0] = (apic_addr | 7);
+					local_invalidate();
+
+					boot_cpu_id = GET_APIC_ID(*((volatile unsigned long *) APIC_ID));
+					nlong = boot_cpu_id<<24;	/* Dummy 'self' for bootup */
+
+					/*
+					 *	Give it back
+					 */
+
+					pg0[0]= cfg;
+					local_invalidate();
+
+/*
+ *
+ *	END OF HACK   END OF HACK   END OF HACK   END OF HACK   END OF HACK
+ *
+ */					
+
+					/*
+					 *	If boot CPU != 0, other CPU
+					 *	is 0, else other CPU is 1.
+					 */
+					if (boot_cpu_id)
+						cpu_present_map=1 | (1 << boot_cpu_id);
+					else
+						cpu_present_map=3;
+				}
 				printk("Processors: %d\n", num_processors);
 				/*
 				 *	Only use the first one found.
@@ -610,8 +666,11 @@
 		 *	Don't even attempt to start the boot CPU!
 		 */
 		if (i == boot_cpu_id)
+		{
+			smp_top_cpu=max(smp_top_cpu,i);
 			continue;
-
+		}
+		
 		if (cpu_present_map & (1 << i))
 		{
 			unsigned long send_status, accept_status;
@@ -766,6 +825,8 @@
 					cpucount++;
 					/* number CPUs logically, starting from 1 (BSP is 0) */
 					cpu_number_map[i] = cpucount;
+					smp_top_cpu=max(smp_top_cpu,i);
+					
 				}
 				else
 				{
@@ -1098,7 +1159,7 @@
 			if(clear_bit(i,(unsigned long *)&smp_invalidate_needed))
 				local_invalidate();
 			set_bit(i, (unsigned long *)&cpu_callin_map[0]);
-			cpu_callin_map[0]|=1<<smp_processor_id();
+		/*	cpu_callin_map[0]|=1<<smp_processor_id();*/
 			break;
 			
 		/*
diff -u --recursive --new-file v1.3.74/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v1.3.74/linux/arch/i386/mm/init.c	Fri Mar 15 16:03:10 1996
+++ linux/arch/i386/mm/init.c	Fri Mar 15 07:45:42 1996
@@ -24,6 +24,7 @@
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <asm/pgtable.h>
+#include <asm/dma.h>
 
 /*
  * The SMP kernel can't handle the 4MB page table optimizations yet
@@ -236,7 +237,7 @@
 		start_mem += PAGE_SIZE;
 	}
 	for (tmp = 0 ; tmp < high_memory ; tmp += PAGE_SIZE) {
-		if (tmp >= 16*1024*1024)
+		if (tmp >= MAX_DMA_ADDRESS)
 			mem_map[MAP_NR(tmp)].dma = 0;
 		if (mem_map[MAP_NR(tmp)].reserved) {
 			if (tmp >= 0xA0000 && tmp < 0x100000)
diff -u --recursive --new-file v1.3.74/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v1.3.74/linux/drivers/block/Config.in	Wed Mar 13 10:09:12 1996
+++ linux/drivers/block/Config.in	Sat Mar 16 13:52:13 1996
@@ -13,6 +13,7 @@
   bool '   Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE
   bool '   Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD
   bool '   Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE
+  bool '   Support removeable IDE interfaces (PCMCIA)' CONFIG_BLK_DEV_IDE_PCMCIA
   bool '   CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640
   if [ "$CONFIG_PCI" = "y" ]; then
     bool '   RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000
@@ -26,6 +27,7 @@
     bool '      QDI QD6580      support' CONFIG_BLK_DEV_QD6580
     bool '      UMC 8672        support' CONFIG_BLK_DEV_UMC8672
     bool '      ALI M1439/M1445 support' CONFIG_BLK_DEV_ALI14XX
+    bool '      PROMISE DC4030  support (ALPHA)' CONFIG_BLK_DEV_PROMISE
   fi
 fi
 
diff -u --recursive --new-file v1.3.74/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v1.3.74/linux/drivers/block/Makefile	Wed Feb 28 11:50:00 1996
+++ linux/drivers/block/Makefile	Sat Mar 16 13:52:13 1996
@@ -85,6 +85,10 @@
 L_OBJS += ali14xx.o
 endif
 
+ifeq ($(CONFIG_BLK_DEV_PROMISE),y)
+L_OBJS += promise.o
+endif
+
 ifeq ($(CONFIG_BLK_DEV_IDECD),y)
 L_OBJS += ide-cd.o
 endif
diff -u --recursive --new-file v1.3.74/linux/drivers/block/README.ide linux/drivers/block/README.ide
--- v1.3.74/linux/drivers/block/README.ide	Sat Mar  2 10:43:21 1996
+++ linux/drivers/block/README.ide	Sat Mar 16 13:52:13 1996
@@ -1,4 +1,4 @@
-README.ide -- Information regarding ide.c and ide-cd.c (IDE driver in 1.3.xx)
+README.ide -- Information regarding ide.c and ide-cd.c (IDE driver in 1.3/2.0)
 ================================================================================
 Supported by:  mlord@pobox.com        -- disks, interfaces, probing
                snyder@fnald0.fnal.gov -- cdroms, ATAPI, audio
@@ -10,7 +10,7 @@
 
 See description later on below for handling BIG IDE drives with >1024 cyls.
 
-Major features of ide.c & ide-cd.c:
+Major features of ide.c & ide-cd.c ("NEW!" marks changes since 1.2.13):
 
 NEW!	- support for IDE ATAPI *tape* drives, courtesy of Gadi Oxman
 		(re-run MAKEDEV.ide to create the tape device entries in /dev/)
@@ -58,6 +58,8 @@
 NEW!    - ide-cd.c now supports door locking and auto-loading.
 		- Also preliminary support for multisession
 		  and direct reads of audio data.
+NEW!	- experimental support for Promise DC4030VL caching interface card
+NEW!		- email thanks/problems to: peterd@pnd-pc.demon.co.uk
 NEW!	- the hdparm-2.7 package can be used to set PIO modes for some chipsets.
 
 For work in progress, see the comments in ide.c, ide-cd.c, and triton.c.
@@ -233,11 +235,11 @@
  "idex=noautotune"	: driver will NOT attempt to tune interface speed
 				This is the default for most chipsets,
 				except the cmd640.
+ "idex=serialize"	: do not overlap operations on idex and ide(x^1)
 
  The following two are valid ONLY on ide0,
  and the defaults for the base,ctl ports must not be altered.
 
- "ide0=serialize"	: do not overlap operations on ide0 and ide1.
  "ide0=dtc2278"		: probe/support DTC2278 interface
  "ide0=ht6560b"		: probe/support HT6560B interface
  "ide0=cmd640_vlb"	: *REQUIRED* for VLB cards with the CMD640 chip
diff -u --recursive --new-file v1.3.74/linux/drivers/block/cmd640.c linux/drivers/block/cmd640.c
--- v1.3.74/linux/drivers/block/cmd640.c	Sat Mar  2 10:43:22 1996
+++ linux/drivers/block/cmd640.c	Sat Mar 16 13:52:13 1996
@@ -1,7 +1,7 @@
 /*
- *  linux/drivers/block/cmd640.c	Version 0.07  Jan 27, 1996
+ *  linux/drivers/block/cmd640.c	Version 0.08  Mar 15, 1996
  *
- *  Copyright (C) 1995-1996  Linus Torvalds & author (see below)
+ *  Copyright (C) 1995-1996  Linus Torvalds & authors (see below)
  */
 
 /*
@@ -41,6 +41,8 @@
  *			CMD at their ftp site.
  *
  *  Version 0.08	Added autotune/noautotune support.  -ml
+ *
+ *  Version 0.09	Try to be smarter about 2nd port enabling.  -ml
  *			
  */
 
@@ -300,12 +302,33 @@
 }
 
 /*
+ *  Returns 1 if an IDE interface/drive exists at 0x170,
+ *  Returns 0 otherwise.
+ */
+int secondary_port_responding (void)
+{
+	/*
+	 * Test for hardware at 0x170 (secondary IDE port).
+	 * Leave the enable-bit alone if something responds.
+	 */
+	outb_p(0x0a,0x176);		/* select drive0 */
+	udelay(1);
+	if (inb_p(0x176) == 0xff) {
+		outb_p(0x0b,0x176);	/* select drive1 */
+		udelay(1);
+		if (inb_p(0x176) == 0xff)
+			return 0;	/* nothing is there */
+	}
+	return 1;			/* something is there */
+}
+
+/*
  * Probe for Cmd640x and initialize it if found
  */
 
 int ide_probe_for_cmd640x(void)
 {
-	int  second_port;
+	int  second_port_toggled = 0;
         byte b;
 
 	if (probe_for_cmd640_pci1()) {
@@ -320,6 +343,7 @@
 	}
 
 	ide_hwifs[0].serialized = 1;	/* ensure this *always* gets set */
+	ide_hwifs[1].serialized = 1;	/* ensure this *always* gets set */
 	
 #if 0	
 	/* Dump initial state of chip registers */
@@ -359,11 +383,6 @@
 	put_cmd640_reg(ARTTIM23, 0xcc); /* 0xc0? */
 
 	/*
-	 * Do not initialize secondary controller for vlbus
-	 */
-	second_port = (bus_type != vlb);
-
-	/*
 	 * Set the maximum allowed bus speed (it is safest until we
 	 * 				      find how to detect bus speed)
 	 * Normally PCI bus runs at 33MHz, but often works overclocked to 40
@@ -375,10 +394,11 @@
 	 */
 	b = get_cmd640_reg(CNTRL);	
 
-	if (second_port)
-		b |= CNTRL_ENA_2ND;
-	else
-		b &= ~CNTRL_ENA_2ND;
+
+	if (!secondary_port_responding()) {
+		b ^= CNTRL_ENA_2ND;	/* toggle the bit */
+		second_port_toggled = 1;
+	}
 
 	/*
 	 * Disable readahead for drives at primary interface
@@ -401,7 +421,7 @@
 	/*
 	 * Initialize 2nd IDE port, if required
 	 */
-	if (second_port) {
+	if (secondary_port_responding()) {
 		ide_hwifs[1].chipset = ide_cmd640;
 		ide_hwifs[1].tuneproc = &cmd640_tune_drive;
 		if (ide_hwifs[1].drives[0].autotune == 0)
@@ -411,7 +431,6 @@
 		/* We reset timings, and disable read-ahead */
 		put_cmd640_reg(ARTTIM23, (DIS_RA2 | DIS_RA3));
 		put_cmd640_reg(DRWTIM23, 0);
-
 		cmd640_reset_controller(1);
 	}
 
@@ -434,9 +453,10 @@
 	 */
 	put_cmd640_reg(CMDTIM, 0);
 
-	printk("\n ... serialized, secondary interface %s\n",
-	       second_port ? "enabled" : "disabled");
-
+	/*
+	 * Tell everyone what we did to their system
+	 */
+	printk("\n ... serialized, secondary port %s\n", second_port_toggled ? "toggled" : "untouched");
 	return 1;
 }
 
diff -u --recursive --new-file v1.3.74/linux/drivers/block/dtc2278.c linux/drivers/block/dtc2278.c
--- v1.3.74/linux/drivers/block/dtc2278.c	Sat Mar  2 10:43:22 1996
+++ linux/drivers/block/dtc2278.c	Sat Mar 16 13:52:13 1996
@@ -117,6 +117,7 @@
 	restore_flags(flags);
 
 	ide_hwifs[0].serialized = 1;
+	ide_hwifs[1].serialized = 1;
 	ide_hwifs[0].chipset = ide_dtc2278;
 	ide_hwifs[1].chipset = ide_dtc2278;
 	ide_hwifs[0].tuneproc = &tune_dtc2278;
diff -u --recursive --new-file v1.3.74/linux/drivers/block/ht6560b.c linux/drivers/block/ht6560b.c
--- v1.3.74/linux/drivers/block/ht6560b.c	Wed Feb 14 14:37:07 1996
+++ linux/drivers/block/ht6560b.c	Sat Mar 16 13:52:13 1996
@@ -152,6 +152,7 @@
                  */
                 outb (timing, IDE_SELECT_REG);
                 (void) inb (IDE_STATUS_REG);
+		OUT_BYTE(drive->select.all,IDE_SELECT_REG);
 		restore_flags (flags);
 	}
 }
@@ -226,6 +227,7 @@
 			ide_hwifs[0].tuneproc = &tune_ht6560b;
 			ide_hwifs[1].tuneproc = &tune_ht6560b;
 			ide_hwifs[0].serialized = 1;
+			ide_hwifs[1].serialized = 1;
 		} else
 			printk("ht6560b: not found\n");
 	}
diff -u --recursive --new-file v1.3.74/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v1.3.74/linux/drivers/block/ide-cd.c	Wed Mar 13 10:09:13 1996
+++ linux/drivers/block/ide-cd.c	Sat Mar 16 13:52:13 1996
@@ -1443,44 +1443,6 @@
     }
 }
 
-
-
-/****************************************************************************
- * drive_cmd handling.
- *
- * Most of the functions accessed via drive_cmd are not valid for ATAPI
- * devices.  Only attempt to execute those which actually should be valid.
- */
-
-static
-void cdrom_do_drive_cmd (ide_drive_t *drive)
-{
-  struct request *rq = HWGROUP(drive)->rq;
-  byte *args = rq->buffer;
-
-  if (args)
-    {
-#if 0  /* This bit isn't done yet... */
-      if (args[0] == WIN_SETFEATURES &&
-	  (args[2] == 0x66 || args[2] == 0xcc || args[2] == 0x02 ||
-	   args[2] == 0xdd || args[2] == 0x5d))
-	{
-	  OUT_BYTE (args[2], io_base + IDE_FEATURE_OFFSET);
-	  <send cmd>
-	}
-      else
-#endif
-	{
-	  printk ("%s: Unsupported drive command %02x %02x %02x\n",
-		  drive->name, args[0], args[1], args[2]);
-	  rq->errors = 1;
-	}
-    }
-
-  cdrom_end_request (1, drive);
-}
-
-
 
 /****************************************************************************
  * cdrom driver request routine.
@@ -1499,9 +1461,6 @@
       ide_do_reset (drive);
       return;
     }
-
-  else if (rq -> cmd == IDE_DRIVE_CMD)
-    cdrom_do_drive_cmd (drive);
 
   else if (rq -> cmd != READ)
     {
diff -u --recursive --new-file v1.3.74/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v1.3.74/linux/drivers/block/ide-tape.c	Wed Feb 14 14:37:08 1996
+++ linux/drivers/block/ide-tape.c	Sat Mar 16 13:52:14 1996
@@ -163,7 +163,7 @@
  *                       The recommended user block size is returned by
  *                        the MTIOCGET ioctl.
  *                       Additional minor changes.
- * Ver 1.3   Feb  9  96  Fixed pipelined read mode bug which prevented the
+ * Ver 1.3   Feb  9 96   Fixed pipelined read mode bug which prevented the
  *                        use of some block sizes during a restore procedure.
  *                       The character device interface will now present a
  *                        continuous view of the media - any mix of block sizes
@@ -180,6 +180,7 @@
  *                        and can be enabled by using hdparm -d1 on the tape's
  *                        block device interface. For more info, read the
  *                        comments in triton.c.
+ * Ver 1.4   Mar 13 96   Fixed serialize support.
  *
  * We are currently in an *alpha* stage. The driver is not complete and not
  * much tested. I would strongly suggest to:
@@ -1825,6 +1826,7 @@
 	unsigned int major = HWIF(drive)->major;
 	idetape_tape_t *tape=&(drive->tape);
 	struct blk_dev_struct *bdev = &blk_dev[major];
+	struct request *next_rq;
 	unsigned long flags;
 	idetape_status_reg_t status;
 
@@ -1839,7 +1841,7 @@
 	 *	we can safely access the tape.
 	 */
 
-	if (bdev->current_request == NULL) {
+	if (HWGROUP (drive)->rq == NULL) {
 		sti ();
 		idetape_poll_for_dsc_direct (data);
 		return;
@@ -1928,8 +1930,13 @@
 	/*
 	 *	Fallback to method 1.
 	 */
-	 	
-	if (bdev->current_request->next == NULL) {
+
+	next_rq=bdev->current_request;
+	if (next_rq == HWGROUP (drive)->rq)
+		next_rq=next_rq->next;
+
+	if (next_rq == NULL) {
+
 		/*
 		 *	There will not be another request after the currently
 		 *	ongoing request, so ide.c won't be able to sample
@@ -3383,10 +3390,12 @@
 	if (tape->active_data_request == NULL)
 		idetape_insert_pipeline_into_queue (drive);		
 
-	if (tape->active_data_request == NULL)
-		return;
-
 	save_flags (flags);cli ();
+	if (tape->active_data_request == NULL) {
+		restore_flags (flags);
+		return;
+	}
+	
 	if (tape->last_stage != NULL)
 		idetape_wait_for_request (&(tape->last_stage->rq));
 
diff -u --recursive --new-file v1.3.74/linux/drivers/block/ide-tape.h linux/drivers/block/ide-tape.h
--- v1.3.74/linux/drivers/block/ide-tape.h	Wed Feb 14 14:37:08 1996
+++ linux/drivers/block/ide-tape.h	Sat Mar 16 13:52:14 1996
@@ -511,7 +511,7 @@
 #define POLL_HWIF_TAPE_DRIVE							\
 	if (hwif->tape_drive != NULL) {						\
 		if (hwif->tape_drive->tape.request_status) {			\
-			OUT_BYTE(hwif->tape_drive->select.all,IDE_SELECT_REG);	\
+			SELECT_DRIVE(hwif,hwif->tape_drive);			\
 			hwif->tape_drive->tape.last_status=GET_STAT();		\
 			hwif->tape_drive->tape.request_status=0;		\
 		}								\
diff -u --recursive --new-file v1.3.74/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v1.3.74/linux/drivers/block/ide.c	Fri Mar 15 16:03:11 1996
+++ linux/drivers/block/ide.c	Sat Mar 16 13:52:15 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 5.30  Feb 27, 1996
+ *  linux/drivers/block/ide.c	Version 5.33  Mar 15, 1996
  *
  *  Copyright (C) 1994-1996  Linus Torvalds & authors (see below)
  */
@@ -208,11 +208,21 @@
  * Version 5.29		fixed non-IDE check for too many physical heads
  *			don't use LBA if capacity is smaller than CHS
  * Version 5.30		remove real_devices kludge, formerly used by genhd.c
+ * Version 5.32		change "KB" to "kB"
+ *			fix serialize (was broken in kernel 1.3.72)
+ *			add support for "hdparm -I"
+ *			use common code for disk/tape/cdrom IDE_DRIVE_CMDs
+ *			add support for Promise DC4030VL caching card
+ *			improved serialize support
+ *			put partition check back into alphabetical order
+ *			add config option for PCMCIA baggage
+ *			try to make PCMCIA support safer to use
+ *			improve security on ioctls(): all are suser() only
+ * Version 5.33			improve handling of HDIO_DRIVE_CMDs that read data
  *
  *  Some additional driver compile-time options are in ide.h
  *
  *  To do, in likely order of completion:
- *	- add Promise DC4030VL support from peterd@pnd-pc.demon.co.uk
  *	- modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f
 */
 
@@ -247,7 +257,13 @@
 #include "ide.h"
 #include "ide_modes.h"
 
-static ide_hwgroup_t	*irq_to_hwgroup [NR_IRQS];
+#ifdef CONFIG_BLK_DEV_PROMISE
+#include "promise.h"
+#define IS_PROMISE_DRIVE (HWIF(drive)->chipset == ide_promise)
+#else
+#define IS_PROMISE_DRIVE (0)	/* auto-NULLs out Promise code */
+#endif /* CONFIG_BLK_DEV_PROMISE */
+
 static const byte	ide_hwif_to_major[MAX_HWIFS] = {IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR};
 
 static const unsigned short default_io_base[MAX_HWIFS] = {0x1f0, 0x170, 0x1e8, 0x168};
@@ -285,6 +301,55 @@
 
 #endif /* DISK_RECOVERY_TIME */
 
+
+/*
+ * Do not even *think* about calling this!
+ */
+static void init_hwif_data (unsigned int index)
+{
+	byte *p;
+	unsigned int unit;
+	ide_hwif_t *hwif = &ide_hwifs[index];
+
+	/* bulk initialize hwif & drive info with zeros */
+	p = ((byte *) hwif) + sizeof(ide_hwif_t);
+	do {
+		*--p = 0;
+	} while (p > (byte *) hwif);
+
+	/* fill in any non-zero initial values */
+	hwif->index     = index;
+	hwif->noprobe	= (index > 1);
+	hwif->io_base	= default_io_base[index];
+	hwif->ctl_port	= hwif->io_base ? hwif->io_base+0x206 : 0x000;
+#ifdef CONFIG_BLK_DEV_HD
+	if (hwif->io_base == HD_DATA)
+		hwif->noprobe = 1; /* may be overriden by ide_setup() */
+#endif /* CONFIG_BLK_DEV_HD */
+	hwif->major	= ide_hwif_to_major[index];
+	hwif->name[0]	= 'i';
+	hwif->name[1]	= 'd';
+	hwif->name[2]	= 'e';
+	hwif->name[3]	= '0' + index;
+#ifdef CONFIG_BLK_DEV_IDETAPE
+	hwif->tape_drive = NULL;
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+	for (unit = 0; unit < MAX_DRIVES; ++unit) {
+		ide_drive_t *drive = &hwif->drives[unit];
+
+		drive->select.all		= (unit<<4)|0xa0;
+		drive->hwif			= hwif;
+		drive->ctl			= 0x08;
+		drive->ready_stat		= READY_STAT;
+		drive->bad_wstat		= BAD_W_STAT;
+		drive->special.b.recalibrate	= 1;
+		drive->special.b.set_geometry	= 1;
+		drive->name[0]			= 'h';
+		drive->name[1]			= 'd';
+		drive->name[2]			= 'a' + (index * MAX_DRIVES) + unit;
+	}
+}
+
 /*
  * init_ide_data() sets reasonable default values into all fields
  * of all instances of the hwifs and drives, but only on the first call.
@@ -302,58 +367,15 @@
 #define MAGIC_COOKIE 0x12345678
 static void init_ide_data (void)
 {
-	byte *p;
-	unsigned int h, unit;
+	unsigned int index;
 	static unsigned long magic_cookie = MAGIC_COOKIE;
 
 	if (magic_cookie != MAGIC_COOKIE)
 		return;		/* already initialized */
 	magic_cookie = 0;
 
-	for (h = 0; h < NR_IRQS; ++h)
-		 irq_to_hwgroup[h] = NULL;
-
-	/* bulk initialize hwif & drive info with zeros */
-	p = ((byte *) ide_hwifs) + sizeof(ide_hwifs);
-	do {
-		*--p = 0;
-	} while (p > (byte *) ide_hwifs);
-
-	/* fill in any non-zero initial values */
-	for (h = 0; h < MAX_HWIFS; ++h) {
-		ide_hwif_t *hwif = &ide_hwifs[h];
-
-		hwif->index     = h;
-		hwif->noprobe	= (h > 1);
-		hwif->io_base	= default_io_base[h];
-		hwif->ctl_port	= hwif->io_base ? hwif->io_base+0x206 : 0x000;
-#ifdef CONFIG_BLK_DEV_HD
-		if (hwif->io_base == HD_DATA)
-			hwif->noprobe = 1; /* may be overriden by ide_setup() */
-#endif /* CONFIG_BLK_DEV_HD */
-		hwif->major	= ide_hwif_to_major[h];
-		hwif->name[0]	= 'i';
-		hwif->name[1]	= 'd';
-		hwif->name[2]	= 'e';
-		hwif->name[3]	= '0' + h;
-#ifdef CONFIG_BLK_DEV_IDETAPE
-		hwif->tape_drive = NULL;
-#endif /* CONFIG_BLK_DEV_IDETAPE */
-		for (unit = 0; unit < MAX_DRIVES; ++unit) {
-			ide_drive_t *drive = &hwif->drives[unit];
-
-			drive->select.all		= (unit<<4)|0xa0;
-			drive->hwif			= hwif;
-			drive->ctl			= 0x08;
-			drive->ready_stat		= READY_STAT;
-			drive->bad_wstat		= BAD_W_STAT;
-			drive->special.b.recalibrate	= 1;
-			drive->special.b.set_geometry	= 1;
-			drive->name[0]			= 'h';
-			drive->name[1]			= 'd';
-			drive->name[2]			= 'a' + (h * MAX_DRIVES) + unit;
-		}
-	}
+	for (index = 0; index < MAX_HWIFS; ++index)
+		init_hwif_data(index);
 }
 
 #if SUPPORT_VLB_SYNC
@@ -479,12 +501,14 @@
 		return 0;
 	if (drive->media != ide_disk)
 		return 0x7fffffff;	/* cdrom or tape */
-	drive->select.b.lba = 0;
-	/* Determine capacity, and use LBA if the drive properly supports it */
-	if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) {
-		if (id->lba_capacity >= capacity) {
-			capacity = id->lba_capacity;
-			drive->select.b.lba = 1;
+	if (!IS_PROMISE_DRIVE) {
+		drive->select.b.lba = 0;
+		/* Determine capacity, and use LBA if the drive properly supports it */
+		if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) {
+			if (id->lba_capacity >= capacity) {
+				capacity = id->lba_capacity;
+				drive->select.b.lba = 1;
+			}
 		}
 	}
 	return (capacity - drive->sect0);
@@ -524,7 +548,7 @@
  */
 static void init_gendisk (ide_hwif_t *hwif)
 {
-	struct gendisk *gd;
+	struct gendisk *gd, **gdp;
 	unsigned int unit, units, minors;
 	int *bs;
 
@@ -555,9 +579,10 @@
 	gd->nr_real	= units;		/* current num real drives */
 	gd->init	= ide_geninit;		/* initialization function */
 	gd->real_devices= hwif;			/* ptr to internal data */
+	gd->next	= NULL;			/* linked list of major devs */
 
-	gd->next = gendisk_head;		/* link new major into list */
-	hwif->gd = gendisk_head = gd;
+	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) ;
+	hwif->gd = *gdp = gd;			/* link onto tail of list */
 }
 
 static void do_reset1 (ide_drive_t *, int);		/* needed below */
@@ -695,9 +720,12 @@
 		if (OK_TO_RESET_CONTROLLER)
 			rdrive->mult_count = 0;
 		if (!rdrive->keep_settings) {
-			rdrive->using_dma = 0;
 			rdrive->mult_req = 0;
 			rdrive->unmask = 0;
+			if (rdrive->using_dma) {
+				rdrive->using_dma = 0;
+				printk("%s: disabled DMA\n", rdrive->name);
+			}
 		}
 		if (rdrive->mult_req != rdrive->mult_count)
 			rdrive->special.b.set_multmode = 1;
@@ -860,8 +888,7 @@
 	if ((rq = HWGROUP(drive)->rq) == NULL || drive == NULL)
 		return;
 	/* retry only "normal" I/O: */
-	if (rq->cmd == IDE_DRIVE_CMD || (rq->cmd != READ && rq->cmd != WRITE && drive->media == ide_disk))
-	{
+	if (rq->cmd == IDE_DRIVE_CMD) {
 		rq->errors = 1;
 		ide_end_drive_cmd(drive, stat, err);
 		return;
@@ -980,13 +1007,12 @@
 }
 
 /*
- * multwrite() transfers a block of one or more sectors of data to a drive
- * as part of a disk multwrite operation.
+ * ide_multwrite() transfers a block of up to mcount sectors of data
+ * to a drive as part of a disk multiple-sector write operation.
  */
-static void multwrite (ide_drive_t *drive)
+void ide_multwrite (ide_drive_t *drive, unsigned int mcount)
 {
 	struct request *rq = &HWGROUP(drive)->wrq;
-	unsigned int mcount = drive->mult_count;
 
 	do {
 		unsigned int nsect = rq->current_nr_sectors;
@@ -1029,7 +1055,7 @@
 	if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
 		if (stat & DRQ_STAT) {
 			if (rq->nr_sectors) {
-				multwrite(drive);
+				ide_multwrite(drive, drive->mult_count);
 				ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
 				return;
 			}
@@ -1105,9 +1131,15 @@
  */
 static void drive_cmd_intr (ide_drive_t *drive)
 {
+	struct request *rq = HWGROUP(drive)->rq;
+	byte *args = (byte *) rq->buffer;
 	byte stat = GET_STAT();
 
 	sti();
+	if ((stat & DRQ_STAT) && args && args[3]) {
+		ide_input_data(drive, &args[4], args[3] * SECTOR_WORDS);
+		stat = GET_STAT();
+	}
 	if (OK_STAT(stat,READY_STAT,BAD_STAT))
 		ide_end_drive_cmd (drive, stat, GET_ERR());
 	else
@@ -1132,13 +1164,13 @@
 			OUT_BYTE(drive->cyl,IDE_LCYL_REG);
 			OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
 			OUT_BYTE(((drive->head-1)|drive->select.all)&0xBF,IDE_SELECT_REG);
-			ide_cmd(drive, WIN_SPECIFY, drive->sect, &set_geometry_intr);
+			if (!IS_PROMISE_DRIVE)
+				ide_cmd(drive, WIN_SPECIFY, drive->sect, &set_geometry_intr);
 		}
 	} else if (s->b.recalibrate) {
 		s->b.recalibrate = 0;
-		if (drive->media == ide_disk) {
+		if (drive->media == ide_disk && !IS_PROMISE_DRIVE)
 			ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr);
-		}
 	} else if (s->b.set_pio) {
 		ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
 		s->b.set_pio = 0;
@@ -1150,7 +1182,8 @@
 		if (drive->media == ide_disk) {
 			if (drive->id && drive->mult_req > drive->id->max_multsect)
 				drive->mult_req = drive->id->max_multsect;
-			ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
+			if (!IS_PROMISE_DRIVE)
+				ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
 		} else
 			drive->mult_req = 0;
 	} else if (s->all) {
@@ -1201,13 +1234,14 @@
 }
 
 /*
- * do_rw_disk() issues WIN_{MULT}READ and WIN_{MULT}WRITE commands to a disk,
- * using LBA if supported, or CHS otherwise, to address sectors.  It also takes
- * care of issuing special DRIVE_CMDs.
+ * do_rw_disk() issues READ and WRITE commands to a disk,
+ * using LBA if supported, or CHS otherwise, to address sectors.
+ * It also takes care of issuing special DRIVE_CMDs.
  */
 static inline void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
 {
-	unsigned short io_base = HWIF(drive)->io_base;
+	ide_hwif_t *hwif = HWIF(drive);
+	unsigned short io_base = hwif->io_base;
 
 	OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
 	OUT_BYTE(rq->nr_sectors,io_base+IDE_NSECTOR_OFFSET);
@@ -1237,6 +1271,14 @@
 			head, sect, rq->nr_sectors, (unsigned long) rq->buffer);
 #endif
 	}
+#ifdef CONFIG_BLK_DEV_PROMISE
+	if (IS_PROMISE_DRIVE) {
+		if (hwif->is_promise2 || rq->cmd == READ) {
+			do_promise_io (drive, rq);
+			return;
+		}
+	}
+#endif /* CONFIG_BLK_DEV_PROMISE */
 	if (rq->cmd == READ) {
 #ifdef CONFIG_BLK_DEV_TRITON
 		if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive)))
@@ -1262,37 +1304,43 @@
 		if (drive->mult_count) {
 			HWGROUP(drive)->wrq = *rq; /* scratchpad */
 			ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
-			multwrite(drive);
+			ide_multwrite(drive, drive->mult_count);
 		} else {
 			ide_set_handler (drive, &write_intr, WAIT_CMD);
 			ide_output_data(drive, rq->buffer, SECTOR_WORDS);
 		}
 		return;
 	}
-	if (rq->cmd == IDE_DRIVE_CMD) {
-		byte *args = rq->buffer;
-		if (args) {
+	printk("%s: bad command: %d\n", drive->name, rq->cmd);
+	ide_end_request(0, HWGROUP(drive));
+}
+
+/*
+ * execute_drive_cmd() issues a special drive command,
+ * usually initiated by ioctl() from the external hdparm program.
+ */
+static void execute_drive_cmd (ide_drive_t *drive, struct request *rq)
+{
+	byte *args = rq->buffer;
+	if (args) {
 #ifdef DEBUG
-			printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x\n",
-			 drive->name, args[0], args[1], args[2]);
+		printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x xx=0x%02x\n",
+		 drive->name, args[0], args[1], args[2], args[3]);
 #endif
-			OUT_BYTE(args[2],io_base+IDE_FEATURE_OFFSET);
-			ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
-			return;
-		} else {
-			/*
-			 * NULL is actually a valid way of waiting for
-			 * all current requests to be flushed from the queue.
-			 */
+		OUT_BYTE(args[2],IDE_FEATURE_REG);
+		ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
+		return;
+	} else {
+		/*
+		 * NULL is actually a valid way of waiting for
+		 * all current requests to be flushed from the queue.
+		 */
 #ifdef DEBUG
-			printk("%s: DRIVE_CMD (null)\n", drive->name);
+		printk("%s: DRIVE_CMD (null)\n", drive->name);
 #endif
-			ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
-			return;
-		}
+		ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
+		return;
 	}
-	printk("%s: bad command: %d\n", drive->name, rq->cmd);
-	ide_end_request(0, HWGROUP(drive));
 }
 
 /*
@@ -1335,10 +1383,6 @@
 		block = 1;  /* redirect MBR access to EZ-Drive partn table */
 #endif /* FAKE_FDISK_FOR_EZDRIVE */
 	((ide_hwgroup_t *)hwif->hwgroup)->drive = drive;
-#ifdef CONFIG_BLK_DEV_HT6560B
-	if (hwif->selectproc)
-		hwif->selectproc (drive);
-#endif /* CONFIG_BLK_DEV_HT6560B */
 #if (DISK_RECOVERY_TIME > 0)
 	while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
 #endif
@@ -1347,13 +1391,17 @@
 	POLL_HWIF_TAPE_DRIVE;	/* macro from ide-tape.h */
 #endif /* CONFIG_BLK_DEV_IDETAPE */
 
-	OUT_BYTE(drive->select.all,IDE_SELECT_REG);
+	SELECT_DRIVE(hwif,drive);
 	if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
 		printk("%s: drive not ready for command\n", drive->name);
 		return;
 	}
 	
 	if (!drive->special.all) {
+		if (rq->cmd == IDE_DRIVE_CMD) {
+			execute_drive_cmd(drive, rq);
+			return;
+		}
 #ifdef CONFIG_BLK_DEV_IDEATAPI
 		switch (drive->media) {
 			case ide_disk:
@@ -1366,12 +1414,6 @@
 #endif /* CONFIG_BLK_DEV_IDECD */
 #ifdef CONFIG_BLK_DEV_IDETAPE
 			case ide_tape:
-				if (rq->cmd == IDE_DRIVE_CMD) {
-					byte *args = (byte *) rq->buffer;
-					OUT_BYTE(args[2],IDE_FEATURE_REG);
-					ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
-					return;
-				}
 				idetape_do_request (drive, rq, block);
 				return;
 #endif /* CONFIG_BLK_DEV_IDETAPE */
@@ -1418,15 +1460,21 @@
 		ide_hwif_t *hwif = hwgroup->hwif;
 		struct request *rq;
 		if ((rq = hwgroup->rq) == NULL) {
+			/*
+			 * hwgroup->next_hwif is different from hwgroup->hwif
+			 * only when a request is inserted using "ide_next".
+			 * This saves wear and tear on IDE tapes.
+			 */
+			hwif = hwgroup->next_hwif;
 			do {
 				rq = blk_dev[hwif->major].current_request;
 				if (rq != NULL && rq->rq_status != RQ_INACTIVE)
 					goto got_rq;
-			} while ((hwif = hwif->next) != hwgroup->hwif);
+			} while ((hwif = hwif->next) != hwgroup->next_hwif);
 			return;		/* no work left for this hwgroup */
 		}
 	got_rq:	
-		do_request(hwgroup->hwif = hwif, hwgroup->rq = rq);
+		do_request(hwgroup->hwif = hwgroup->next_hwif = hwif, hwgroup->rq = rq);
 		cli();
 	} while (hwgroup->handler == NULL);
 }
@@ -1548,10 +1596,7 @@
 				ide_drive_t *drive = &hwif->drives[unit];
 				if (!drive->present)
 					continue;
-#ifdef CONFIG_BLK_DEV_HT6560B
-				if (hwif->selectproc)
-					hwif->selectproc (drive);
-#endif /* CONFIG_BLK_DEV_HT6560B */
+				SELECT_DRIVE(hwif,drive);
 				if (!OK_STAT(stat=GET_STAT(), drive->ready_stat, BAD_STAT))
 					(void) ide_dump_status(drive, "unexpected_intr", stat);
 				if ((stat & DRQ_STAT))
@@ -1559,18 +1604,15 @@
 			}
 		}
 	} while ((hwif = hwif->next) != hwgroup->hwif);
-#ifdef CONFIG_BLK_DEV_HT6560B
-	if (hwif->selectproc)
-		hwif->selectproc (hwgroup->drive);
-#endif /* CONFIG_BLK_DEV_HT6560B */
+	SELECT_DRIVE(hwif,hwgroup->drive); /* Ugh.. probably interrupts current I/O */
 }
 
 /*
  * entry point for all interrupts, caller does cli() for us
  */
-static void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
+void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
 {
-	ide_hwgroup_t  *hwgroup = irq_to_hwgroup[irq];
+	ide_hwgroup_t  *hwgroup = dev_id;
 	ide_handler_t  *handler;
 
 	if (irq == hwgroup->hwif->irq && (handler = hwgroup->handler) != NULL) {
@@ -1674,6 +1716,8 @@
 	struct blk_dev_struct *bdev = &blk_dev[major];
 	struct semaphore sem = MUTEX_LOCKED;
 
+	if (IS_PROMISE_DRIVE && rq->buffer != NULL)
+		return -ENOSYS;  /* special drive cmds not supported */
 	rq->errors = 0;
 	rq->rq_status = RQ_ACTIVE;
 	rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
@@ -1682,6 +1726,8 @@
 
 	save_flags(flags);
 	cli();
+	if (action == ide_next)
+		HWGROUP(drive)->next_hwif = HWIF(drive);
 	cur_rq = bdev->current_request;
 
 	if (cur_rq == NULL || action == ide_preempt) {
@@ -1842,19 +1888,22 @@
 static int ide_ioctl (struct inode *inode, struct file *file,
 			unsigned int cmd, unsigned long arg)
 {
-	struct hd_geometry *loc = (struct hd_geometry *) arg;
 	int err;
 	ide_drive_t *drive;
 	unsigned long flags;
 	struct request rq;
 
-	ide_init_drive_cmd (&rq);
+	if (!suser())
+		return -EACCES;
 	if (!inode || !(inode->i_rdev))
 		return -EINVAL;
 	if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
 		return -ENODEV;
+	ide_init_drive_cmd (&rq);
 	switch (cmd) {
 		case HDIO_GETGEO:
+		{
+			struct hd_geometry *loc = (struct hd_geometry *) arg;
 			if (!loc || drive->media != ide_disk) return -EINVAL;
 			err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
 			if (err) return err;
@@ -1864,15 +1913,13 @@
 			put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
 				(unsigned long *) &loc->start);
 			return 0;
-
+		}
 		case BLKFLSBUF:
-			if(!suser()) return -EACCES;
 			fsync_dev(inode->i_rdev);
 			invalidate_buffers(inode->i_rdev);
 			return 0;
 
 		case BLKRASET:
-			if(!suser()) return -EACCES;
 			if(arg > 0xff) return -EINVAL;
 			read_ahead[MAJOR(inode->i_rdev)] = arg;
 			return 0;
@@ -1926,8 +1973,6 @@
 			if (arg > 1)
 				return -EINVAL;
 		case HDIO_SET_32BIT:
-			if (!suser())
-				return -EACCES;
 			if ((MINOR(inode->i_rdev) & PARTN_MASK))
 				return -EINVAL;
 			save_flags(flags);
@@ -1967,8 +2012,6 @@
 			return 0;
 
 		case HDIO_SET_MULTCOUNT:
-			if (!suser())
-				return -EACCES;
 			if (MINOR(inode->i_rdev) & PARTN_MASK)
 				return -EINVAL;
 			if (drive->id && arg > drive->id->max_multsect)
@@ -1987,26 +2030,33 @@
 
 		case HDIO_DRIVE_CMD:
 		{
-			unsigned long args;
-
-			if (NULL == (long *) arg)
+			byte args[4], *argbuf = args;
+			int argsize = 4;
+			if (NULL == (void *) arg) {
 				err = ide_do_drive_cmd(drive, &rq, ide_wait);
-			else {
-				if (!(err = verify_area(VERIFY_READ,(long *)arg,sizeof(long))))
-				{
-					args = get_user((long *)arg);
-					if (!(err = verify_area(VERIFY_WRITE,(long *)arg,sizeof(long)))) {
-						rq.buffer = (char *) &args;
-						err = ide_do_drive_cmd(drive, &rq, ide_wait);
-						put_user(args,(long *)arg);
-					}
+			} else if (!(err = verify_area(VERIFY_READ,(void *)arg, 4))) {
+				memcpy_fromfs(args, (void *)arg, 4);
+				if (args[3]) {
+					argsize = 4 + (SECTOR_WORDS * 4 * args[3]);
+					argbuf = kmalloc(argsize, GFP_KERNEL);
+					if (argbuf == NULL)
+						return -ENOMEM;
+					argbuf[0] = args[0];
+					argbuf[1] = args[1];
+					argbuf[2] = args[2];
+					argbuf[3] = args[3];
 				}
+				if (!(err = verify_area(VERIFY_WRITE,(void *)arg, argsize))) {
+					rq.buffer = argbuf;
+					err = ide_do_drive_cmd(drive, &rq, ide_wait);
+					memcpy_tofs((void *)arg, argbuf, argsize);
+				}
+				if (argsize > 4)
+					kfree(argbuf);
 			}
 			return err;
 		}
 		case HDIO_SET_PIO_MODE:
-			if (!suser())
-				return -EACCES;
 			if (MINOR(inode->i_rdev) & PARTN_MASK)
 				return -EINVAL;
 			if (!HWIF(drive)->tuneproc)
@@ -2117,6 +2167,13 @@
 	if (cmd == WIN_PIDENTIFY) {
 		byte type = (id->config >> 8) & 0x1f;
 		printk("%s: %s, ATAPI ", drive->name, id->model);
+#ifdef CONFIG_BLK_DEV_PROMISE
+		if (HWIF(drive)->is_promise2) {
+			printk(" -- not supported on 2nd Promise port\n");
+			drive->present = 0;
+			return;
+		}
+#endif /* CONFIG_BLK_DEV_PROMISE */
 		switch (type) {
 			case 0:		/* Early cdrom models used zero */
 			case 5:
@@ -2226,7 +2283,7 @@
 
 	(void) current_capacity (drive); /* initialize LBA selection */
 
-	printk ("%s: %.40s, %ldMB w/%dKB Cache, %sCHS=%d/%d/%d",
+	printk ("%s: %.40s, %ldMB w/%dkB Cache, %sCHS=%d/%d/%d",
 	 drive->name, id->model, current_capacity(drive)/2048L, id->buf_size/2,
 	 drive->select.b.lba ? "LBA, " : "",
 	 drive->bios_cyl, drive->bios_head, drive->bios_sect);
@@ -2286,7 +2343,13 @@
 	} else
 		hd_status = IDE_ALTSTATUS_REG;	/* use non-intrusive polling */
 
-	OUT_BYTE(cmd,IDE_COMMAND_REG);		/* ask drive for ID */
+#if CONFIG_BLK_DEV_PROMISE
+	if (IS_PROMISE_DRIVE) {
+		if(promise_cmd(drive,PROMISE_IDENTIFY))
+			return 1;
+	} else
+#endif /* CONFIG_BLK_DEV_PROMISE */
+		OUT_BYTE(cmd,IDE_COMMAND_REG);		/* ask drive for ID */
 	timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
 	timeout += jiffies;
 	do {
@@ -2339,6 +2402,7 @@
 static int do_probe (ide_drive_t *drive, byte cmd)
 {
 	int rc;
+	ide_hwif_t *hwif;
 #ifdef CONFIG_BLK_DEV_IDEATAPI
 	if (drive->present) {	/* avoid waiting for inappropriate probes */
 		if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
@@ -2350,10 +2414,8 @@
 		drive->name, drive->present, drive->media,
 		(cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
 #endif
-#ifdef CONFIG_BLK_DEV_HT6560B
-	if (HWIF(drive)->selectproc)
-		HWIF(drive)->selectproc (drive);
-#endif /* CONFIG_BLK_DEV_HT6560B */
+	hwif = HWIF(drive);
+	SELECT_DRIVE(hwif,drive);
 	OUT_BYTE(drive->select.all,IDE_SELECT_REG);	/* select target drive */
 	delay_10ms();				/* wait for BUSY_STAT */
 	if (IN_BYTE(IDE_SELECT_REG) != drive->select.all && !drive->present) {
@@ -2422,7 +2484,12 @@
 {
 	unsigned int unit;
 
+#if CONFIG_BLK_DEV_PROMISE
+	if (!hwif->is_promise2 &&
+	   (check_region(hwif->io_base,8) || check_region(hwif->ctl_port,1))) {
+#else
 	if (check_region(hwif->io_base,8) || check_region(hwif->ctl_port,1)) {
+#endif /* CONFIG_BLK_DEV_PROMISE */
 		int msgout = 0;
 		for (unit = 0; unit < MAX_DRIVES; ++unit) {
 			ide_drive_t *drive = &hwif->drives[unit];
@@ -2447,6 +2514,8 @@
 			ide_drive_t *drive = &hwif->drives[unit];
 			(void) probe_for_drive (drive);
 			if (drive->present && drive->media == ide_disk) {
+				if (IS_PROMISE_DRIVE)
+					drive->select.b.lba = 1; /* required by promise driver */
 				if ((!drive->head || drive->head > 16) && !drive->select.b.lba) {
 					printk("%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n",
 					 drive->name, drive->head);
@@ -2559,11 +2628,11 @@
  * "idex=noautotune"	: driver will NOT attempt to tune interface speed
  *				This is the default for most chipsets,
  *				except the cmd640.
+ * "idex=serialize"	: do not overlap operations on idex and ide(x^1)
  *
  * The following two are valid ONLY on ide0,
  * and the defaults for the base,ctl ports must not be altered.
  *
- * "ide0=serialize"	: do not overlap operations on ide0 and ide1.
  * "ide0=dtc2278"	: probe/support DTC2278 interface
  * "ide0=ht6560b"	: probe/support HT6560B interface
  * "ide0=cmd640_vlb"	: *REQUIRED* for VLB cards with the CMD640 chip
@@ -2609,7 +2678,7 @@
 				hwif->noprobe = 0;
 				goto done;
 			case -4: /* "serialize" */
-				printk(" -- USE \"ide%c=serialize\" INSTEAD", '0'+hw);
+				printk(" -- USE \"ide%d=serialize\" INSTEAD", hw);
 				goto do_serialize;
 			case -5: /* "autotune" */
 				drive->autotune = 1;
@@ -2638,7 +2707,7 @@
 		 * Be VERY CAREFUL changing this: note hardcoded indexes below
 		 */
 		const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune",
-			"qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", NULL};
+			"qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", NULL};
 		hw = s[3] - '0';
 		hwif = &ide_hwifs[hw];
 		i = match_parm(&s[4], ide_words, vals, 3);
@@ -2659,6 +2728,13 @@
 			goto bad_hwif;
 
 		switch (i) {
+#ifdef CONFIG_BLK_DEV_PROMISE
+			case -11: /* "dc4030" */
+			{
+				setup_dc4030(hwif);
+				goto done;
+			}
+#endif /* CONFIG_BLK_DEV_PROMISE */
 #ifdef CONFIG_BLK_DEV_ALI14XX
 			case -10: /* "ali14xx" */
 			{
@@ -2717,8 +2793,8 @@
 				goto done;
 			case -2: /* "serialize" */
 			do_serialize:
-				if (hw > 1) goto bad_hwif;
-				ide_hwifs[0].serialized = 1;
+				ide_hwifs[hw].serialized = 1;   /* serialize */
+				ide_hwifs[hw^1].serialized = 1; /* with mate */
 				goto done;
 
 			case -1: /* "noprobe" */
@@ -2853,6 +2929,10 @@
 	byte cmos_disks, *BIOS = (byte *) &drive_info;
 	int unit;
 
+#ifdef CONFIG_BLK_DEV_PROMISE
+	if (hwif->is_promise2)
+		return;
+#endif /* CONFIG_BLK_DEV_PROMISE */
 	outb_p(0x12,0x70);		/* specify CMOS address 0x12 */
 	cmos_disks = inb_p(0x71);	/* read the data from 0x12 */
 	/* Extract drive geometry from CMOS+BIOS if not already setup */
@@ -2882,35 +2962,43 @@
 static int init_irq (ide_hwif_t *hwif)
 {
 	unsigned long flags;
-	int irq = hwif->irq;
-	ide_hwgroup_t *hwgroup = irq_to_hwgroup[irq];
+	ide_hwgroup_t *hwgroup = hwif->hwgroup;
+	ide_hwif_t *mate_hwif;
+	unsigned int index, mate_irq = hwif->irq;
 
 	save_flags(flags);
 	cli();
 
 	/*
-	 * Grab the irq if we don't already have it from a previous hwif
+	 * Handle serialization, regardless of init sequence
 	 */
-	if (hwgroup == NULL)  {
-		if (request_irq(irq, ide_intr, SA_INTERRUPT|SA_SAMPLE_RANDOM, hwif->name, NULL)) {
-			restore_flags(flags);
-			printk(" -- FAILED!");
-			return 1;
-		}
+	mate_hwif = &ide_hwifs[hwif->index ^ 1];
+	if (hwif->serialized && mate_hwif->present) {
+		hwgroup = mate_hwif->hwgroup;
+		mate_irq = mate_hwif->irq;
 	}
+
 	/*
-	 * Check for serialization with ide0.
-	 * This code depends on us having already taken care of ide0.
+	 * If another hwif is sharing our irq, then join its hwgroup.
 	 */
-	if (hwif->index == 1 && ide_hwifs[0].serialized && ide_hwifs[0].present)
-		hwgroup = ide_hwifs[0].hwgroup;
+	if (hwgroup == NULL) {
+		for (index = 0; index < MAX_HWIFS; index++) {
+			if (index != hwif->index) {
+				ide_hwif_t *g = &ide_hwifs[index];
+				if (g->irq == hwif->irq || g->irq == mate_irq) {
+					hwgroup = ide_hwifs[index].hwgroup;
+					break;
+				}
+			}
+		}
+	}
+
 	/*
-	 * If this is the first interface in a group,
-	 * then we need to create the hwgroup structure
+	 * If we are still without a hwgroup, then form a new one
 	 */
 	if (hwgroup == NULL) {
 		hwgroup = kmalloc (sizeof(ide_hwgroup_t), GFP_KERNEL);
-		hwgroup->hwif 	 = hwif->next = hwif;
+		hwgroup->hwif 	 = hwgroup->next_hwif = hwif->next = hwif;
 		hwgroup->rq      = NULL;
 		hwgroup->handler = NULL;
 		hwgroup->drive   = &hwif->drives[0];
@@ -2918,17 +3006,30 @@
 		init_timer(&hwgroup->timer);
 		hwgroup->timer.function = &timer_expiry;
 		hwgroup->timer.data = (unsigned long) hwgroup;
-	} else {
-		hwif->next = hwgroup->hwif->next;
-		hwgroup->hwif->next = hwif;
 	}
+
+	/*
+	 * Allocate the irq, if not already obtained for another hwif
+	 */
+	if (!hwif->got_irq) {
+		if (request_irq(hwif->irq, ide_intr, SA_INTERRUPT|SA_SAMPLE_RANDOM, hwif->name, hwgroup)) {
+			restore_flags(flags);
+			return 1;
+		}
+		hwif->got_irq = 1;
+	}
+
+	/*
+	 * Everything is okay, so link us into the hwgroup
+	 */
 	hwif->hwgroup = hwgroup;
-	irq_to_hwgroup[irq] = hwgroup;
+	hwif->next = hwgroup->hwif->next;
+	hwgroup->hwif->next = hwif;
 
 	restore_flags(flags);	/* safe now that hwif->hwgroup is set up */
 
 	printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", hwif->name,
-		hwif->io_base, hwif->io_base+7, hwif->ctl_port, irq);
+		hwif->io_base, hwif->io_base+7, hwif->ctl_port, hwif->irq);
 	if (hwgroup->hwif != hwif)
 		printk(" (serialized with %s)", hwgroup->hwif->name);
 	printk("\n");
@@ -3012,6 +3113,9 @@
 		ide_probe_for_cmd640x();
 	}
 #endif
+#ifdef CONFIG_BLK_DEV_PROMISE
+	init_dc4030();
+#endif
 }
 
 static int hwif_init (int h)
@@ -3076,7 +3180,7 @@
  */
 int ide_init (void)
 {
-	int h;
+	int index;
 
 	init_ide_data ();
 	/*
@@ -3087,8 +3191,8 @@
 	/*
 	 * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports
 	 */
-	for (h = 0; h < MAX_HWIFS; ++h)
-		hwif_init(h);
+	for (index = 0; index < MAX_HWIFS; ++index)
+		hwif_init (index);
 
 #ifdef CONFIG_BLK_DEV_IDETAPE
 	idetape_register_chrdev();	/* Register character device interface to the ide tape */
@@ -3097,61 +3201,107 @@
 	return 0;
 }
 
+#ifdef CONFIG_BLK_DEV_IDE_PCMCIA
 int ide_register(int io_base, int ctl_port, int irq)
 {
-	int h, i;
+	int index, i, rc = -1;
 	ide_hwif_t *hwif;
-	for (h = 0; h < MAX_HWIFS; ++h) {
-		hwif = &ide_hwifs[h];
-		if (hwif->present == 0) break;
-	}
-	hwif->io_base = io_base;
-	hwif->ctl_port = ctl_port;
-	hwif->irq = irq;
-	hwif->noprobe = 0;
-	if (hwif_init(h) != 0) {
-		hwif->gd->real_devices = hwif->drives[0].name;
-		for (i = 0; i < hwif->gd->nr_real; i++)
-			revalidate_disk(MKDEV(hwif->major, i<<PARTN_BITS));
-		return h;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	for (index = 0; index < MAX_HWIFS; ++index) {
+		hwif = &ide_hwifs[index];
+		if (hwif->present) {
+			if (hwif->io_base == io_base || hwif->ctl_port == ctl_port)
+				break;
+		} else {
+			hwif->io_base = io_base;
+			hwif->ctl_port = ctl_port;
+			hwif->irq = irq;
+			hwif->noprobe = 0;
+			if (!hwif_init(index))
+				break;
+			hwif->gd->real_devices = hwif->drives[0].name;
+			for (i = 0; i < hwif->gd->nr_real; i++)
+				revalidate_disk(MKDEV(hwif->major, i<<PARTN_BITS));
+			rc = index;
+		}
 	}
-	else
-		return -1;
+	restore_flags(flags);
+	return rc;
 }
 
-void ide_unregister(int h)
+void ide_unregister (unsigned int index)
 {
 	struct gendisk *prev_gd, *gd;
-	ide_hwif_t *hwif;
+	ide_hwif_t *hwif, *g;
+	ide_hwgroup_t *hwgroup;
+	int irq_count = 0;
+	unsigned long flags;
 
-	if ((h < 0) || (h >= MAX_HWIFS))
+	if (index >= MAX_HWIFS)
+		return;
+	save_flags(flags);
+	cli();
+	hwif = &ide_hwifs[index];
+	if (!hwif->present || hwif->drives[0].busy || hwif->drives[1].busy) {
+		restore_flags(flags);
 		return;
-	hwif = &ide_hwifs[h];
-	if (hwif->present) {
-		hwif->present = 0;
-		/* This assumes that there is only one group member */
-		free_irq(hwif->irq, NULL);
-		kfree(hwif->hwgroup);
-		irq_to_hwgroup[hwif->irq] = NULL;
-		unregister_blkdev(hwif->major, hwif->name);
-		kfree(blksize_size[hwif->major]);
-		blk_dev[hwif->major].request_fn = NULL;
-		blksize_size[hwif->major] = NULL;
-		gd = gendisk_head; prev_gd = NULL;
-		while (gd && (gd != hwif->gd)) {
-			prev_gd = gd;
-			gd = gd->next;
-		}
-		if (gd != hwif->gd)
-			printk("gd not in disk chain!\n");
-		else {
-			if (prev_gd != NULL)
-				prev_gd->next = gd->next;
-			else
-				gendisk_head = gd->next;
-			kfree(gd->sizes);
-			kfree(gd->part);
-			kfree(gd);
-		}
 	}
+	hwif->present = 0;
+	hwgroup = hwif->hwgroup;
+
+	/*
+	 * free the irq if we were the only hwif using it
+	 */
+	g = hwgroup->hwif;
+	do {
+		if (g->irq == hwif->irq)
+			++irq_count;
+		g = g->next;
+	} while (g != hwgroup->hwif);
+	if (irq_count == 1)
+		free_irq(hwif->irq, hwgroup);
+
+	/*
+	 * Remove us from the hwgroup, and free
+	 * the hwgroup if we were the only member
+	 */
+	while (hwgroup->hwif->next != hwif)
+		hwgroup->hwif = hwgroup->hwif->next;
+	hwgroup->hwif->next = hwif->next;
+	if (hwgroup->hwif == hwif)
+		hwgroup->hwif = hwif->next;
+	if (hwgroup->next_hwif == hwif)
+		hwgroup->next_hwif = hwif->next;
+	if (hwgroup->hwif == hwif)
+		kfree(hwgroup);
+
+	/*
+	 * Remove us from the kernel's knowledge
+	 */
+	unregister_blkdev(hwif->major, hwif->name);
+	kfree(blksize_size[hwif->major]);
+	blk_dev[hwif->major].request_fn = NULL;
+	blksize_size[hwif->major] = NULL;
+	gd = gendisk_head; prev_gd = NULL;
+	while (gd && (gd != hwif->gd)) {
+		prev_gd = gd;
+		gd = gd->next;
+	}
+	if (gd != hwif->gd)
+		printk("gd not in disk chain!\n");
+	else {
+		if (prev_gd != NULL)
+			prev_gd->next = gd->next;
+		else
+			gendisk_head = gd->next;
+		kfree(gd->sizes);
+		kfree(gd->part);
+		kfree(gd);
+	}
+	init_hwif_data (index);	/* restore hwif data to pristine status */
+	restore_flags(flags);
 }
+#endif /* CONFIG_BLK_DEV_IDE_PCMCIA */
diff -u --recursive --new-file v1.3.74/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v1.3.74/linux/drivers/block/ide.h	Wed Mar 13 10:09:15 1996
+++ linux/drivers/block/ide.h	Sat Mar 16 15:01:24 1996
@@ -74,7 +74,7 @@
  * Definitions for accessing IDE controller registers
  */
 
-#define HWIF(drive)		((ide_hwif_t *)drive->hwif)
+#define HWIF(drive)		((ide_hwif_t *)((drive)->hwif))
 #define HWGROUP(drive)		((ide_hwgroup_t *)(HWIF(drive)->hwgroup))
 
 #define IDE_DATA_OFFSET		(0)
@@ -102,10 +102,10 @@
 #define IDE_ALTSTATUS_REG	IDE_CONTROL_REG
 
 #ifdef REALLY_FAST_IO
-#define OUT_BYTE(b,p)		outb((b),p)
+#define OUT_BYTE(b,p)		outb((b),(p))
 #define IN_BYTE(p)		(byte)inb(p)
 #else
-#define OUT_BYTE(b,p)		outb_p((b),p)
+#define OUT_BYTE(b,p)		outb_p((b),(p))
 #define IN_BYTE(p)		(byte)inb_p(p)
 #endif /* REALLY_FAST_IO */
 
@@ -140,6 +140,18 @@
 #define WAIT_WORSTCASE	(30*HZ)	/* 30sec  - worst case when spinning up */
 #define WAIT_CMD	(10*HZ)	/* 10sec  - maximum wait for an IRQ to happen */
 
+#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PROMISE)
+#define SELECT_DRIVE(hwif,drive)				\
+{								\
+	if (hwif->selectproc)					\
+		hwif->selectproc(drive);			\
+	else							\
+		OUT_BYTE((drive)->select.all, hwif->io_base+IDE_SELECT_OFFSET); \
+}
+#else
+#define SELECT_DRIVE(hwif,drive)  OUT_BYTE((drive)->select.all, hwif->io_base+IDE_SELECT_OFFSET);
+#endif	/* CONFIG_BLK_DEV_HT6560B || CONFIG_BLK_DEV_PROMISE */
+		
 #ifdef CONFIG_BLK_DEV_IDETAPE
 #include "ide-tape.h"
 #endif /* CONFIG_BLK_DEV_IDETAPE */
@@ -340,7 +352,6 @@
 #ifdef CONFIG_BLK_DEV_IDETAPE
 	idetape_tape_t	tape;		/* for ide-tape.c */
 #endif /* CONFIG_BLK_DEV_IDETAPE */
-
 	} ide_drive_t;
 
 /*
@@ -377,8 +388,7 @@
 typedef void (ide_tuneproc_t)(ide_drive_t *, byte);
 
 /*
- * This is used to provide HT6560B interface support.
- * It will probably also be used by the DC4030VL driver.
+ * This is used to provide HT6560B & PROMISE interface support.
  */
 typedef void (ide_selectproc_t) (ide_drive_t *);
 
@@ -388,7 +398,8 @@
  */
 typedef enum {	ide_unknown,	ide_generic,	ide_triton,
 		ide_cmd640,	ide_dtc2278,	ide_ali14xx,
-		ide_qd6580,	ide_umc8672,	ide_ht6560b }
+		ide_qd6580,	ide_umc8672,	ide_ht6560b,
+		ide_promise }
 	hwif_chipset_t;
 
 typedef struct hwif_s {
@@ -399,9 +410,9 @@
 	ide_drive_t	drives[MAX_DRIVES];	/* drive info */
 	struct gendisk	*gd;		/* gendisk structure */
 	ide_tuneproc_t	*tuneproc;	/* routine to tune PIO mode for drives */
-#ifdef CONFIG_BLK_DEV_HT6560B
+#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PROMISE)
 	ide_selectproc_t *selectproc;	/* tweaks hardware to select drive */
-#endif /* CONFIG_BLK_DEV_HT6560B */
+#endif
 	ide_dmaproc_t	*dmaproc;	/* dma read/write/abort routine */
 	unsigned long	*dmatable;	/* dma physical region descriptor table */
 	unsigned short	dma_base;	/* base addr for dma ports (triton) */
@@ -410,10 +421,14 @@
 	char 		name[5];	/* name of interface, eg. "ide0" */
 	byte		index;		/* 0 for ide0; 1 for ide1; ... */
 	hwif_chipset_t	chipset;	/* sub-module for tuning.. */
-	unsigned	noprobe : 1;	/* don't probe for this interface */
-	unsigned	present : 1;	/* this interface exists */
-	unsigned	serialized : 1;	/* valid only for ide_hwifs[0] */
-	unsigned	no_unmask : 1;	/* disallow setting unmask bits */
+	unsigned	noprobe    : 1;	/* don't probe for this interface */
+	unsigned	present    : 1;	/* this interface exists */
+	unsigned	serialized : 1;	/* serialized operation with mate hwif */
+	unsigned	no_unmask  : 1;	/* disallow setting unmask bits */
+	unsigned	got_irq    : 1;	/* 1 = already alloc'd our irq */
+#ifdef CONFIG_BLK_DEV_PROMISE
+	unsigned	is_promise2: 1;	/* 2nd i/f on promose DC4030 */
+#endif /* CONFIG_BLK_DEV_PROMISE */
 #if (DISK_RECOVERY_TIME > 0)
 	unsigned long	last_time;	/* time when previous rq was done */
 #endif
@@ -435,6 +450,7 @@
 	ide_handler_t		*handler;/* irq handler, if active */
 	ide_drive_t		*drive;	/* current drive */
 	ide_hwif_t		*hwif;	/* ptr to current hwif in linked-list */
+	ide_hwif_t		*next_hwif; /* next selected hwif (for tape) */
 	struct request		*rq;	/* current request */
 	struct timer_list	timer;	/* failsafe timer */
 	struct request		wrq;	/* local copy of current write rq */
@@ -583,6 +599,12 @@
  * stat/err are used only when (HWGROUP(drive)->rq->cmd == IDE_DRIVE_CMD).
  */
 void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err);
+
+/*
+ * ide_multwrite() transfers a block of up to mcount sectors of data
+ * to a drive as part of a disk multwrite operation.
+ */
+void ide_multwrite (ide_drive_t *drive, unsigned int mcount);
 
 #ifdef CONFIG_BLK_DEV_IDECD
 /*
diff -u --recursive --new-file v1.3.74/linux/drivers/block/promise.c linux/drivers/block/promise.c
--- v1.3.74/linux/drivers/block/promise.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/promise.c	Sat Mar 16 13:52:15 1996
@@ -0,0 +1,346 @@
+/*
+ *  linux/drivers/block/promise.c	Version 0.04  Mar 15, 1996
+ *
+ *  Copyright (C) 1995-1996  Linus Torvalds & authors (see below)
+ */
+
+/*
+ *  Principal Author/Maintainer:  peterd@pnd-pc.demon.co.uk
+ *
+ *  This file provides support for the second port and cache of Promise
+ *  IDE interfaces, e.g. DC4030, DC5030.
+ *
+ *  Thanks are due to Mark Lord for advice and patiently answering stupid
+ *  questions, and all those mugs^H^H^H^Hbrave souls who've tested this.
+ *
+ *  Version 0.01	Initial version, #include'd in ide.c rather than
+ *                      compiled separately.
+ *                      Reads use Promise commands, writes as before. Drives
+ *                      on second channel are read-only.
+ *  Version 0.02        Writes working on second channel, reads on both
+ *                      channels. Writes fail under high load. Suspect
+ *			transfers of >127 sectors don't work.
+ *  Version 0.03        Brought into line with ide.c version 5.27.
+ *                      Other minor changes.
+ *  Version 0.04        Updated for ide.c version 5.30
+ *                      Changed initialization strategy
+ *  Version 0.05	Kernel integration.  -ml
+ */
+
+
+/*
+ * From:  'peterd@pnd-pc.demon.co.uk'
+ *
+ * Here's another version of the Promise driver for DC4030VL2 cards.
+ * There have been few substantive changes to the code, but it is now in
+ * line with the more recent ide.c changes, and is somewhat more configurable.
+ *
+ * Once you've compiled it in, you'll have to also enable the interface
+ * setup routine from the kernel command line, as in 
+ *
+ *	'linux ide0=dc4030'
+ *
+ * As before, it seems that somewhere around 3Megs when writing, bad things
+ * start to happen [timeouts/retries -ml]. If anyone can give me more feedback,
+ * I'd really appreciate it.  [email: peterd@pnd-pc.demon.co.uk]
+ *
+ */
+
+
+#undef REALLY_SLOW_IO		/* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include "ide.h"
+#include "promise.h"
+
+/* This is needed as the controller may not interrupt if the required data is
+available in the cache. We have to simulate an interrupt. Ugh! */
+
+extern void ide_intr(int, void *dev_id, struct pt_regs*);
+
+/*
+ * promise_selectproc() is invoked by ide.c
+ * in preparation for access to the specified drive.
+ */
+static void promise_selectproc (ide_drive_t *drive)
+{
+	unsigned int number;
+
+	OUT_BYTE(drive->select.all,IDE_SELECT_REG);
+	udelay(1);	/* paranoia */
+	number = ((HWIF(drive)->is_promise2)<<1) + drive->select.b.unit;
+	OUT_BYTE(number,IDE_FEATURE_REG);
+}
+
+/*
+ * promise_cmd handles the set of vendor specific commands that are initiated
+ * by command F0. They all have the same success/failure notification.
+ */
+int promise_cmd(ide_drive_t *drive, byte cmd)
+{
+	unsigned long timeout, timer;
+	byte status_val;
+
+	promise_selectproc(drive);	/* redundant? */
+	OUT_BYTE(0xF3,IDE_SECTOR_REG);
+	OUT_BYTE(cmd,IDE_SELECT_REG);
+	OUT_BYTE(PROMISE_EXTENDED_COMMAND,IDE_COMMAND_REG);
+	timeout = HZ * 10;
+	timeout += jiffies;
+	do {
+		if(jiffies > timeout) {
+			return 2; /* device timed out */
+		}
+		/* This is out of delay_10ms() */
+		/* Delays at least 10ms to give interface a chance */
+		timer = jiffies + (HZ + 99)/100 + 1;
+		while (timer > jiffies);
+		status_val = IN_BYTE(IDE_SECTOR_REG);
+	} while (status_val != 0x50 && status_val != 0x70);
+
+	if(status_val == 0x50)
+		return 0; /* device returned success */
+	else
+		return 1; /* device returned failure */
+}
+
+ide_hwif_t *hwif_required = NULL;
+
+void setup_dc4030 (ide_hwif_t *hwif)
+{
+    hwif_required = hwif;
+}
+
+/*
+init_dc4030: Test for presence of a Promise caching controller card.
+Returns: 0 if no Promise card present at this io_base
+	 1 if Promise card found
+*/
+int init_dc4030 (void)
+{
+	ide_hwif_t *hwif = hwif_required;
+        ide_drive_t *drive;
+	ide_hwif_t *second_hwif;
+	struct dc_ident ident;
+	int i;
+	
+	if (!hwif) return 0;
+
+	drive = &hwif->drives[0];
+	second_hwif = &ide_hwifs[hwif->index+1];
+	if(hwif->is_promise2) /* we've already been found ! */
+	    return 1;
+
+	if(IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF)
+	{
+	    return 0;
+	}
+	OUT_BYTE(0x08,IDE_CONTROL_REG);
+	if(promise_cmd(drive,PROMISE_GET_CONFIG)) {
+	    return 0;
+	}
+	if(ide_wait_stat(drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) {
+	    printk("%s: Failed Promise read config!\n",hwif->name);
+	    return 0;
+	}
+	ide_input_data(drive,&ident,SECTOR_WORDS);
+	if(ident.id[1] != 'P' || ident.id[0] != 'T') {
+	    return 0;
+	}
+	printk("%s: Promise caching controller, ",hwif->name);
+	switch(ident.type) {
+            case 0x43:	printk("DC4030VL, "); break;
+            default:	printk("unknown - type 0x%02x - please report!, "
+			       ,ident.type);
+			return 0;
+	}
+	printk("%dKB cache, ",(int)ident.cache_mem);
+	switch(ident.irq) {
+            case 0x00: hwif->irq = 14; break;
+            case 0x01: hwif->irq = 12; break;
+            default:   hwif->irq = 15; break;
+	}
+	printk("on IRQ %d\n",hwif->irq);
+	hwif->chipset    = second_hwif->chipset    = ide_promise;
+	hwif->selectproc = second_hwif->selectproc = &promise_selectproc;
+	second_hwif->is_promise2 = 1;
+	second_hwif->io_base = hwif->io_base;
+	second_hwif->ctl_port = hwif->ctl_port;	
+	second_hwif->irq = hwif->irq;
+	for (i=0; i<2 ; i++) {
+	    hwif->drives[i].io_32bit = 3;
+	    second_hwif->drives[i].io_32bit = 3;
+	    if(!ident.current_tm[i+2].cyl) second_hwif->drives[i].noprobe=1;
+	}
+	return 1;
+}
+
+/*
+ * promise_read_intr() is the handler for disk read/multread interrupts
+ */
+static void promise_read_intr (ide_drive_t *drive)
+{
+	byte stat;
+	int i;
+	unsigned int sectors_left, sectors_avail, nsect;
+	struct request *rq;
+
+	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
+		ide_error(drive, "promise_read_intr", stat);
+		return;
+	}
+
+read_again:
+	do {
+	    sectors_left = IN_BYTE(IDE_NSECTOR_REG);
+	    IN_BYTE(IDE_SECTOR_REG);
+	} while (IN_BYTE(IDE_NSECTOR_REG) != sectors_left);
+	rq = HWGROUP(drive)->rq;
+	sectors_avail = rq->nr_sectors - sectors_left;
+
+read_next:
+	rq = HWGROUP(drive)->rq;
+	if ((nsect = rq->current_nr_sectors) > sectors_avail)
+		nsect = sectors_avail;
+	sectors_avail -= nsect;
+	ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
+#ifdef DEBUG
+	printk("%s:  promise_read: sectors(%ld-%ld), buffer=0x%08lx, "
+	       "remaining=%ld\n", drive->name, rq->sector, rq->sector+nsect-1, 
+	       (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
+#endif
+	rq->sector += nsect;
+	rq->buffer += nsect<<9;
+	rq->errors = 0;
+	i = (rq->nr_sectors -= nsect);
+	if ((rq->current_nr_sectors -= nsect) <= 0)
+		ide_end_request(1, HWGROUP(drive));
+	if (i > 0) {
+		if (sectors_avail)
+		    goto read_next;
+		stat = GET_STAT();
+		if(stat & DRQ_STAT)
+		    goto read_again;
+		if(stat & BUSY_STAT) {
+		    ide_set_handler (drive, &promise_read_intr, WAIT_CMD);
+		    return;
+		}
+		printk("Ah! promise read intr: sectors left !DRQ !BUSY\n");
+		ide_error(drive, "promise read intr", stat);
+	}
+}
+
+/*
+ * promise_write_pollfunc() is the handler for disk write completion polling.
+ */
+static void promise_write_pollfunc (ide_drive_t *drive)
+{
+	int i;
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	struct request *rq;
+
+        if (IN_BYTE(IDE_NSECTOR_REG) != 0) {
+            if (jiffies < hwgroup->poll_timeout) {
+                ide_set_handler (drive, &promise_write_pollfunc, 1);
+                return; /* continue polling... */
+            }
+            printk("%s: write timed-out!\n",drive->name);
+            ide_error (drive, "write timeout", GET_STAT());
+            return;
+        }
+        
+	ide_multwrite(drive, 4);
+        rq = hwgroup->rq;
+        for (i = rq->nr_sectors; i > 0;) {
+            i -= rq->current_nr_sectors;
+            ide_end_request(1, hwgroup);
+        }
+        return;
+}
+
+/*
+ * promise_write() transfers a block of one or more sectors of data to a
+ * drive as part of a disk write operation. All but 4 sectors are transfered
+ * in the first attempt, then the interface is polled (nicely!) for completion
+ * before the final 4 sectors are transfered. Don't ask me why, but this is
+ * how it's done in the drivers for other O/Ses. There is no interrupt
+ * generated on writes, which is why we have to do it like this.
+ */
+static void promise_write (ide_drive_t *drive)
+{
+    ide_hwgroup_t *hwgroup = HWGROUP(drive);
+    struct request *rq = &hwgroup->wrq;
+    int i;
+
+    if (rq->nr_sectors > 4) {
+        ide_multwrite(drive, rq->nr_sectors - 4);
+        hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+        ide_set_handler (drive, &promise_write_pollfunc, 1);
+        return;
+    } else {
+        ide_multwrite(drive, rq->nr_sectors);
+        rq = hwgroup->rq;
+        for (i = rq->nr_sectors; i > 0;) {
+            i -= rq->current_nr_sectors;
+            ide_end_request(1, hwgroup);
+        }
+    }
+}
+
+/*
+ * do_promise_rw_disk() issues READ and WRITE commands to a
+ * disk on a promise controller, using LBA to address sectors.  It also
+ * takes care of issuing special DRIVE_CMDs.
+ */
+void do_promise_io (ide_drive_t *drive, struct request *rq)
+{
+	unsigned long timeout;
+	unsigned short io_base = HWIF(drive)->io_base;
+	byte stat;
+
+	if (rq->cmd == READ) {
+	    ide_set_handler(drive, &promise_read_intr, WAIT_CMD);
+	    OUT_BYTE(PROMISE_READ, io_base+IDE_COMMAND_OFFSET);
+	    /* The card's behaviour is odd at this point. If the data is
+	       available, DRQ will be true, and no interrupt will be
+	       generated by the card. If this is the case, we need to simulate
+	       an interrupt. Ugh! Otherwise, if an interrupt will occur, bit0
+	       of the SELECT register will be high, so we can just return and
+	       be interrupted.*/
+	    timeout = jiffies + HZ/20; /* 50ms wait */
+	    do {
+		stat=GET_STAT();
+		if(stat & DRQ_STAT) {
+		    ide_intr(HWIF(drive)->irq,NULL,NULL);
+		    return;
+		}
+		if(IN_BYTE(io_base+IDE_SELECT_OFFSET) & 0x01)
+		    return;
+	    } while (jiffies < timeout);
+	    printk("%s: reading: No DRQ and not waiting - Odd!\n",
+		   drive->name);
+	    return;
+	}
+	if (rq->cmd == WRITE) {
+	    OUT_BYTE(PROMISE_WRITE, io_base+IDE_COMMAND_OFFSET);
+	    if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+		printk("%s: no DRQ after issuing PROMISE_WRITE\n", drive->name);
+		return;
+	    }
+	    if (!drive->unmask)
+		cli();
+	    HWGROUP(drive)->wrq = *rq; /* scratchpad */
+	    promise_write(drive);
+	    return;
+	}
+	printk("%s: bad command: %d\n", drive->name, rq->cmd);
+	ide_end_request(0, HWGROUP(drive));
+}
diff -u --recursive --new-file v1.3.74/linux/drivers/block/promise.h linux/drivers/block/promise.h
--- v1.3.74/linux/drivers/block/promise.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/promise.h	Sat Mar 16 13:52:15 1996
@@ -0,0 +1,52 @@
+/*
+ *  linux/drivers/block/promise.h
+ *
+ *  Copyright (C) 1995-6  Linus Torvalds & authors
+ */
+
+/*
+ * Principal author: Peter Denison <peterd@pnd-pc.demon.co.uk>
+ */
+
+#ifndef IDE_PROMISE_H
+#define IDE_PROMISE_H
+
+#define	PROMISE_EXTENDED_COMMAND	0xF0
+#define	PROMISE_READ			0xF2
+#define	PROMISE_WRITE			0xF3
+/* Extended commands - main command code = 0xf0 */
+#define	PROMISE_GET_CONFIG		0x10
+#define	PROMISE_IDENTIFY		0x20
+
+struct translation_mode {
+	u16	cyl;
+	u8	head;
+	u8	sect;
+};
+
+struct dc_ident {
+	u8	type;
+	u8	unknown1;
+	u8	hw_revision;
+	u8	firmware_major;
+	u8	firmware_minor;
+	u8	bios_address;
+	u8	irq;
+	u8	unknown2;
+	u16	cache_mem;
+	u16	unknown3;
+	u8	id[2];
+	u16	info;
+	struct translation_mode current_tm[4];
+	u8	pad[SECTOR_WORDS*4 - 32];
+};
+
+/*
+ * Routines exported to ide.c:
+ */
+void do_promise_io (ide_drive_t *, struct request *);
+int promise_cmd(ide_drive_t *, byte);
+void setup_dc4030 (ide_hwif_t *);
+int init_dc4030 (void);
+
+#endif IDE_PROMISE_H
diff -u --recursive --new-file v1.3.74/linux/drivers/block/rz1000.c linux/drivers/block/rz1000.c
--- v1.3.74/linux/drivers/block/rz1000.c	Sat Mar  2 10:43:22 1996
+++ linux/drivers/block/rz1000.c	Sat Mar 16 13:52:15 1996
@@ -48,6 +48,7 @@
 			ide_hwifs[0].no_unmask = 1;
 			ide_hwifs[1].no_unmask = 1;
 			ide_hwifs[0].serialized = 1;
+			ide_hwifs[1].serialized = 1;
 			ide_pci_access_error (rc);
 			printk("serialized, disabled unmasking\n");
 		} else
diff -u --recursive --new-file v1.3.74/linux/drivers/block/triton.c linux/drivers/block/triton.c
--- v1.3.74/linux/drivers/block/triton.c	Sat Mar  2 10:43:22 1996
+++ linux/drivers/block/triton.c	Sat Mar 16 13:52:15 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/triton.c	Version 1.06  Feb 6, 1996
+ *  linux/drivers/block/triton.c	Version 1.08  Mar 13, 1996
  *
  *  Copyright (c) 1995-1996  Mark Lord
  *  May be copied or modified under the terms of the GNU General Public License
@@ -142,6 +142,7 @@
  */
 #define PRD_BYTES	8
 #define PRD_ENTRIES	(PAGE_SIZE / (2 * PRD_BYTES))
+#define DEFAULT_BMIBA	0xe800	/* in case BIOS did not init it */
 
 /*
  * dma_intr() is the handler for disk read/write DMA interrupts
@@ -321,7 +322,7 @@
 
 /*
  * print_triton_drive_flags() displays the currently programmed options
- * in the Triton chipset for a given drive.
+ * in the 430FX (Triton) chipset for a given drive.
  *
  *	If fastDMA  is "no", then slow ISA timings are used for DMA data xfers.
  *	If fastPIO  is "no", then slow ISA timings are used for PIO data xfers.
@@ -345,11 +346,11 @@
 {
 	static unsigned long dmatable = 0;
 
-	printk("    %s: BusMaster DMA at 0x%04x-0x%04x", hwif->name, base, base+7);
+	printk("    %s: BM-DMA at 0x%04x-0x%04x", hwif->name, base, base+7);
 	if (check_region(base, 8)) {
 		printk(" -- ERROR, PORTS ALREADY IN USE");
 	} else {
-		request_region(base, 8, "triton DMA");
+		request_region(base, 8, "IDE DMA");
 		hwif->dma_base = base;
 		if (!dmatable) {
 			/*
@@ -370,20 +371,6 @@
 }
 
 /*
- * calc_mode() returns the ATA PIO mode number, based on the number
- * of cycle clks passed in.  Assumes 33Mhz bus operation (30ns per clk).
- */
-byte calc_mode (byte clks)
-{
-	if (clks == 3)	return 5;
-	if (clks == 4)	return 4;
-	if (clks <  6)	return 3;
-	if (clks <  8)	return 2;
-	if (clks < 13)	return 1;
-	return 0;
-}
-
-/*
  * ide_init_triton() prepares the IDE driver for DMA operation.
  * This routine is called once, from ide.c during driver initialization,
  * for each triton chipset which is found (unlikely to be more than one).
@@ -395,26 +382,38 @@
 	unsigned short bmiba, pcicmd;
 	unsigned int timings;
 
-	printk("ide: Triton BM-IDE on PCI bus %d function %d\n", bus, fn);
+	printk("ide: 430FX (Triton) on PCI bus %d function %d\n", bus, fn);
 	/*
 	 * See if IDE and BM-DMA features are enabled:
 	 */
 	if ((rc = pcibios_read_config_word(bus, fn, 0x04, &pcicmd)))
 		goto quit;
 	if ((pcicmd & 1) == 0)  {
-		printk("ide: Triton IDE ports are not enabled\n");
+		printk("ide: ports are not enabled (BIOS)\n");
 		goto quit;
 	}
 	if ((pcicmd & 4) == 0) {
-		printk("ide: Triton BM-DMA feature is not enabled -- upgrade your BIOS\n");
+		printk("ide: BM-DMA feature is not enabled (BIOS)\n");
 	} else {
 		/*
 		 * Get the bmiba base address
 		 */
-		if ((rc = pcibios_read_config_word(bus, fn, 0x20, &bmiba)))
-			goto quit;
-		bmiba &= 0xfff0;	/* extract port base address */
-		dma_enabled = 1;
+		int try_again = 1;
+		do {
+			if ((rc = pcibios_read_config_word(bus, fn, 0x20, &bmiba)))
+				goto quit;
+			bmiba &= 0xfff0;	/* extract port base address */
+			if (bmiba) {
+				dma_enabled = 1;
+			} else {
+				printk("ide: BM-DMA base register is invalid (BIOS problem)\n");
+				if (inb(DEFAULT_BMIBA) == 0xff) {
+					printk("ide: setting BM-DMA base register to 0x%04x\n", DEFAULT_BMIBA);
+					if ((rc = pcibios_write_config_word(bus, fn, 0x20, DEFAULT_BMIBA)))
+						goto quit;
+				}
+			}
+		} while (!dma_enabled && try_again--);
 	}
 
 	/*
@@ -423,7 +422,7 @@
 	if ((rc = pcibios_read_config_dword(bus, fn, 0x40, &timings)))
 		goto quit;
 	if (!(timings & 0x80008000)) {
-		printk("ide: neither Triton IDE port is enabled\n");
+		printk("ide: neither port is enabled\n");
 		goto quit;
 	}
 
@@ -452,8 +451,8 @@
 			continue;
 		s_clks = ((~time >> 12) & 3) + 2;
 		r_clks = ((~time >>  8) & 3) + 1;
-		printk("    %s timing: (0x%04x) sample_CLKs=%d, recovery_CLKs=%d (PIO mode%d)\n",
-		 hwif->name, time, s_clks, r_clks, calc_mode(s_clks+r_clks));
+		printk("    %s timing: (0x%04x) sample_CLKs=%d, recovery_CLKs=%d\n",
+		 hwif->name, time, s_clks, r_clks);
 		print_triton_drive_flags (0, time & 0xf);
 		print_triton_drive_flags (1, (time >> 4) & 0xf);
 	}
diff -u --recursive --new-file v1.3.74/linux/drivers/cdrom/mcdx.c linux/drivers/cdrom/mcdx.c
--- v1.3.74/linux/drivers/cdrom/mcdx.c	Tue Mar  5 10:10:54 1996
+++ linux/drivers/cdrom/mcdx.c	Fri Mar 15 10:58:45 1996
@@ -1,7 +1,16 @@
 /*
  * The Mitsumi CDROM interface
  * Copyright (C) 1995 Heiko Schlittermann <heiko@lotte.sax.de>
- * VERSION: 1.7
+ * VERSION: 1.8
+ *
+ ****************** H E L P *********************************
+ * If you ever plan to update your CD ROM drive and perhaps
+ * want to sell or simply give away your Mitsumi FX-001[DS] 
+ * -- Please, Please --
+ * mail me (heiko@lotte.sax.de).  When my last drive goes 
+ * ballistic no more driver support will be available from me !!!
+ *************************************************************
+ *
  * 
  * 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
@@ -35,7 +44,7 @@
 
 #if RCS
 static const char *mcdx_c_version
-		= "mcdx.c,v 1.31 1996/03/02 00:21:18 heiko Exp";
+		= "$Id: mcdx.c,v 1.39 1996/03/15 00:00:59 heiko Exp $";
 #endif
 
 #include <linux/version.h>
@@ -582,13 +591,14 @@
 				      CURRENT->buffer,
 				      CURRENT->sector,
 				      CURRENT->nr_sectors))) {
-              INFO(("do_request() read error\n"));
+              /*INFO(("do_request() read error\n"));*/
+			  xwarn("do_requst() read error\n");
               if (stuffp->status & MCDX_ST_EOM) {
                   CURRENT->sector += CURRENT->nr_sectors;
                   CURRENT->nr_sectors = 0;
               } 
+			  invalidate_buffers(CURRENT->rq_dev);
               end_request(0);
-			  /* return; */
               goto again;
 	      }
 	      CURRENT->sector += i;
@@ -820,12 +830,8 @@
 	stuffp = mcdx_stuffp[MINOR(full_dev)];
 	mcdx_getstatus(stuffp, 1);
 
-	if (stuffp->yyy == 0) {
-		INFO((" ... not changed\n"));
-		return 0;
-	}
+	if (stuffp->yyy == 0) return 0;
 
-	INFO((" ... changed\n"));
 	stuffp->yyy = 0;
 	return 1;
 }
@@ -844,9 +850,9 @@
 	I wanna make this independend of cpu speed. [1 jiffie is 1/HZ] sec */
 {
     unsigned long tout = jiffies + jifs;
+	if (jifs < 0) printk ("********\n");
 
     /* TRACE((INIT, "mcdx_delay %d\n", jifs)); */
-    if (jifs <= 0) return;
 
     if (current->pid == 0) {        /* no sleep allowed */
 		while (jiffies < tout) {
@@ -873,7 +879,7 @@
     stuffp = mcdx_irq_map[irq];
 
     if (stuffp == NULL ) {
-		WARN(("mcdx: no device for intr %d\n", irq));
+		xwarn("mcdx: no device for intr %d\n", irq);
 		return;
     }
 
@@ -890,10 +896,10 @@
 	if (!stuffp->introk) {
 		TRACE((IRQ, "intr() irq %d hw status 0x%02x\n", irq, b));
 		if (~b & MCDX_RBIT_STEN) {
-			INFO((  "intr() irq %d    status 0x%02x\n", 
-					irq, inb((unsigned int) stuffp->rreg_data)));
+			xinfo(  "intr() irq %d    status 0x%02x\n", 
+					irq, inb((unsigned int) stuffp->rreg_data));
 		} else {
-			INFO((  "intr() irq %d ambigous hw status\n", irq));
+			xinfo(  "intr() irq %d ambigous hw status\n", irq);
 		}
 	} else {
 		TRACE((IRQ, "irq() irq %d ok, status %02x\n", irq, b));
@@ -988,7 +994,7 @@
 
         /* now actually get the data */
         while (sz--) {
-            if (-1 == mcdx_getval(stuffp, timeout, -1, bp)) {
+            if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) {
                 INFO(("talk() %02x timed out (data), %d tr%s left\n", 
                         cmd[0], tries - 1, tries == 2 ? "y" : "ies"));
                 st = -1; break;
@@ -1095,8 +1101,13 @@
 {
 	int drive;
 
-	WARN(("Version 1.7 for %s\n", kernel_version));
-	WARN(("mcdx.c,v 1.31 1996/03/02 00:21:18 heiko Exp\n"));
+#ifdef MODULE
+	WARN(("Version 1.8 for %s\n", kernel_version));
+#else
+	WARN(("Version 1.8\n"));
+#endif
+
+	WARN(("$Id: mcdx.c,v 1.39 1996/03/15 00:00:59 heiko Exp $\n"));
 
 	/* zero the pointer array */
 	for (drive = 0; drive < MCDX_NDRIVES; drive++)
@@ -1677,7 +1688,7 @@
 	int ans;
 
     if (stuffp->present & MULTI) {
-		ans = mcdx_talk(stuffp, "\x11", 1, buf, sizeof(buf), 1 * HZ, tries);
+		ans = mcdx_talk(stuffp, "\x11", 1, buf, sizeof(buf), 2 * HZ, tries);
 		multi->multi = buf[1];
         multi->msf_last.minute = buf[2];
         multi->msf_last.second = buf[3];
@@ -1694,7 +1705,7 @@
 {
 	char buf[9];
 	int ans;
-	ans = mcdx_talk(stuffp, "\x10", 1, buf, sizeof(buf), 1 * HZ, tries);
+	ans = mcdx_talk(stuffp, "\x10", 1, buf, sizeof(buf), 2 * HZ, tries);
 	if (ans == -1) {
 		info->n_first = 0;
 		info->n_last = 0;
@@ -1775,7 +1786,7 @@
 	int ans;
 
 	if (-1 == (ans = mcdx_talk(stuffp, "\xdc", 
-			1, buf, sizeof(buf), 1 * HZ, tries)))
+			1, buf, sizeof(buf), 2 * HZ, tries)))
 		return ans;
 
 	ver->code = buf[1];
diff -u --recursive --new-file v1.3.74/linux/drivers/char/ChangeLog linux/drivers/char/ChangeLog
--- v1.3.74/linux/drivers/char/ChangeLog	Sun Feb 11 15:32:44 1996
+++ linux/drivers/char/ChangeLog	Sat Mar 16 20:17:23 1996
@@ -1,3 +1,8 @@
+Sat Mar 16 14:33:13 1996 <aeb@cwi.nl>
+
+	* tty_io.c (disassociate_ctty): If disassociate_ctty is called by
+		exit, do not perform an implicit vhangup on a pty.
+
 Fri Feb  9 14:15:47 1996    <tytso@rsts-11.mit.edu>
 
 	* serial.c (block_til_ready): Fixed another race condition which
@@ -42,7 +47,7 @@
 
 Wed Oct 11 12:45:24 1995    <tytso@rsts-11.mit.edu>
 
-	* tty_io.c (disassociate_ctty): If dissassociate_ctty is called by
+	* tty_io.c (disassociate_ctty): If disassociate_ctty is called by
 		exit, perform an implicit vhangup on the tty.
 
 	* pty.c (pty_close): When the master pty is closed, send a hangup
diff -u --recursive --new-file v1.3.74/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- v1.3.74/linux/drivers/char/apm_bios.c	Sun Mar 10 09:49:47 1996
+++ linux/drivers/char/apm_bios.c	Sat Mar 16 20:17:27 1996
@@ -1127,6 +1127,8 @@
 	error = apm_enable_power_management();
 	if (error)
 		apm_error("enable power management", error);
+	if (error == APM_DISABLED)
+		return -1;
 #endif
 
 	init_timer(&apm_timer);
diff -u --recursive --new-file v1.3.74/linux/drivers/char/console.c linux/drivers/char/console.c
--- v1.3.74/linux/drivers/char/console.c	Tue Mar  5 10:10:54 1996
+++ linux/drivers/char/console.c	Fri Mar 15 11:01:45 1996
@@ -123,6 +123,8 @@
 static struct tty_struct *console_table[MAX_NR_CONSOLES];
 static struct termios *console_termios[MAX_NR_CONSOLES];
 static struct termios *console_termios_locked[MAX_NR_CONSOLES];
+unsigned short *vc_scrbuf[MAX_NR_CONSOLES];
+struct vc vc_cons [MAX_NR_CONSOLES];
 
 static void con_setsize(unsigned long rows, unsigned long cols);
 static void vc_init(unsigned int console, unsigned long rows,
@@ -153,31 +155,26 @@
 extern int set_get_font(unsigned char *, int, int);
 
 /* Description of the hardware situation */
-/* used in vga.c/tga.c/etc.c... :-) */
-       unsigned char	video_type;		/* Type of display being used	*/
-       unsigned long	video_mem_base;		/* Base of video memory		*/
-       unsigned long	video_mem_term;		/* End of video memory		*/
-static unsigned char	video_page;		/* Initial video page (unused)  */
-       /* these two also used in vesa_blank.c */
-       unsigned short	video_port_reg;		/* Video register select port	*/
-       unsigned short	video_port_val;		/* Video register value port	*/
-       /* these three also used in selection.c */
-       unsigned long	video_num_columns;	/* Number of text columns	*/
-       unsigned long	video_num_lines;	/* Number of text lines		*/
-       unsigned long	video_size_row;
-       unsigned long	video_screen_size;
-       int can_do_color = 0;
-static int printable = 0;			/* Is console ready for printing? */
-	/* these also used in in vt.c */
-       int		video_mode_512ch = 0;	/* 512-character mode */
-       unsigned long	video_font_height;	/* Height of current screen font */
-       unsigned long	video_scan_lines;	/* Number of scan lines on screen */
-       unsigned long    default_font_height;    /* Height of default screen font */
-       int		video_font_is_default = 1;
+unsigned char	video_type;		/* Type of display being used	*/
+unsigned long	video_mem_base;		/* Base of video memory		*/
+unsigned long	video_mem_term;		/* End of video memory		*/
+unsigned short	video_port_reg;		/* Video register select port	*/
+unsigned short	video_port_val;		/* Video register value port	*/
+unsigned long	video_num_columns;	/* Number of text columns	*/
+unsigned long	video_num_lines;	/* Number of text lines		*/
+unsigned long	video_size_row;
+unsigned long	video_screen_size;
+
+int can_do_color = 0;
+static int printable = 0;		/* Is console ready for printing? */
+
+int		video_mode_512ch = 0;	/* 512-character mode */
+unsigned long	video_font_height;	/* Height of current screen font */
+unsigned long	video_scan_lines;	/* Number of scan lines on screen */
+unsigned long   default_font_height;    /* Height of default screen font */
+int		video_font_is_default = 1;
 static unsigned short console_charmask = 0x0ff;
 
-       unsigned short *vc_scrbuf[MAX_NR_CONSOLES];
-
 /* used by kbd_bh - set by keyboard_interrupt */
        int do_poke_blanked_console = 0;
        int console_blanked = 0;
@@ -185,8 +182,6 @@
 static int vesa_off_interval = 0;
 static long blank_origin, blank__origin, unblank_origin;
 
-struct vc vc_cons [MAX_NR_CONSOLES];
-
 
 #ifdef CONFIG_SERIAL_ECHO
 
@@ -515,63 +510,68 @@
 /*
  * Hardware scrollback support
  */
-unsigned short __real_origin;
-unsigned short __origin;	   /* offset of currently displayed screen */
-#define last_lpos (((video_mem_term-video_mem_base)/video_num_columns/2)-video_num_lines+1)
-#define last_origin_rel ( last_lpos * video_num_columns )
-#define last_origin ( video_mem_base + last_origin_rel * 2 )
-unsigned short __scrollback_mode;   /* 1 means scrollback can wrap */
 extern void __set_origin(unsigned short);
-
-void scrollback(int lines)
-{
-	if (!lines)
-		lines = video_num_lines/2;
-	lines *= video_num_columns;
-	lines = __origin - lines;
-	if (__scrollback_mode == 0) {
-		if (lines < 0)
-			lines = 0;
-	} else {
-		int s_top = __real_origin+video_num_lines*video_num_columns ;
-		if (lines < 0) {
-			int count ;
+unsigned short __real_origin;       /* offset of non-scrolled screen */
+unsigned short __origin;	    /* offset of currently displayed screen */
+unsigned char has_wrapped;          /* all of videomem is data of fg_console */
+static unsigned char hardscroll_enabled;
+static unsigned char hardscroll_disabled_by_init = 0;
+
+void no_scroll(char *str, int *ints)
+{
+  /*
+   * Disabling scrollback is required for the Braillex ib80-piezo
+   * Braille reader made by F.H. Papenmeier (Germany).
+   * Use the "no-scroll" bootflag.
+   */
+	hardscroll_disabled_by_init = 1;
+	hardscroll_enabled = 0;
+}
+
+static void scrolldelta(int lines)
+{
+	int new_origin;
+	int last_origin_rel = (((video_mem_term - video_mem_base)
+         / video_num_columns / 2) - (video_num_lines - 1)) * video_num_columns;
+
+	new_origin = __origin + lines * video_num_columns;
+	if (__origin > __real_origin)
+		new_origin -= last_origin_rel;
+	if (new_origin < 0) {
+		int s_top = __real_origin + video_num_lines*video_num_columns;
+		new_origin += last_origin_rel;
+		if (new_origin < s_top)
+			new_origin = s_top;
+		if (new_origin > last_origin_rel - video_num_columns
+		    || has_wrapped == 0)
+			new_origin = 0;
+		else {
 			unsigned short * d = (unsigned short *) video_mem_base;
-			unsigned short * s = (unsigned short *) last_origin;
-
-			lines += last_origin_rel;
-			/* in case the top part of the screen has been modified since
-			 * the scroll wrapped, copy the top bit back to the bottom */
-			count = (video_num_lines-1)*video_num_columns;
+			unsigned short * s = d + last_origin_rel;
+			int count = (video_num_lines-1)*video_num_columns;
 			while (count) {
 				count--;
 				scr_writew(scr_readw(d++),s++);
 			}
-		} else if (__origin > __real_origin && lines < s_top)
-			lines = s_top ;
-	}
-	__set_origin(lines);
+		}
+	} else if (new_origin > __real_origin)
+		new_origin = __real_origin;
+
+	__set_origin(new_origin);
+}
+
+void scrollback(int lines)
+{
+	if (!lines)
+		lines = video_num_lines/2;
+	scrolldelta(-lines);
 }
 
 void scrollfront(int lines)
 {
 	if (!lines)
 		lines = video_num_lines/2;
-	lines *= video_num_columns;
-	if (__origin > __real_origin) {
-		/* assume __scrollback_mode == 1 */
-		lines += __origin;
-		if (lines >= last_origin_rel) {
-			lines -= last_origin_rel ;
-			if (lines > __real_origin)
-				lines = __real_origin;
-		}
-	} else {
-		lines += __origin;
-		if (lines > __real_origin)
-			lines = __real_origin;
-	}
-	__set_origin(lines);
+	scrolldelta(lines);
 }
 
 static void set_origin(int currcons)
@@ -587,20 +587,17 @@
 
 void scrup(int currcons, unsigned int t, unsigned int b)
 {
-	int hardscroll = 1;
+	int hardscroll = hardscroll_enabled;
 
 	if (b > video_num_lines || t >= b)
 		return;
-	if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_VGAC
-	    && video_type != VIDEO_TYPE_EGAM)
-		hardscroll = 0;
-	else if (t || b != video_num_lines)
+	if (t || b != video_num_lines)
 		hardscroll = 0;
 	if (hardscroll) {
 		origin += video_size_row;
 		pos += video_size_row;
 		scr_end += video_size_row;
-		if (origin >= last_origin) {
+		if (scr_end > video_mem_end) {
 			unsigned short * d = (unsigned short *) video_mem_start;
 			unsigned short * s = (unsigned short *) origin;
 			unsigned int count;
@@ -620,7 +617,7 @@
 			origin = video_mem_start;
 			has_scrolled = 1;
 			if (currcons == fg_console)
-				__scrollback_mode = 1;
+				has_wrapped = 1;
 		} else {
 			unsigned short * d;
 			unsigned int count;
@@ -631,8 +628,6 @@
 				count--;
 				scr_writew(video_erase_char, d++);
 			}
-			if (scr_end > last_origin)   /* we've wrapped into kept region */
-				__scrollback_mode = 0;
 		}
 		set_origin(currcons);
 	} else {
@@ -2021,8 +2016,6 @@
 		panic("Couldn't register console driver\n");
 
 	con_setsize(ORIG_VIDEO_LINES, ORIG_VIDEO_COLS);
-	video_page = ORIG_VIDEO_PAGE; 			/* never used */
-	__scrollback_mode = 0 ;
 
 	timer_table[BLANK_TIMER].fn = blank_screen;
 	timer_table[BLANK_TIMER].expires = 0;
@@ -2033,7 +2026,11 @@
 
 	kmem_start = con_type_init(kmem_start, &display_desc);
 
-	/* Initialize the variables used for scrolling (mostly EGA/VGA)	*/
+	hardscroll_enabled = (hardscroll_disabled_by_init ? 0 :
+	  (video_type == VIDEO_TYPE_EGAC
+	    || video_type == VIDEO_TYPE_VGAC
+	    || video_type == VIDEO_TYPE_EGAM));
+	has_wrapped = 0 ;
 
 	/* Due to kmalloc roundup allocating statically is more efficient -
 	   so provide MIN_NR_CONSOLES for people with very little memory */
diff -u --recursive --new-file v1.3.74/linux/drivers/char/ftape/RELEASE-NOTES linux/drivers/char/ftape/RELEASE-NOTES
--- v1.3.74/linux/drivers/char/ftape/RELEASE-NOTES	Fri Mar 15 16:03:11 1996
+++ linux/drivers/char/ftape/RELEASE-NOTES	Fri Mar 15 11:59:15 1996
@@ -1,3 +1,12 @@
+===== Release notes for ftape-2.08, 14/03/96 =====
+
+If you correct a problem with ftape, please send your patch to
+khp@pip.dknet.dk too.
+
+- Updated to reflect that NR_MEM_LISTS is gone in 1.3.74
+- Teac 700 added to list of known drives.
+- The registered device name is now "ft" rather than "ftape".
+
 ===== Release notes for ftape-2.07a, 14/03/96 =====
 
 Bugfixes by Marcin Dalecki <dalecki@namu03.gwdg.de>:
diff -u --recursive --new-file v1.3.74/linux/drivers/char/ftape/kernel-interface.c linux/drivers/char/ftape/kernel-interface.c
--- v1.3.74/linux/drivers/char/ftape/kernel-interface.c	Fri Mar 15 16:03:14 1996
+++ linux/drivers/char/ftape/kernel-interface.c	Fri Mar 15 12:03:33 1996
@@ -50,8 +50,7 @@
  * NR_BUFFERS chunks of 32Kbyte. --khp
  */
 
-byte *tape_buffer[NR_BUFFERS] =
-{NULL};
+byte *tape_buffer[NR_BUFFERS] = {NULL};
 
 /*      Local vars.
  */
@@ -66,25 +65,16 @@
 		      int req_len);
 static int ftape_write(struct inode *ino, struct file *fp, const char *buff,
 		       int req_len);
-static int ftape_lseek(struct inode *ino, struct file *filep,
-		       off_t offset, int origin);
-#if 0
-static int ftape_select(void);
-static int ftape_mmap(int dev, unsigned off, int prot);
-#else
-#define ftape_select NULL
-#define ftape_mmap NULL
-#endif
 
 static struct file_operations ftape_cdev =
 {
-	ftape_lseek,		/* lseek */
+	NULL,			/* lseek */
 	ftape_read,		/* read */
 	ftape_write,		/* write */
 	NULL,			/* readdir */
-	ftape_select,		/* select */
+	NULL,			/* select */
 	ftape_ioctl,		/* ioctl */
-	ftape_mmap,		/* mmap */
+	NULL,			/* mmap */
 	ftape_open,		/* open */
 	ftape_close,		/* release */
 	NULL,			/* fsync */
@@ -94,15 +84,18 @@
  * DMA'able memory allocation stuff.
  */
 
-static inline
-int __get_order(int size)
+/* Pure 2^n version of get_order */
+static inline int __get_order(unsigned long size)
 {
 	int order;
 
-	for (order = 0; order < NR_MEM_LISTS; ++order)
-		if (size <= (PAGE_SIZE << order))
-			return order;
-	return -1;
+	size >>= (PAGE_SHIFT-1);
+	order = -1;
+	do {
+		size >>= 1;
+		order++;
+	} while (size);
+	return order;
 }
 
 static inline
@@ -130,9 +123,9 @@
 {
 	int n;
 	int order;
-	TRACE_FUN(5, "init_module");
+	TRACE_FUN(5, "ftape_init");
 #ifdef MODULE
-	printk(KERN_INFO "ftape-2.07 960304\n"
+	printk(KERN_INFO "ftape-2.08 960314\n"
 	       KERN_INFO " (c) 1993-1995 Bas Laarhoven (bas@vimec.nl)\n"
 	       KERN_INFO " (c) 1995-1996 Kai Harrekilde-Petersen (khp@pip.dknet.dk)\n"
 	KERN_INFO " QIC-117 driver for QIC-40/80/3010/3020 tape drives\n"
@@ -141,28 +134,21 @@
 	       " with versioned symbols"
 #endif
 	       "\n", kernel_version);
-#else				/* !MODULE */
+#else /* !MODULE */
 	/* print a short no-nonsense boot message */
-	printk("ftape-2.07 960304 for Linux 1.3.70\n");
+	printk("ftape-2.08 960314 for Linux 1.3.70\n");
 #endif				/* MODULE */
 	TRACE(3, "installing QIC-117 ftape driver...");
-	if (register_chrdev(QIC117_TAPE_MAJOR, "ftape", &ftape_cdev)) {
+	if (register_chrdev(QIC117_TAPE_MAJOR, "ft", &ftape_cdev)) {
 		TRACE(1, "register_chrdev failed");
 		TRACE_EXIT;
 		return -EIO;
 	}
-	TRACEx1(3, "init_module @ 0x%p", init_module);
+	TRACEx1(3, "ftape_init @ 0x%p", ftape_init);
 	/*
 	 * Allocate the DMA buffers. They are deallocated at cleanup() time.
 	 */
 	order = __get_order(BUFF_SIZE);
-	if (order < 0) {
-		TRACE(1, "__get_order failed (no memory?)");
-		if (unregister_chrdev(QIC117_TAPE_MAJOR, "ftape") != 0) {
-			TRACE(3, "unregister_chrdev failed");
-		}
-		return -ENOMEM;
-	}
 	for (n = 0; n < NR_BUFFERS; n++) {
 		tape_buffer[n] = (byte *) dmaalloc(order);
 		if (!tape_buffer[n]) {
@@ -174,7 +160,7 @@
 				}
 			}
 			current->blocked = old_sigmask;		/* restore mask */
-			if (unregister_chrdev(QIC117_TAPE_MAJOR, "ftape") != 0) {
+			if (unregister_chrdev(QIC117_TAPE_MAJOR, "ft") != 0) {
 				TRACE(3, "unregister_chrdev failed");
 			}
 			TRACE_EXIT;
@@ -205,23 +191,19 @@
 	int order;
 	TRACE_FUN(5, "cleanup_module");
 
-	if (unregister_chrdev(QIC117_TAPE_MAJOR, "ftape") != 0) {
+	if (unregister_chrdev(QIC117_TAPE_MAJOR, "ft") != 0) {
 		TRACE(3, "failed");
 	} else {
 		TRACE(3, "successful");
 	}
 	order = __get_order(BUFF_SIZE);
-	if (order < 0) {
-		TRACE(1, "__get_order failed (but why?!)");
-	} else {
-		for (n = 0; n < NR_BUFFERS; n++) {
-			if (tape_buffer[n]) {
-				dmafree(tape_buffer[n], order);
-				tape_buffer[n] = NULL;
-				TRACEx1(3, "removed dma-buffer #%d", n);
-			} else {
-				TRACEx1(1, "dma-buffer #%d == NULL (bug?)", n);
-			}
+	for (n = 0; n < NR_BUFFERS; n++) {
+		if (tape_buffer[n]) {
+			dmafree(tape_buffer[n], order);
+			tape_buffer[n] = NULL;
+			TRACEx1(3, "removed dma-buffer #%d", n);
+		} else {
+			TRACEx1(1, "dma-buffer #%d == NULL (bug?)", n);
 		}
 	}
 	TRACE_EXIT;
@@ -370,11 +352,4 @@
 	current->blocked = old_sigmask;		/* restore mask */
 	TRACE_EXIT;
 	return result;
-}
-
-/*      Seek tape device - not implemented !
- */
-static int ftape_lseek(struct inode *ino, struct file *filep, off_t offset, int origin)
-{
-	return -ESPIPE;
 }
diff -u --recursive --new-file v1.3.74/linux/drivers/char/ftape/vendors.h linux/drivers/char/ftape/vendors.h
--- v1.3.74/linux/drivers/char/ftape/vendors.h	Sun Mar 10 09:49:48 1996
+++ linux/drivers/char/ftape/vendors.h	Fri Mar 15 12:04:09 1996
@@ -88,7 +88,7 @@
   { 0x001c8,  64, no_wake_up,        "Wangtek 3080F" }, \
   { 0x001c8,  64, wake_up_colorado,  "Wangtek 3080F" }, \
   { 0x001ca,  67, no_wake_up,        "Wangtek 3080F (new)" }, \
-  { 0x001cc,  77, wake_up_colorado,  "Wangtek 3200" }, \
+  { 0x001cc,  77, wake_up_colorado,  "Wangtek 3200 / Teac 700" }, \
   { 0x001cd,  75, wake_up_colorado,  "Reveal TB1400" }, \
   { 0x00380,   0, wake_up_colorado,  "Exabyte EXB-1500" }, \
   { 0x08880,  64, no_wake_up,        "Iomega 250" }, \
diff -u --recursive --new-file v1.3.74/linux/drivers/char/selection.h linux/drivers/char/selection.h
--- v1.3.74/linux/drivers/char/selection.h	Tue Mar  5 10:10:55 1996
+++ linux/drivers/char/selection.h	Sat Mar 16 15:01:54 1996
@@ -37,7 +37,7 @@
 
 extern unsigned short __real_origin;
 extern unsigned short __origin;
-extern unsigned short __scrollback_mode;
+extern unsigned char has_wrapped;
 
 extern unsigned short *vc_scrbuf[MAX_NR_CONSOLES];
 
diff -u --recursive --new-file v1.3.74/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v1.3.74/linux/drivers/char/tty_io.c	Wed Feb 28 11:50:01 1996
+++ linux/drivers/char/tty_io.c	Sat Mar 16 20:17:23 1996
@@ -477,7 +477,7 @@
 	struct task_struct *p;
 
 	if (tty) {
-		if (on_exit)
+		if (on_exit && tty->driver.type != TTY_DRIVER_TYPE_PTY)
 			tty_vhangup(tty);
 	} else {
 		if (current->tty_old_pgrp) {
diff -u --recursive --new-file v1.3.74/linux/drivers/char/vesa_blank.c linux/drivers/char/vesa_blank.c
--- v1.3.74/linux/drivers/char/vesa_blank.c	Fri Feb  9 17:53:01 1996
+++ linux/drivers/char/vesa_blank.c	Fri Mar 15 11:01:45 1996
@@ -26,9 +26,7 @@
 |      fd = 0;
 |    arg.ten = 10;
 |    arg.onoff = 0;
-|    if (!strcmp(argv[1], "on"))
-|      arg.onoff = 1;
-|    else if (!strcmp(argv[1], "vsync"))
+|    if (!strcmp(argv[1], "on") || !strcmp(argv[1], "vsync"))
 |      arg.onoff = 1;
 |    else if (!strcmp(argv[1], "hsync"))
 |      arg.onoff = 2;
diff -u --recursive --new-file v1.3.74/linux/drivers/char/vga.c linux/drivers/char/vga.c
--- v1.3.74/linux/drivers/char/vga.c	Tue Nov 21 13:22:10 1995
+++ linux/drivers/char/vga.c	Fri Mar 15 11:01:46 1996
@@ -26,7 +26,7 @@
  * Improved loadable font/UTF-8 support by H. Peter Anvin 
  * Feb-Sep 1995 <peter.anvin@linux.org>
  *
- * improved scrollback, plus colour palette handling, by Simon Tatham
+ * Colour palette handling, by Simon Tatham
  * 17-Jun-95 <sgt20@cam.ac.uk>
  *
  */
@@ -190,8 +190,11 @@
 				outb_p (6, 0x3cf) ;
 #endif
 
-				/* normalise the palette registers, to point the
-				 * 16 screen colours to the first 16 DAC entries */
+				/*
+				 * Normalise the palette registers, to point
+				 * the 16 screen colours to the first 16
+				 * DAC entries.
+				 */
 
 				for (i=0; i<16; i++) {
 					inb_p (0x3da) ;
@@ -200,8 +203,8 @@
 				}
 				outb_p (0x20, 0x3c0) ;
 
-				/* now set the DAC registers back to their default
-				 * values */
+				/* now set the DAC registers back to their
+				 * default values */
 
 				for (i=0; i<16; i++) {
 					outb_p (color_table[i], 0x3c8) ;
@@ -227,7 +230,6 @@
 {
 	memcpyw((unsigned short *)vc_scrbuf[currcons],
 		(unsigned short *)origin, video_screen_size);
-	__scrollback_mode = 0 ;
 	origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
 	scr_end = video_mem_end = video_mem_start + video_screen_size;
 	pos = origin + y*video_size_row + (x<<1);
@@ -276,6 +278,7 @@
 	origin = video_mem_base + offset;
 	scr_end = origin + video_screen_size;
 	pos = origin + y*video_size_row + (x<<1);
+	has_wrapped = 0;
 }
 
 /*
diff -u --recursive --new-file v1.3.74/linux/drivers/net/3c503.c linux/drivers/net/3c503.c
--- v1.3.74/linux/drivers/net/3c503.c	Wed Mar 13 10:09:16 1996
+++ linux/drivers/net/3c503.c	Sat Mar 16 13:52:12 1996
@@ -436,7 +436,7 @@
 {
     unsigned short int *wrd;
     int boguscount;		/* timeout counter */
-    unsigned short tmp_rev;	/* temporary for reversed values */
+    unsigned short word;	/* temporary for better machine code */
 
     if (ei_status.word16)      /* Tx packets go into bank 0 on EL2/16 card */
 	outb(EGACFR_RSEL|EGACFR_TCM, E33G_GACFR);
@@ -456,9 +456,9 @@
  *  Set up then start the internal memory transfer to Tx Start Page
  */
 
-    tmp_rev = (unsigned short)start_page;
-    outb(tmp_rev&0xFF, E33G_DMAAL);
-    outb(tmp_rev>>8, E33G_DMAAH);
+    word = (unsigned short)start_page;
+    outb(word&0xFF, E33G_DMAAH);
+    outb(word>>8, E33G_DMAAL);
 
     outb_p((ei_status.interface_num ? ECNTRL_AUI : ECNTRL_THIN ) | ECNTRL_OUTPUT
 	   | ECNTRL_START, E33G_CNTRL);
@@ -508,7 +508,7 @@
 {
     int boguscount;
     unsigned long hdr_start = dev->mem_start + ((ring_page - EL2_MB1_START_PG)<<8);
-    unsigned short tmp_rev;
+    unsigned short word;
 
     if (dev->mem_start) {       /* Use the shared memory. */
 	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
@@ -519,9 +519,9 @@
  *  No shared memory, use programmed I/O.
  */
 
-    tmp_rev = (unsigned short)ring_page;
-    outb(tmp_rev&0xFF, E33G_DMAAL);
-    outb(tmp_rev>>8, E33G_DMAAH);
+    word = (unsigned short)ring_page;
+    outb(word&0xFF, E33G_DMAAH);
+    outb(word>>8, E33G_DMAAL);
 
     outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
 	   | ECNTRL_START, E33G_CNTRL);
@@ -547,7 +547,7 @@
 {
     int boguscount = 0;
     unsigned short int *buf;
-    unsigned short tmp_rev;
+    unsigned short word;
 
     int end_of_ring = dev->rmem_end;
 
@@ -570,9 +570,9 @@
 /*
  *  No shared memory, use programmed I/O.
  */
-    tmp_rev = (unsigned short) ring_offset;
-    outb(tmp_rev&0xFF, E33G_DMAAL);
-    outb(tmp_rev>>8, E33G_DMAAH);
+    word = (unsigned short) ring_offset;
+    outb(word>>8, E33G_DMAAH);
+    outb(word&0xFF, E33G_DMAAL);
 
     outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
 	   | ECNTRL_START, E33G_CNTRL);
diff -u --recursive --new-file v1.3.74/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v1.3.74/linux/drivers/net/3c509.c	Wed Mar 13 10:09:16 1996
+++ linux/drivers/net/3c509.c	Sat Mar 16 14:33:15 1996
@@ -22,6 +22,10 @@
 	packet latency but lower overhead.  If interrupts are disabled for an
 	unusually long time it could also result in missed packets, but in
 	practice this rarely happens.
+	
+	
+	FIXES:
+		Alan Cox:	Removed the 'Unexpected interrupt' bug.
 */
 
 static const  char *version = "3c509.c:1.03 10/8/94 becker@cesdis.gsfc.nasa.gov\n";
@@ -321,9 +325,8 @@
 	outw(RxReset, ioaddr + EL3_CMD);
 	outw(SetReadZero | 0x00, ioaddr + EL3_CMD);
 
-	if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", NULL)) {
+	if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", dev))
 		return -EAGAIN;
-	}
 
 	EL3WINDOW(0);
 	if (el3_debug > 3)
@@ -333,7 +336,6 @@
 	/* Activate board: this is probably unnecessary. */
 	outw(0x0001, ioaddr + 4);
 
-	irq2dev_map[dev->irq] = dev;
 
 	/* Set the IRQ line. */
 	outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
@@ -478,7 +480,7 @@
 static void
 el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	struct device *dev = (struct device *)(irq2dev_map[irq]);
+	struct device *dev = (struct device *)dev_id;
 	int ioaddr, status;
 	int i = 0;
 
@@ -687,14 +689,11 @@
 		outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
 	}
 
-	free_irq(dev->irq, NULL);
+	free_irq(dev->irq, dev);
 	/* Switching back to window 0 disables the IRQ. */
 	EL3WINDOW(0);
 	/* But we explicitly zero the IRQ line select anyway. */
 	outw(0x0f00, ioaddr + WN0_IRQ);
-
-
-	irq2dev_map[dev->irq] = 0;
 
 	update_stats(ioaddr, dev);
 	MOD_DEC_USE_COUNT;
diff -u --recursive --new-file v1.3.74/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v1.3.74/linux/drivers/net/Makefile	Fri Mar 15 16:03:14 1996
+++ linux/drivers/net/Makefile	Sat Mar 16 13:52:10 1996
@@ -383,7 +383,6 @@
 
 ifeq ($(CONFIG_PI),y)
 L_OBJS += pi2.o
-CONFIG_PI = CONFIG_PI
 endif
 
 ifeq ($(CONFIG_PT),y)
diff -u --recursive --new-file v1.3.74/linux/drivers/sound/configure.c linux/drivers/sound/configure.c
--- v1.3.74/linux/drivers/sound/configure.c	Fri Mar 15 16:03:17 1996
+++ linux/drivers/sound/configure.c	Sat Mar 16 13:52:16 1996
@@ -3,7 +3,7 @@
  *
  *	 AEDSP16 will not work without significant changes.
  */
-#define DISABLED_OPTIONS 	(B(OPT_PNP)|B(OPT_AEDSP16)|B(OPT_UNUSED1)|B(OPT_UNUSED2))
+#define DISABLED_OPTIONS 	(B(OPT_SPNP)|B(OPT_AEDSP16)|B(OPT_UNUSED1)|B(OPT_UNUSED2))
 /*
  * sound/configure.c  - Configuration program for the Linux Sound Driver
  */
@@ -38,7 +38,7 @@
 #define OPT_MAD16	12
 #define OPT_CS4232	13
 #define OPT_MAUI	14
-#define OPT_PNP		15
+#define OPT_SPNP		15
 
 #define OPT_HIGHLEVEL   16	/* This must be same than the next one */
 #define OPT_UNUSED1	16
@@ -64,10 +64,10 @@
 		    B (OPT_GUS) | B (OPT_TRIX) | B (OPT_SSCAPE)|B(OPT_MAD16) | \
 		    B (OPT_CS4232)|B(OPT_MAUI))
 #define MPU_DEVS (B(OPT_PSS)|B(OPT_SSCAPE)|B(OPT_TRIX)|B(OPT_MAD16)|\
-		  B(OPT_CS4232)|B(OPT_PNP)|B(OPT_MAUI))
+		  B(OPT_CS4232)|B(OPT_SPNP)|B(OPT_MAUI))
 #define AD1848_DEVS (B(OPT_GUS16)|B(OPT_MSS)|B(OPT_PSS)|B(OPT_GUSMAX)|\
 		     B(OPT_SSCAPE)|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_CS4232)|\
-		     B(OPT_PNP))
+		     B(OPT_SPNP))
 #define SEQUENCER_DEVS (OPT_MIDI|OPT_YM3812|OPT_ADLIB|OPT_GUS|OPT_MAUI|MIDI_CARDS)
 /*
  * Options that have been disabled for some reason (incompletely implemented
@@ -123,7 +123,7 @@
   {0, 0, "MAD16", 1, 0, 0},
   {0, 0, "CS4232", 1, 0, 0},
   {0, 0, "MAUI", 1, 0, 0},
-  {0, 0, "PNP", 1, 0, 0},
+  {0, 0, "SPNP", 1, 0, 0},
 
   {B (OPT_SB), B (OPT_PAS), "UNUSED1", 1, 0, 1},
   {B (OPT_SB) | B (OPT_UNUSED1), B (OPT_PAS), "UNUSED2", 1, 0, 1},
@@ -418,6 +418,7 @@
     {
 
       for (i = 0; i < OPT_LAST; i++)
+      if (!(DISABLED_OPTIONS & B (i)))
 	if (mask == B (i))
 	  {
 	    unsigned int             j;
diff -u --recursive --new-file v1.3.74/linux/drivers/sound/dev_table.h linux/drivers/sound/dev_table.h
--- v1.3.74/linux/drivers/sound/dev_table.h	Fri Mar 15 16:03:17 1996
+++ linux/drivers/sound/dev_table.h	Sat Mar 16 13:52:16 1996
@@ -345,7 +345,7 @@
 		{"TRXPROSB", 0, SNDCARD_TRXPRO_SB, "AudioTriX (SB mode)",	attach_trix_sb, probe_trix_sb, unload_trix_sb},
 		{"TRXPROMPU", 0, SNDCARD_TRXPRO_MPU, "AudioTriX MIDI",	attach_trix_mpu, probe_trix_mpu, unload_trix_mpu},
 #endif
-#ifdef CONFIG_PNP
+#ifdef CONFIG_SPNP
 		{"AD1848", 0, 500, "SoundPort",	attach_pnp_ad1848, probe_pnp_ad1848, unload_pnp_ad1848},
 #endif
 		{NULL, 0, 0,		"*?*",			NULL, NULL, NULL}
diff -u --recursive --new-file v1.3.74/linux/drivers/sound/sb_dsp.c linux/drivers/sound/sb_dsp.c
--- v1.3.74/linux/drivers/sound/sb_dsp.c	Fri Mar 15 16:03:18 1996
+++ linux/drivers/sound/sb_dsp.c	Sat Mar 16 13:52:35 1996
@@ -827,9 +827,6 @@
       return -ENXIO;
     }
 
-  if (!sb_midi_busy)
-    sb_reset_dsp ();
-
   if (sb_no_recording && mode & OPEN_READ)
     {
       printk ("SB Warning: Recording not supported by this device\n");
@@ -837,9 +834,12 @@
 
   if (sb_intr_active || (sb_midi_busy && sb_midi_mode == UART_MIDI))
     {
-      printk ("SB: PCM not possible during MIDI input\n");
+      printk ("SB: Audio device or MIDI already in use\n");
       return -EBUSY;
     }
+
+  if (!sb_midi_busy)
+    sb_reset_dsp ();
 
   if (!irq_verified)
     {
diff -u --recursive --new-file v1.3.74/linux/fs/binfmt_aout.c linux/fs/binfmt_aout.c
--- v1.3.74/linux/fs/binfmt_aout.c	Wed Mar 13 10:09:18 1996
+++ linux/fs/binfmt_aout.c	Fri Mar 15 15:22:59 1996
@@ -202,7 +202,7 @@
  * memory and creates the pointer tables from them, and puts their
  * addresses on the "stack", returning the new stack pointer value.
  */
-static unsigned long * create_aout_tables(char * p, struct linux_binprm * bprm, int ibcs)
+static unsigned long * create_aout_tables(char * p, struct linux_binprm * bprm)
 {
 	unsigned long *argv,*envp;
 	unsigned long * sp;
@@ -228,10 +228,8 @@
 	sp -= argc+1;
 	argv = sp;
 #ifdef __i386__
-	if (!ibcs) {
-		put_user(envp,--sp);
-		put_user(argv,--sp);
-	}
+	put_user(envp,--sp);
+	put_user(argv,--sp);
 #endif
 	put_user(argc,--sp);
 	current->mm->arg_start = (unsigned long) p;
@@ -385,8 +383,7 @@
 
 	p = setup_arg_pages(p, bprm);
 	
-	p = (unsigned long) create_aout_tables((char *)p, bprm,
-					current->personality != PER_LINUX);
+	p = (unsigned long) create_aout_tables((char *)p, bprm);
 	current->mm->start_stack = p;
 #ifdef __alpha__
 	regs->gp = ex.a_gpvalue;
diff -u --recursive --new-file v1.3.74/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v1.3.74/linux/fs/nfs/nfsroot.c	Sat Feb 17 16:02:53 1996
+++ linux/fs/nfs/nfsroot.c	Sat Mar 16 13:52:10 1996
@@ -1,31 +1,55 @@
 /*
- *  linux/fs/nfs/nfsroot.c
+ *  linux/fs/nfs/nfsroot.c -- version 2.0
  *
  *  Copyright (C) 1995  Gero Kuhlmann <gero@gkminix.han.de>
+ *  Copyright (C) 1996  Martin Mares <mj@k332.feld.cvut.cz>
+ *
+ *  Allow an NFS filesystem to be mounted as root. The way this works is:
+ *     (1) Determine the local IP address via RARP or BOOTP or from the
+ *         kernel command line.
+ *     (2) Handle RPC negotiation with the system which replied to RARP or
+ *         was reported as a boot server by BOOTP or manually.
+ *     (3) The actual mounting is done later, when init() is running.
  *
- *  Allow an NFS filesystem to be mounted as root. The way this works
- *  is to first determine the local IP address via RARP. Then handle
- *  the RPC negotiation with the system which replied to the RARP. The
- *  actual mounting is done later, when init() is running. In addition
- *  it's possible to avoid using RARP if the necessary addresses are
- *  provided on the kernel command line. This is necessary to use boot-
- *  roms which use bootp instead of RARP.
  *
  *	Changes:
  *
  *	Alan Cox	:	Removed get_address name clash with FPU.
  *	Alan Cox	:	Reformatted a bit.
  *	Michael Rausch  :	Fixed recognition of an incoming RARP answer.
+ *	Martin Mares	: (2.0)	Auto-configuration via BOOTP supported.
+ *	Martin Mares	:	Manual selection of interface & BOOTP/RARP.
+ *	Martin Mares	:	Using network routes instead of host routes,
+ *				allowing the default configuration to be used
+ *				for normal operation of the host.
+ *	Martin Mares	:	Randomized timer with exponential backoff
+ *				installed to minimize network congestion.
+ *	Martin Mares	:	Code cleanup.
+ *
+ *
+ *	Known bugs and caveats:
+ *
+ *	- BOOTP code doesn't handle multiple network interfaces properly.
+ *	  For now, it uses the first one, trying to prefer ethernet-like
+ *	  devices. Not critical as diskless stations are usually single-homed.
  *
  */
 
 
 /* Define this to allow debugging output */
-#undef NFSROOT_DEBUG 1
+#undef NFSROOT_DEBUG
+#undef NFSROOT_MORE_DEBUG
 
-/* Define the timeout for waiting for a RARP reply */
-#define RARP_TIMEOUT	30	/* 30 seconds */
-#define RARP_RETRIES	 5	/* 5 retries */
+/* Choose default protocol(s) */
+#define CONFIG_USE_BOOTP
+#define CONFIG_USE_RARP
+
+/* Define the timeout for waiting for a RARP/BOOTP reply */
+#define CONF_BASE_TIMEOUT	(HZ*5)	/* Initial timeout: 5 seconds */
+#define CONF_RETRIES	 	10	/* 10 retries */
+#define CONF_TIMEOUT_RANDOM	(HZ)	/* Maximum amount of randomization */
+#define CONF_TIMEOUT_MULT	*5/4	/* Speed of timeout growth */
+#define CONF_TIMEOUT_MAX	(HZ*30)	/* Maximum allowed timeout */
 
 #include <linux/config.h>
 #include <linux/types.h>
@@ -53,6 +77,9 @@
 #include <linux/nfs_mount.h>
 #include <linux/in.h>
 #include <net/route.h>
+#include <net/sock.h>
+#include <linux/random.h>
+#include <linux/fcntl.h>
 
 
 /* Range of privileged ports */
@@ -61,7 +88,7 @@
 #define NPORTS		(ENDPORT - STARTPORT + 1)
 
 
-
+/* List of open devices */
 struct open_dev {
 	struct device *dev;
 	unsigned short old_flags;
@@ -69,15 +96,35 @@
 };
 
 static struct open_dev *open_base = NULL;
-static struct device *root_dev = NULL;
+
+/* IP configuration */
+static struct device *root_dev = NULL;	/* Device selected for booting */
+static char user_dev_name[IFNAMSIZ];	/* Name of user-selected boot device */
 static struct sockaddr_in myaddr;	/* My IP address */
 static struct sockaddr_in server;	/* Server IP address */
 static struct sockaddr_in gateway;	/* Gateway IP address */
 static struct sockaddr_in netmask;	/* Netmask for local subnet */
+
+/* BOOTP/RARP variables */
+static int bootp_flag;			/* User said: Use BOOTP! */
+static int rarp_flag;			/* User said: Use RARP! */
+static int bootp_dev_count = 0;		/* Number of devices allowing BOOTP */
+static int rarp_dev_count = 0;		/* Number of devices allowing RARP */
+volatile static int pkt_arrived;	/* BOOTP/RARP packet detected */
+
+#define ARRIVED_BOOTP 1
+#define ARRIVED_RARP 2
+
+/* NFS-related data */
 static struct nfs_mount_data nfs_data;	/* NFS mount info */
 static char nfs_path[NFS_MAXPATHLEN];	/* Name of directory to mount */
 static int nfs_port;			/* Port to connect to for NFS service */
 
+/* Macro for formatting of addresses in debug dumps */
+#define IN_NTOA(x) (((x) == INADDR_NONE) ? "none" : in_ntoa(x))
+
+/* Yes, we use sys_socket, but there's no include file for it */
+extern asmlinkage int sys_socket(int family, int type, int protocol);
 
 
 /***************************************************************************
@@ -87,19 +134,21 @@
  ***************************************************************************/
 
 /*
- * Setup and initialize all network devices
+ * Setup and initialize all network devices. If there is a user-prefered
+ * interface, ignore all other interfaces.
  */
 static int root_dev_open(void)
 {
-	struct open_dev *openp;
+	struct open_dev *openp, **last;
 	struct device *dev;
 	unsigned short old_flags;
-	int num = 0;
 
+	last = &open_base;
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if (dev->type < ARPHRD_SLIP &&
 		    dev->family == AF_INET &&
-		    !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) {
+		    !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) &&
+		    (!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) {
 			/* First up the interface */
 			old_flags = dev->flags;
 			dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
@@ -113,19 +162,22 @@
 				continue;
 			openp->dev = dev;
 			openp->old_flags = old_flags;
-			openp->next = open_base;
-			open_base = openp;
-			num++;
+			*last = openp;
+			last = &openp->next;
+			bootp_dev_count++;
+			if (!(dev->flags & IFF_NOARP))
+				rarp_dev_count++;
+#ifdef NFSROOT_DEBUG
+	printk(KERN_NOTICE "Root-NFS: Opened %s\n", dev->name);
+#endif
 		}
 	}
+	*last = NULL;
 
-	if (num == 0) {
-		printk(KERN_ERR "NFS: Unable to open at least one network device\n");
+	if (!bootp_dev_count && !rarp_dev_count) {
+		printk(KERN_ERR "Root-NFS: Unable to open at least one network device\n");
 		return -1;
 	}
-#ifdef NFSROOT_DEBUG
-	printk(KERN_NOTICE "NFS: Opened %d network interfaces\n", num);
-#endif
 	return 0;
 }
 
@@ -154,11 +206,9 @@
 }
 
 
-
-
 /***************************************************************************
 
-			RARP Subroutines
+			      RARP Subroutines
 
  ***************************************************************************/
 
@@ -259,13 +309,14 @@
 	 * variables.
 	 */
 	cli();
-	if (root_dev != NULL) {
+	if (pkt_arrived) {
 		sti();
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
-	root_dev = dev;
+	pkt_arrived = ARRIVED_RARP;
 	sti();
+	root_dev = dev;
 
 	if (myaddr.sin_addr.s_addr == INADDR_NONE) {
 		myaddr.sin_family = dev->family;
@@ -283,14 +334,12 @@
 /*
  *  Send RARP request packet over all devices which allow RARP.
  */
-static int root_rarp_send(void)
+static void root_rarp_send(void)
 {
 	struct open_dev *openp;
 	struct device *dev;
 	int num = 0;
 
-	/* always print this message so user knows what's going on... */
-	printk(KERN_NOTICE "NFS: Sending RARP request...\n");
 	for (openp = open_base; openp != NULL; openp = openp->next) {
 		dev = openp->dev;
 		if (!(dev->flags & IFF_NOARP)) {
@@ -299,67 +348,600 @@
 			num++;
 		}
 	}
+}
+
+
+/***************************************************************************
+
+			     BOOTP Subroutines
+
+ ***************************************************************************/
+
+static struct device *bootp_dev = NULL;	/* Device selected as best BOOTP target */
+
+static int bootp_xmit_fd = -1;		/* Socket descriptor for transmit */
+static struct socket *bootp_xmit_sock;	/* The socket itself */
+static int bootp_recv_fd = -1;		/* Socket descriptor for receive */
+static struct socket *bootp_recv_sock;	/* The socket itself */
+
+struct bootp_pkt {		/* BOOTP packet format */
+	u8 op;			/* 1=request, 2=reply */
+	u8 htype;		/* HW address type */
+	u8 hlen;		/* HW address length */
+	u8 hops;		/* Used only by gateways */
+	u32 xid;		/* Transaction ID */
+	u16 secs;		/* Seconds since we started */
+	u16 flags;		/* Just what is says */
+	u32 client_ip;		/* Client's IP address if known */
+	u32 your_ip;		/* Assigned IP address */
+	u32 server_ip;		/* Server's IP address */
+	u32 relay_ip;		/* IP address of BOOTP relay */
+	u8 hw_addr[16];		/* Client's HW address */
+	u8 serv_name[64];	/* Server host name */
+	u8 boot_file[128];	/* Name of boot file */
+	u8 vendor_area[128];	/* Area for extensions */
+};
+
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+static struct bootp_pkt *xmit_bootp;	/* Packet being transmitted */
+static struct bootp_pkt *recv_bootp;	/* Packet being received */
+
+static int bootp_have_route = 0;	/* BOOTP route installed */
+
+/*
+ *  Free BOOTP packet buffers
+ */
+static void root_free_bootp(void)
+{
+	if (xmit_bootp) {
+		kfree_s(xmit_bootp, sizeof(struct bootp_pkt));
+		xmit_bootp = NULL;
+	}
+	if (recv_bootp) {
+		kfree_s(recv_bootp, sizeof(struct bootp_pkt));
+		recv_bootp = NULL;
+	}
+}
+
+
+/*
+ *  Allocate memory for BOOTP packet buffers
+ */
+static inline int root_alloc_bootp(void)
+{
+	if (!(xmit_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL)) ||
+	    !(recv_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL))) {
+		printk("BOOTP: Out of memory!");
+		return -1;
+	}
+	return 0;
+}
+
+
+/*
+ *  Create default route for BOOTP sending
+ */
+static int root_add_bootp_route(void)
+{
+	struct rtentry route;
+
+	memset(&route, 0, sizeof(route));
+	route.rt_dev = bootp_dev->name;
+	route.rt_mss = bootp_dev->mtu;
+	route.rt_flags = RTF_UP;
+	((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
+	((struct sockaddr_in *) &(route.rt_dst)) -> sin_family = AF_INET;
+	((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
+	((struct sockaddr_in *) &(route.rt_genmask)) -> sin_family = AF_INET;
+	if (ip_rt_new(&route)) {
+		printk(KERN_ERR "BOOTP: Adding of route failed!\n");
+		return -1;
+	}
+	bootp_have_route = 1;
+	return 0;
+}
+
 
-	if (num == 0) {
-		printk(KERN_ERR "NFS: Couldn't find device to send RARP request to\n");
+/*
+ *  Delete default route for BOOTP sending
+ */
+static int root_del_bootp_route(void)
+{
+	struct rtentry route;
+
+	if (!bootp_have_route)
+		return 0;
+	memset(&route, 0, sizeof(route));
+	((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
+	((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
+	if (ip_rt_kill(&route)) {
+		printk(KERN_ERR "BOOTP: Deleting of route failed!\n");
 		return -1;
 	}
+	bootp_have_route = 0;
 	return 0;
 }
 
 
 /*
+ *  Open UDP socket.
+ */
+static int root_open_udp_sock(int *fd, struct socket **sock)
+{
+	struct file *file;
+	struct inode *inode;
+
+	*fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (*fd >= 0) {
+		file = current->files->fd[*fd];
+		inode = file->f_inode;
+		*sock = &inode->u.socket_i;
+		return 0;
+	}
+
+	printk(KERN_ERR "BOOTP: Cannot open UDP socket!\n");
+	return -1;
+}
+
+
+/*
+ *  Connect UDP socket.
+ */
+static int root_connect_udp_sock(struct socket *sock, u32 addr, u16 port)
+{
+	struct sockaddr_in sa;
+	int result;
+
+	sa.sin_family = AF_INET;
+	sa.sin_addr.s_addr = htonl(addr);
+	sa.sin_port = htons(port);
+	result = sock->ops->connect(sock, (struct sockaddr *) &sa, sizeof(sa), 0);
+	if (result < 0) {
+		printk(KERN_ERR "BOOTP: connect() failed\n");
+		return -1;
+	}
+	return 0;
+}
+
+
+/*
+ *  Bind UDP socket.
+ */
+static int root_bind_udp_sock(struct socket *sock, u32 addr, u16 port)
+{
+	struct sockaddr_in sa;
+	int result;
+
+	sa.sin_family = AF_INET;
+	sa.sin_addr.s_addr = htonl(addr);
+	sa.sin_port = htons(port);
+	result = sock->ops->bind(sock, (struct sockaddr *) &sa, sizeof(sa));
+	if (result < 0) {
+		printk(KERN_ERR "BOOTP: bind() failed\n");
+		return -1;
+	}
+	return 0;
+}
+
+
+/*
+ *  Send UDP packet.
+ */
+static inline int root_send_udp(struct socket *sock, void *buf, int size)
+{
+	u32 oldfs;
+	int result;
+	struct msghdr msg;
+	struct iovec iov;
+
+	oldfs = get_fs();
+	set_fs(get_ds());
+	iov.iov_base = buf;
+	iov.iov_len = size;
+	msg.msg_name = NULL;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	msg.msg_accrights = NULL;
+	result = sock->ops->sendmsg(sock, &msg, size, 0, 0);
+	set_fs(oldfs);
+	return (result != size);
+}
+
+
+/*
+ *  Try to receive UDP packet.
+ */
+static inline int root_recv_udp(struct socket *sock, void *buf, int size)
+{
+	u32 oldfs;
+	int result;
+	struct msghdr msg;
+	struct iovec iov;
+
+	oldfs = get_fs();
+	set_fs(get_ds());
+	iov.iov_base = buf;
+	iov.iov_len = size;
+	msg.msg_name = NULL;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	msg.msg_accrights = NULL;
+	msg.msg_namelen = 0;
+	result = sock->ops->recvmsg(sock, &msg, size, O_NONBLOCK, 0, &msg.msg_namelen);
+	set_fs(oldfs);
+	return result;
+}
+
+
+/*
+ *  Initialize BOOTP extension fields in the request.
+ */
+static void root_bootp_init_ext(u8 *e)
+{
+	*e++ = 99;		/* RFC1048 Magic Cookie */
+	*e++ = 130;
+	*e++ = 83;
+	*e++ = 99;
+	*e++ = 1;		/* Subnet mask request */
+	*e++ = 4;
+	e += 4;
+	*e++ = 3;		/* Default gateway request */
+	*e++ = 4;
+	e += 4;
+	*e++ = 12;		/* Host name request */
+	*e++ = 32;
+	e += 32;
+	*e++ = 15;		/* Domain name request */
+	*e++ = 32;
+	e += 32;
+	*e++ = 17;		/* Boot path */
+	*e++ = 32;
+	e += 32;
+	*e = 255;		/* End of the list */
+}
+
+
+/*
+ *  Deinitialize the BOOTP mechanism.
+ */
+static void root_bootp_close(void)
+{
+	if (bootp_xmit_fd != -1)
+		sys_close(bootp_xmit_fd);
+	if (bootp_recv_fd != -1)
+		sys_close(bootp_recv_fd);
+	root_del_bootp_route();
+	root_free_bootp();
+}
+
+
+/*
+ *  Initialize the BOOTP mechanism.
+ */
+static int root_bootp_open(void)
+{
+	struct open_dev *openp;
+	struct device *dev, *best_dev;
+
+	/*
+	 * Select the best interface for BOOTP. We try to select a first
+	 * Ethernet-like interface. It's shame I know no simple way how to send
+	 * BOOTP's to all interfaces, but it doesn't apply to usual diskless
+	 * stations as they don't have multiple interfaces.
+	 */
+
+	best_dev = NULL;
+	for (openp = open_base; openp != NULL; openp = openp->next) {
+		dev = openp->dev;
+		if (dev->flags & IFF_BROADCAST) {
+			if (!best_dev ||
+			   ((best_dev->flags & IFF_NOARP) && !(dev->flags & IFF_NOARP)))
+				best_dev = dev;
+			}
+		}
+
+	if (!best_dev) {
+		printk(KERN_ERR "BOOTP: This cannot happen!\n");
+		return -1;
+	}
+	bootp_dev = best_dev;
+
+	/* Allocate memory for BOOTP packets */
+	if (root_alloc_bootp())
+		return -1;
+
+	/* Construct BOOTP request */
+	memset(xmit_bootp, 0, sizeof(struct bootp_pkt));
+	xmit_bootp->op = BOOTP_REQUEST;
+	get_random_bytes(&xmit_bootp->xid, sizeof(xmit_bootp->xid));
+	xmit_bootp->htype = best_dev->type;
+	xmit_bootp->hlen = best_dev->addr_len;
+	memcpy(xmit_bootp->hw_addr, best_dev->dev_addr, best_dev->addr_len);
+	root_bootp_init_ext(xmit_bootp->vendor_area);
+#ifdef NFSROOT_DEBUG
+	{
+		int x;
+		printk(KERN_NOTICE "BOOTP: XID=%08x, DE=%s, HT=%02x, HL=%02x, HA=",
+			xmit_bootp->xid,
+			best_dev->name,
+			xmit_bootp->htype,
+			xmit_bootp->hlen);
+		for(x=0; x<xmit_bootp->hlen; x++)
+			printk("%02x", xmit_bootp->hw_addr[x]);
+		printk("\n");
+	}
+#endif
+
+	/* Create default route to that interface */
+	if (root_add_bootp_route())
+		return -1;
+
+	/* Open the sockets */
+	if (root_open_udp_sock(&bootp_xmit_fd, &bootp_xmit_sock) ||
+	    root_open_udp_sock(&bootp_recv_fd, &bootp_recv_sock))
+		return -1;
+
+	/* Bind/connect the sockets */
+	((struct sock *) bootp_xmit_sock->data) -> broadcast = 1;
+	((struct sock *) bootp_xmit_sock->data) -> reuse = 1;
+	((struct sock *) bootp_recv_sock->data) -> reuse = 1;
+	if (root_bind_udp_sock(bootp_recv_sock, INADDR_ANY, 68) ||
+	    root_bind_udp_sock(bootp_xmit_sock, INADDR_ANY, 68) ||
+	    root_connect_udp_sock(bootp_xmit_sock, INADDR_BROADCAST, 67))
+		return -1;
+
+	return 0;
+}
+
+
+/*
+ *  Send BOOTP request.
+ */
+static int root_bootp_send(u32 jiffies)
+{
+	xmit_bootp->secs = htons(jiffies / HZ);
+	return root_send_udp(bootp_xmit_sock, xmit_bootp, sizeof(struct bootp_pkt));
+}
+
+
+/*
+ *  Copy BOOTP-supplied string if not already set.
+ */
+static int root_bootp_string(char *dest, char *src, int len, int max)
+{
+	if (*dest || !len)
+		return 0;
+	if (len > max-1)
+		len = max-1;
+	strncpy(dest, src, len);
+	dest[len] = '\0';
+	return 1;
+}
+
+
+/*
+ *  Process BOOTP extension.
+ */
+static void root_do_bootp_ext(u8 *ext)
+{
+	u8 *c;
+	static int got_bootp_domain = 0;
+
+#ifdef NFSROOT_MORE_DEBUG
+	printk("BOOTP: Got extension %02x",*ext);
+	for(c=ext+2; c<ext+2+ext[1]; c++)
+		printk(" %02x", *c);
+	printk("\n");
+#endif
+
+	switch (*ext++) {
+		case 1:		/* Subnet mask */
+			if (netmask.sin_addr.s_addr == INADDR_NONE)
+				memcpy(&netmask.sin_addr.s_addr, ext+1, 4);
+			break;
+		case 3:		/* Default gateway */
+			if (gateway.sin_addr.s_addr == INADDR_NONE)
+				memcpy(&gateway.sin_addr.s_addr, ext+1, 4);
+			break;
+		case 12:	/* Host name */
+			if (root_bootp_string(system_utsname.nodename, ext+1, *ext, __NEW_UTS_LEN)) {
+				c = strchr(system_utsname.nodename, '.');
+				if (c) {
+					*c++ = 0;
+					if (!system_utsname.domainname[0]) {
+						strcpy(system_utsname.domainname, c);
+						got_bootp_domain = 1;
+					}
+				}
+			}
+			break;
+		case 15:	/* Domain name */
+			if (got_bootp_domain && *ext && ext[1])
+				system_utsname.domainname[0] = '\0';
+			root_bootp_string(system_utsname.domainname, ext+1, *ext, __NEW_UTS_LEN);
+			break;
+		case 17:	/* Root path */
+			root_bootp_string(nfs_path, ext+1, *ext, NFS_MAXPATHLEN);
+			break;
+	}
+}
+
+
+/*
+ *  Receive BOOTP request.
+ */
+static void root_bootp_recv(void)
+{
+	int len;
+	u8 *ext, *end, *opt;
+
+	len = root_recv_udp(bootp_recv_sock, recv_bootp, sizeof(struct bootp_pkt));
+	if (len < 0)
+		return;
+
+	/* Check consistency of incoming packet */
+	if (len < 300 ||			/* See RFC 1542:2.1 */
+	    recv_bootp->op != BOOTP_REPLY ||
+	    recv_bootp->htype != xmit_bootp->htype ||
+	    recv_bootp->hlen != xmit_bootp->hlen ||
+	    recv_bootp->xid != xmit_bootp->xid) {
+#ifdef NFSROOT_DEBUG
+		printk("?");
+#endif
+		return;
+		}
+
+	/* Record BOOTP packet arrival in the global variables */
+	cli();
+	if (pkt_arrived) {
+		sti();
+		return;
+	}
+	pkt_arrived = ARRIVED_BOOTP;
+	sti();
+	root_dev = bootp_dev;
+
+	/* Extract basic fields */
+	myaddr.sin_addr.s_addr = recv_bootp->your_ip;
+	server.sin_addr.s_addr = recv_bootp->server_ip;
+
+	/* Parse extensions */
+	if (recv_bootp->vendor_area[0] == 99 &&	/* Check magic cookie */
+	    recv_bootp->vendor_area[1] == 130 &&
+	    recv_bootp->vendor_area[2] == 83 &&
+	    recv_bootp->vendor_area[3] == 99) {
+		ext = &recv_bootp->vendor_area[4];
+		end = (u8 *) recv_bootp + len;
+		while (ext < end && *ext != 255) {
+			if (*ext == 0)		/* Padding */
+				ext++;
+			else {
+				opt = ext;
+				ext += ext[1] + 2;
+				if (ext <= end)
+					root_do_bootp_ext(opt);
+			}
+		}
+	}
+}
+
+
+/***************************************************************************
+
+			Dynamic configuration of IP.
+
+ ***************************************************************************/
+
+/*
  *  Determine client and server IP numbers and appropriate device by using
- *  the RARP protocol.
+ *  the RARP and BOOTP protocols.
  */
-static int do_rarp(void)
+static int root_auto_config(void)
 {
-	int retries = 0;
-	unsigned long timeout = 0;
-	volatile struct device **root_dev_ptr = (volatile struct device **) &root_dev;
+	int retries;
+	u32 timeout, jiff;
+	u32 start_jiffies;
+
+	/* Check devices */
+	if (bootp_flag && !bootp_dev_count) {
+		printk(KERN_ERR "BOOTP: No suitable device found.\n");
+		bootp_flag = 0;
+		}
+	if (rarp_flag && !rarp_dev_count) {
+		printk(KERN_ERR "RARP: No suitable device found.\n");
+		rarp_flag = 0;
+		}
+
+	/* If neither BOOTP nor RARP was selected manually, use both of them */
+	if (!bootp_flag && !rarp_flag) {
+#ifdef CONFIG_USE_BOOTP
+		if (bootp_dev_count)
+			bootp_flag = 1;
+#endif
+#ifdef CONFIG_USE_RARP
+		if (rarp_dev_count)
+			rarp_flag = 1;
+#endif
+		if (!bootp_flag && !rarp_flag)
+			return -1;
+	}
 
-	/* Setup RARP protocol */
-	root_rarp_open();
+	/* Setup RARP and BOOTP protocols */
+	if (rarp_flag)
+		root_rarp_open();
+	if (bootp_flag && root_bootp_open()) {
+		root_bootp_close();
+		return -1;
+	}
 
 	/*
-	 * Send RARP request and wait, until we get an answer. This loop
-	 * seems to be a terrible waste of cpu time, but actually there is
-	 * no process running at all, so we don't need to use any
+	 * Send requests and wait, until we get an answer. This loop
+	 * seems to be a terrible waste of CPU time, but actually there is
+	 * only one process running at all, so we don't need to use any
 	 * scheduler functions.
 	 * [Actually we could now, but the nothing else running note still 
 	 *  applies.. - AC]
 	 */
-	for (retries = 0; retries < RARP_RETRIES && *root_dev_ptr == NULL; retries++) {
-		if (root_rarp_send() < 0)
+	printk(KERN_NOTICE "Sending %s%s%s requests...",
+		bootp_flag ? "BOOTP" : "",
+		bootp_flag && rarp_flag ? " and " : "",
+		rarp_flag ? "RARP" : "");
+	start_jiffies = jiffies;
+	retries = CONF_RETRIES;
+	get_random_bytes(&timeout, sizeof(timeout));
+	timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM);
+	for(;;) {
+		if (bootp_flag && root_bootp_send(jiffies - start_jiffies)) {
+			printk("...BOOTP failed!\n");
+			root_bootp_close();
+			bootp_flag = 0;
+			if (!rarp_flag)
+				break;
+		}
+		if (rarp_flag)
+			root_rarp_send();
+		printk(".");
+		jiff = jiffies + timeout;
+		while (jiffies < jiff && !pkt_arrived)
+			root_bootp_recv();
+		if (pkt_arrived)
 			break;
-		timeout = jiffies + (RARP_TIMEOUT * HZ);
-		while (jiffies < timeout && *root_dev_ptr == NULL)
-			;
+		if (! --retries) {
+			printk("timed out!\n");
+			break;
+		}
+		timeout = timeout CONF_TIMEOUT_MULT;
+		if (timeout > CONF_TIMEOUT_MAX)
+			timeout = CONF_TIMEOUT_MAX;
 	}
 
-	root_rarp_close();
-	if (*root_dev_ptr == NULL && timeout > 0) {
-		printk(KERN_ERR "NFS: Timed out while waiting for RARP answer\n");
+	if (rarp_flag)
+		root_rarp_close();
+	if (bootp_flag)
+		root_bootp_close();
+
+	if (!pkt_arrived)
 		return -1;
-	}
-	printk(KERN_NOTICE "NFS: ");
-	printk("Got RARP answer from %s, ", in_ntoa(server.sin_addr.s_addr));
+
+	printk("OK\n");
+	printk(KERN_NOTICE "Root-NFS: Got %s answer from %s, ",
+		(pkt_arrived == ARRIVED_BOOTP) ? "BOOTP" : "RARP",
+		in_ntoa(server.sin_addr.s_addr));
 	printk("my address is %s\n", in_ntoa(myaddr.sin_addr.s_addr));
 
 	return 0;
 }
 
 
-
-
 /***************************************************************************
 
-			Routines to setup NFS
+			     Parsing of options
 
  ***************************************************************************/
 
 
-
 /*
  *  The following integer options are recognized
  */
@@ -410,12 +992,7 @@
 	char buf[NFS_MAXPATHLEN];
 	char *cp, *options, *val;
 
-	/* Set the default system name in case none was previously found */
-	if (!system_utsname.nodename[0]) {
-		strncpy(system_utsname.nodename, in_ntoa(myaddr.sin_addr.s_addr), __NEW_UTS_LEN);
-		system_utsname.nodename[__NEW_UTS_LEN] = '\0';
-	}
-	/* It is possible to override the host IP number here */
+	/* It is possible to override the server IP number here */
 	if (*name >= '0' && *name <= '9' && (cp = strchr(name, ':')) != NULL) {
 		*cp++ = '\0';
 		server.sin_addr.s_addr = in_aton(name);
@@ -432,7 +1009,7 @@
 	if ((options = strchr(buf, ',')))
 		*options++ = '\0';
 	if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
-		printk(KERN_ERR "NFS: Pathname for remote directory too long\n");
+		printk(KERN_ERR "Root-NFS: Pathname for remote directory too long\n");
 		return -1;
 	}
 	sprintf(nfs_path, buf, cp);
@@ -480,26 +1057,40 @@
 /*
  *  Tell the user what's going on.
  */
+#ifdef NFSROOT_DEBUG
 static void root_nfs_print(void)
 {
-#ifdef NFSROOT_DEBUG
-	printk(KERN_NOTICE "NFS: Mounting %s on server %s as root\n",
+	printk(KERN_NOTICE "Root-NFS: IP config: dev=%s, ",
+		root_dev ? root_dev->name : "none");
+	printk("local=%s, ", IN_NTOA(myaddr.sin_addr.s_addr));
+	printk("server=%s, ", IN_NTOA(server.sin_addr.s_addr));
+	printk("gw=%s, ", IN_NTOA(gateway.sin_addr.s_addr));
+	printk("mask=%s, ", IN_NTOA(netmask.sin_addr.s_addr));
+	printk("host=%s, domain=%s\n",
+		system_utsname.nodename[0] ? system_utsname.nodename : "none",
+		system_utsname.domainname[0] ? system_utsname.domainname : "none");
+	printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n",
 		nfs_path, nfs_data.hostname);
-	printk(KERN_NOTICE "NFS:     rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
+	printk(KERN_NOTICE "Root-NFS:     rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
 		nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
-	printk(KERN_NOTICE "NFS:     acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
+	printk(KERN_NOTICE "Root-NFS:     acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
 		nfs_data.acregmin, nfs_data.acregmax,
 		nfs_data.acdirmin, nfs_data.acdirmax);
-	printk(KERN_NOTICE "NFS:     port = %d, flags = %08x\n",
+	printk(KERN_NOTICE "Root-NFS:     port = %d, flags = %08x\n",
 		nfs_port, nfs_data.flags);
-#endif
 }
+#endif
 
 
 /*
- *  Parse any IP addresses
+ *  Parse any IP configuration options (the "nfsaddrs" parameter).
+ *  The parameter consists of option fields separated by colons. It can start
+ *  with device name and possibly with auto-config type ("bootp" or "rarp")
+ *  followed by own IP address, IP address of the server, IP address of
+ *  default gateway, local netmask and a host name. Any of the addresses can
+ *  be blank to indicate that default value should be used.
  */
-static void root_nfs_addrs(char *addrs)
+static void root_nfs_ip_config(char *addrs)
 {
 	char *cp, *ip, *dp;
 	int num = 0;
@@ -511,21 +1102,32 @@
 	    gateway.sin_addr.s_addr = netmask.sin_addr.s_addr = INADDR_NONE;
 	system_utsname.nodename[0] = '\0';
 	system_utsname.domainname[0] = '\0';
+	user_dev_name[0] = '\0';
+	bootp_flag = rarp_flag = 0;
 
-	/*
-	 * Parse the address field. It contains 4 IP addresses which are
-	 * separated by colons: Field 0 = my own address Field 1 = server
-	 * address Field 2 = gateway address Field 3 = netmask address
-	 * Field 4 = client host name
-	 */
+	/* Check for device name and BOOTP/RARP flags */
 	ip = addrs;
+	while (ip && *ip && (*ip < '0' || *ip > '9')) {
+		if ((cp = strchr(ip, ':')))
+			*cp++ = '\0';
+		if (*ip) {
+			if (!strcmp(ip, "rarp"))
+				rarp_flag = 1;
+			else if (!strcmp(ip, "bootp"))
+				bootp_flag = 1;
+			else if (!user_dev_name[0]) {
+				strncpy(user_dev_name, ip, IFNAMSIZ);
+				user_dev_name[IFNAMSIZ-1] = '\0';
+			}
+		}
+		ip = cp;
+	}
+
+	/* Parse the IP addresses */
 	while (ip && *ip) {
 		if ((cp = strchr(ip, ':')))
 			*cp++ = '\0';
 		if (strlen(ip) > 0) {
-#ifdef NFSROOT_DEBUG
-			printk(KERN_NOTICE "NFS: IP address num %d is \"%s\"\n", num, ip);
-#endif
 			switch (num) {
 			case 0:
 				myaddr.sin_addr.s_addr = in_aton(ip);
@@ -555,16 +1157,35 @@
 		ip = cp;
 		num++;
 	}
+#ifdef NFSROOT_DEBUG
+	printk(KERN_NOTICE "Root-NFS: IP options: dev=%s, RARP=%d, BOOTP=%d, ",
+		user_dev_name[0] ? user_dev_name : "none",
+		rarp_flag,
+		bootp_flag);
+	printk("local=%s, ", IN_NTOA(myaddr.sin_addr.s_addr));
+	printk("server=%s, ", IN_NTOA(server.sin_addr.s_addr));
+	printk("gw=%s, ", IN_NTOA(gateway.sin_addr.s_addr));
+	printk("mask=%s, ", IN_NTOA(netmask.sin_addr.s_addr));
+	printk("host=%s, domain=%s\n",
+		system_utsname.nodename[0] ? system_utsname.nodename : "none",
+		system_utsname.domainname[0] ? system_utsname.domainname : "none");
+#endif
 }
 
 
 /*
  *  Set the interface address and configure a route to the server.
  */
-static void root_nfs_setup(void)
+static int root_nfs_setup(void)
 {
 	struct rtentry route;
 
+	/* Set the default system name in case none was previously found */
+	if (!system_utsname.nodename[0]) {
+		strncpy(system_utsname.nodename, in_ntoa(myaddr.sin_addr.s_addr), __NEW_UTS_LEN);
+		system_utsname.nodename[__NEW_UTS_LEN] = '\0';
+	}
+
 	/* Set the correct netmask */
 	if (netmask.sin_addr.s_addr == INADDR_NONE)
 		netmask.sin_addr.s_addr = ip_get_mask(myaddr.sin_addr.s_addr);
@@ -578,31 +1199,43 @@
 
 	/*
 	 * Now add a route to the server. If there is no gateway given,
-	 * the server is on our own local network, so a host route is
-	 * sufficient. Otherwise we first have to create a host route to
-	 * the gateway, and then setup a gatewayed host route to the
-	 * server. Note that it's not possible to setup a network route
-	 * because we don't know the network mask of the server network.
+	 * the server is on the same subnet, so we establish only a route to
+	 * the local network. Otherwise we create a route to the gateway (the
+	 * same local network router as in the former case) and then setup a
+	 * gatewayed default route. Note that this gives sufficient network
+	 * setup even for full system operation in all common cases.
 	 */
-	memset(&route, 0, sizeof(route));
+	memset(&route, 0, sizeof(route));	/* Local subnet route */
 	route.rt_dev = root_dev->name;
 	route.rt_mss = root_dev->mtu;
-	route.rt_flags = RTF_HOST | RTF_UP;
+	route.rt_flags = RTF_UP;
+	*((struct sockaddr_in *) &(route.rt_dst)) = myaddr;
+	(((struct sockaddr_in *) &(route.rt_dst))) -> sin_addr.s_addr &= netmask.sin_addr.s_addr;
 	*((struct sockaddr_in *) &(route.rt_genmask)) = netmask;
+	if (ip_rt_new(&route)) {
+		printk(KERN_ERR "Root-NFS: Adding of local route failed!\n");
+		return -1;
+	}
 
-	if (gateway.sin_addr.s_addr == INADDR_NONE ||
-	    gateway.sin_addr.s_addr == server.sin_addr.s_addr ||
-	    !((server.sin_addr.s_addr ^ root_dev->pa_addr) & root_dev->pa_mask)) {
-		*((struct sockaddr_in *) &(route.rt_dst)) = server;
-		ip_rt_new(&route);
-	} else {
-		*((struct sockaddr_in *) &(route.rt_dst)) = gateway;
-		ip_rt_new(&route);
-		route.rt_flags |= RTF_GATEWAY;
+	if (gateway.sin_addr.s_addr != INADDR_NONE) {	/* Default route */
+		(((struct sockaddr_in *) &(route.rt_dst))) -> sin_addr.s_addr = 0;
+		(((struct sockaddr_in *) &(route.rt_genmask))) -> sin_addr.s_addr = 0;
 		*((struct sockaddr_in *) &(route.rt_gateway)) = gateway;
-		*((struct sockaddr_in *) &(route.rt_dst)) = server;
-		ip_rt_new(&route);
+		route.rt_flags |= RTF_GATEWAY;
+		if ((gateway.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
+			printk(KERN_ERR "Root-NFS: Gateway not on local network!\n");
+			return -1;
+		}
+		if (ip_rt_new(&route)) {
+			printk(KERN_ERR "Root-NFS: Adding of default route failed!\n");
+			return -1;
+		}
+	} else if ((server.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
+		printk(KERN_ERR "Root-NFS: Boot server not on local network and no default gateway configured!\n");
+		return -1;
 	}
+
+	return 0;
 }
 
 
@@ -612,27 +1245,36 @@
  */
 int nfs_root_init(char *nfsname, char *nfsaddrs)
 {
+	/*
+	 * Get local and server IP address. First check for network config
+	 * parameters in the command line parameter.
+	 */
+	root_nfs_ip_config(nfsaddrs);
+
+	/* Parse NFS options */
+	if (root_nfs_parse(nfsname))
+		return -1;
+
 	/* Setup all network devices */
 	if (root_dev_open() < 0)
 		return -1;
 
 	/*
-	 * Get local and server IP address. First check for addresses in
-	 * command line parameter. If one of the IP addresses is missing,
-	 * or there's more than one network interface in the system, use
-	 * RARP to get the missing values and routing information. If all
-	 * addresses are given, the best way to find a proper routing is
-	 * to use icmp echo requests ("ping"), but that would add a lot of
-	 * code to this module, which is only really necessary in the rare
-	 * case of multiple ethernet devices in the (diskless) system and
-	 * if the server is on another subnet (otherwise RARP can serve as
-	 * a ping substitute). If only one device is installed the routing
-	 * is obvious.
+	 * If the config information is insufficient (e.g., our IP address or
+	 * IP address of the boot server is missing or we have multiple network
+	 * interfaces and no default was set), use BOOTP or RARP to get the
+	 * missing values.
+	 *
+	 * Note that we don't try to set up correct routes for multiple
+	 * interfaces (could be solved by trying icmp echo requests), because
+	 * it's only necessary in the rare case of multiple ethernet devices
+	 * in the (diskless) system and if the server is on another subnet.
+	 * If only one interface is installed, the routing is obvious.
 	 */
-	root_nfs_addrs(nfsaddrs);
 	if ((myaddr.sin_addr.s_addr == INADDR_NONE ||
 	     server.sin_addr.s_addr == INADDR_NONE ||
-	     (open_base != NULL && open_base->next != NULL)) && do_rarp() < 0) {
+	     (open_base != NULL && open_base->next != NULL)) &&
+	     root_auto_config() < 0) {
 		root_dev_close();
 		return -1;
 	}
@@ -640,7 +1282,8 @@
 		if (open_base != NULL && open_base->next == NULL) {
 			root_dev = open_base->dev;
 		} else {
-			printk(KERN_ERR "NFS: Unable to find routing to server\n");
+			/* I hope this cannot happen */
+			printk(KERN_ERR "Root-NFS: 1 == 0!\n");
 			root_dev_close();
 			return -1;
 		}
@@ -651,23 +1294,21 @@
 	 */
 	root_dev_close();
 
-	/*
-	 * Initialize the global variables necessary for NFS. The server
-	 * directory is actually mounted after init() has been started.
-	 */
-	if (root_nfs_parse(nfsname) < 0)
+	/* Setup devices and routes */
+	if (root_nfs_setup())
 		return -1;
+
+#ifdef NFSROOT_DEBUG
 	root_nfs_print();
-	root_nfs_setup();
+#endif
+
 	return 0;
 }
 
 
-
-
 /***************************************************************************
 
-		Routines to actually mount the root directory
+	       Routines to actually mount the root directory
 
  ***************************************************************************/
 
@@ -675,8 +1316,6 @@
 static struct inode nfs_inode;		/* Inode containing socket */
 static int *rpc_packet = NULL;		/* RPC packet */
 
-extern asmlinkage int sys_socket(int family, int type, int protocol);
-
 
 /*
  *  Open a UDP socket.
@@ -687,7 +1326,7 @@
 
 	/* Open the socket */
 	if ((nfs_data.fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
-		printk(KERN_ERR "NFS: Cannot open UDP socket\n");
+		printk(KERN_ERR "Root-NFS: Cannot open UDP socket!\n");
 		return -1;
 	}
 	/*
@@ -764,12 +1403,12 @@
 		}
 	}
 	if (res < 0) {
-		printk(KERN_ERR "NFS: Cannot find a suitable listening port\n");
+		printk(KERN_ERR "Root-NFS: Cannot find a suitable listening port\n");
 		root_nfs_close(1);
 		return -1;
 	}
 #ifdef NFSROOT_DEBUG
-	printk(KERN_NOTICE "NFS: Binding to listening port %d\n", port);
+	printk(KERN_NOTICE "Root-NFS: Binding to listening port %d\n", port);
 #endif
 	return 0;
 }
@@ -840,7 +1479,7 @@
 
 	if (rpc_packet == NULL) {
 		if (!(rpc_packet = kmalloc(nfs_data.wsize + 1024, GFP_NFS))) {
-			printk(KERN_ERR "NFS: Cannot allocate UDP buffer\n");
+			printk(KERN_ERR "Root-NFS: Cannot allocate UDP buffer\n");
 			return NULL;
 		}
 	}
@@ -884,21 +1523,21 @@
 
 	if (nfs_port < 0) {
 		if ((port = root_nfs_get_port(NFS_NFS_PROGRAM, NFS_NFS_VERSION)) < 0) {
-			printk(KERN_ERR "NFS: Unable to get nfsd port number from server, using default\n");
+			printk(KERN_ERR "Root-NFS: Unable to get nfsd port number from server, using default\n");
 			port = NFS_NFS_PORT;
 		}
 		nfs_port = port;
 #ifdef NFSROOT_DEBUG
-		printk(KERN_NOTICE "NFS: Portmapper on server returned %d as nfsd port\n", port);
+		printk(KERN_NOTICE "Root-NFS: Portmapper on server returned %d as nfsd port\n", port);
 #endif
 	}
 	if ((port = root_nfs_get_port(NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION)) < 0) {
-		printk(KERN_ERR "NFS: Unable to get mountd port number from server, using default\n");
+		printk(KERN_ERR "Root-NFS: Unable to get mountd port number from server, using default\n");
 		port = NFS_MOUNT_PORT;
 	}
 	server.sin_port = htons(port);
 #ifdef NFSROOT_DEBUG
-	printk(KERN_NOTICE "NFS: Portmapper on server returned %d as mountd port\n", port);
+	printk(KERN_NOTICE "Root-NFS: Portmapper on server returned %d as mountd port\n", port);
 #endif
 
 	return 0;
@@ -935,10 +1574,9 @@
 	status = ntohl(*p++);
 	if (status == 0) {
 		nfs_data.root = *((struct nfs_fh *) p);
-		printk(KERN_NOTICE "NFS: ");
-		printk("Got file handle for %s via RPC\n", nfs_path);
+		printk(KERN_NOTICE "Root-NFS: Got file handle for %s via RPC\n", nfs_path);
 	} else {
-		printk(KERN_ERR "NFS: Server returned error %d while mounting %s\n",
+		printk(KERN_ERR "Root-NFS: Server returned error %d while mounting %s\n",
 			status, nfs_path);
 		root_nfs_close(1);
 		return -1;
diff -u --recursive --new-file v1.3.74/linux/include/asm-i386/bitops.h linux/include/asm-i386/bitops.h
--- v1.3.74/linux/include/asm-i386/bitops.h	Mon Dec 11 15:42:05 1995
+++ linux/include/asm-i386/bitops.h	Sat Mar 16 13:52:10 1996
@@ -15,8 +15,10 @@
 
 #ifdef __SMP__
 #define LOCK_PREFIX "lock ; "
+#define SMPVOL volatile
 #else
 #define LOCK_PREFIX ""
+#define SMPVOL
 #endif
 
 /*
@@ -26,7 +28,7 @@
 #define ADDR (*(struct __dummy *) addr)
 #define CONST_ADDR (*(const struct __dummy *) addr)
 
-extern __inline__ int set_bit(int nr, void * addr)
+extern __inline__ int set_bit(int nr, SMPVOL void * addr)
 {
 	int oldbit;
 
@@ -37,7 +39,7 @@
 	return oldbit;
 }
 
-extern __inline__ int clear_bit(int nr, void * addr)
+extern __inline__ int clear_bit(int nr, SMPVOL void * addr)
 {
 	int oldbit;
 
@@ -48,7 +50,7 @@
 	return oldbit;
 }
 
-extern __inline__ int change_bit(int nr, void * addr)
+extern __inline__ int change_bit(int nr, SMPVOL void * addr)
 {
 	int oldbit;
 
@@ -62,7 +64,7 @@
 /*
  * This routine doesn't need to be atomic.
  */
-extern __inline__ int test_bit(int nr, const void * addr)
+extern __inline__ int test_bit(int nr, const SMPVOL void * addr)
 {
 	return 1UL & (((const unsigned int *) addr)[nr >> 5] >> (nr & 31));
 }
diff -u --recursive --new-file v1.3.74/linux/include/asm-i386/smp.h linux/include/asm-i386/smp.h
--- v1.3.74/linux/include/asm-i386/smp.h	Wed Mar 13 10:09:22 1996
+++ linux/include/asm-i386/smp.h	Sat Mar 16 13:52:10 1996
@@ -184,6 +184,7 @@
 extern volatile unsigned long smp_invalidate_needed;
 extern void smp_invalidate(void);
 extern volatile unsigned long kernel_flag, kernel_counter;
+extern volatile unsigned long cpu_callin_map[NR_CPUS];
 extern volatile unsigned char active_kernel_processor;
 extern void smp_message_irq(int cpl, void *dev_id, struct pt_regs *regs);
 extern void smp_reschedule_irq(int cpl, struct pt_regs *regs);
diff -u --recursive --new-file v1.3.74/linux/include/linux/ax25.h linux/include/linux/ax25.h
--- v1.3.74/linux/include/linux/ax25.h	Wed Mar 13 10:09:22 1996
+++ linux/include/linux/ax25.h	Sat Mar 16 13:52:10 1996
@@ -32,6 +32,23 @@
 	ax25_address digi_addr[AX25_MAX_DIGIS];
 };
 
+struct ax25_route_opt_struct
+{
+	ax25_address port_addr;
+	ax25_address dest_addr;
+	int cmd;
+	int arg;
+};
+
+struct ax25_ctl_struct
+{
+	ax25_address port_addr;
+	ax25_address source_addr;
+	ax25_address dest_addr;
+	unsigned int cmd;
+	unsigned long arg;
+};
+
 struct ax25_bpqaddr_struct
 {
 	char dev[16];
@@ -49,6 +66,8 @@
 #define AX25_IDLE	9
 #define AX25_PACLEN	10
 
+#define AX25_KILL	99
+
 #define SIOCAX25GETUID		(SIOCPROTOPRIVATE)
 #define SIOCAX25ADDUID		(SIOCPROTOPRIVATE+1)
 #define SIOCAX25DELUID		(SIOCPROTOPRIVATE+2)
@@ -56,6 +75,18 @@
 #define	SIOCAX25BPQADDR		(SIOCPROTOPRIVATE+4)
 #define	SIOCAX25GETPARMS	(SIOCPROTOPRIVATE+5)
 #define	SIOCAX25SETPARMS	(SIOCPROTOPRIVATE+6)
+#define SIOCAX25OPTRT		(SIOCPROTOPRIVATE+7)
+#define SIOCAX25CTLCON		(SIOCPROTOPRIVATE+8)
+
+#define AX25_SET_RT_PERMANENT	1
+#define AX25_SET_RT_IPMODE	2
+
+#define AX25_RT_DYNAMIC		0
+#define AX25_RT_PERMANENT	1
+
+#define AX25_RT_IPMODE_DEFAULT	' '
+#define AX25_RT_IPMODE_DATAGRAM	'D'
+#define AX25_RT_IPMODE_VC	'V'
 
 #define AX25_NOUID_DEFAULT	0
 #define AX25_NOUID_BLOCK	1
diff -u --recursive --new-file v1.3.74/linux/include/linux/binfmts.h linux/include/linux/binfmts.h
--- v1.3.74/linux/include/linux/binfmts.h	Sun Mar 10 09:49:56 1996
+++ linux/include/linux/binfmts.h	Fri Mar 15 15:22:59 1996
@@ -32,7 +32,7 @@
  */
 struct linux_binfmt {
 	struct linux_binfmt * next;
-	int *use_count;
+	long *use_count;
 	int (*load_binary)(struct linux_binprm *, struct  pt_regs * regs);
 	int (*load_shlib)(int fd);
 	int (*core_dump)(long signr, struct pt_regs * regs);
diff -u --recursive --new-file v1.3.74/linux/include/linux/hdreg.h linux/include/linux/hdreg.h
--- v1.3.74/linux/include/linux/hdreg.h	Sun Mar 10 09:49:56 1996
+++ linux/include/linux/hdreg.h	Sat Mar 16 15:00:02 1996
@@ -164,8 +164,12 @@
 #endif	/* CONFIG_BLK_DEV_HD */
 #ifdef CONFIG_BLK_DEV_IDE
 void ide_setup(char *);
+
+#ifdef CONFIG_BLK_DEV_IDE_PCMCIA
 int ide_register(int io_port, int ctl_port, int irq);
-void ide_unregister(int h);
+void ide_unregister(unsigned int);
+#endif  /* CONFIG_BLK_DEV_IDE_PCMCIA */
+
 #endif	/* CONFIG_BLK_DEV_IDE */
 
 #endif  /* __KERNEL__ */
diff -u --recursive --new-file v1.3.74/linux/include/linux/if_packet.h linux/include/linux/if_packet.h
--- v1.3.74/linux/include/linux/if_packet.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/if_packet.h	Sat Mar 16 13:52:11 1996
@@ -0,0 +1,11 @@
+#ifndef __LINUX_IF_PACKET_H
+#define __LINUX_IF_PACKET_H
+
+struct sockaddr_pkt
+{
+	unsigned short spkt_family;
+	unsigned char spkt_device[14];
+	unsigned short spkt_protocol;
+};
+
+#endif
diff -u --recursive --new-file v1.3.74/linux/include/linux/mcdx.h linux/include/linux/mcdx.h
--- v1.3.74/linux/include/linux/mcdx.h	Tue Mar  5 10:11:12 1996
+++ linux/include/linux/mcdx.h	Fri Mar 15 10:58:46 1996
@@ -1,7 +1,7 @@
 /*
  * Definitions for the Mitsumi CDROM interface
  * Copyright (C) 1995 Heiko Schlittermann <heiko@lotte.sax.de>
- * VERSION: 1.7
+ * VERSION: 1.8
  * 
  * 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
@@ -82,12 +82,15 @@
 
 #if MCDX_QUIET == 1
 #define INFO(x)  
-#define WARN(x) warn x
+#define xinfo(fmt, args...)
 #else
 #define INFO(x) warn x
-#define WARN(x) warn x
+#define xinfo(fmt, args...) _warn(fmt, ## args)
 #endif
 
+#define WARN(x) warn x
+#define xwarn(fmt, args...) _warn(fmt, ## args)
+#define _warn warn
 
 #if MCDX_DEBUG == 1
 #define TRACE(x) trace x
diff -u --recursive --new-file v1.3.74/linux/include/linux/personality.h linux/include/linux/personality.h
--- v1.3.74/linux/include/linux/personality.h	Wed Aug  2 13:21:16 1995
+++ linux/include/linux/personality.h	Fri Mar 15 15:22:59 1996
@@ -37,7 +37,7 @@
 	unsigned char pers_low, pers_high;
 	unsigned long * signal_map;
 	unsigned long * signal_invmap;
-	int *use_count;
+	long *use_count;
 	struct exec_domain *next;
 };
 
diff -u --recursive --new-file v1.3.74/linux/include/linux/smp.h linux/include/linux/smp.h
--- v1.3.74/linux/include/linux/smp.h	Fri Feb  9 17:53:10 1996
+++ linux/include/linux/smp.h	Sat Mar 16 13:52:11 1996
@@ -14,6 +14,7 @@
 extern void smp_callin(void);			/* Processor call in. Must hold processors until .. */
 extern void smp_commence(void);			/* Multiprocessors may now schedule */
 extern int smp_num_cpus;
+extern int smp_top_cpu;				/* Top CPU number  */
 extern int smp_threads_ready;			/* True once the per process idle is forked */
 #ifdef __SMP_PROF__
 extern volatile unsigned long smp_spins[NR_CPUS];	/* count of interrupt spins */
@@ -47,6 +48,7 @@
  
 #define smp_num_cpus			1
 #define smp_processor_id()		0
+#define smp_top_cpu			0
 #define smp_message_pass(t,m,d,w)	
 #define smp_threads_ready		1
 #define kernel_lock()
diff -u --recursive --new-file v1.3.74/linux/include/linux/tty.h linux/include/linux/tty.h
--- v1.3.74/linux/include/linux/tty.h	Sat Feb 17 16:02:54 1996
+++ linux/include/linux/tty.h	Sat Mar 16 15:00:02 1996
@@ -52,7 +52,6 @@
 
 #define ORIG_X			(screen_info.orig_x)
 #define ORIG_Y			(screen_info.orig_y)
-#define ORIG_VIDEO_PAGE		(screen_info.orig_video_page)
 #define ORIG_VIDEO_MODE		(screen_info.orig_video_mode)
 #define ORIG_VIDEO_COLS 	(screen_info.orig_video_cols)
 #define ORIG_VIDEO_EGA_BX	(screen_info.orig_video_ega_bx)
diff -u --recursive --new-file v1.3.74/linux/include/net/route.h linux/include/net/route.h
--- v1.3.74/linux/include/net/route.h	Sat Feb 17 16:02:55 1996
+++ linux/include/net/route.h	Sat Mar 16 15:00:09 1996
@@ -192,6 +192,7 @@
 extern int		rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
 extern int		ip_rt_ioctl(unsigned int cmd, void *arg);
 extern int		ip_rt_new(struct rtentry *rt);
+extern int		ip_rt_kill(struct rtentry *rt);
 extern void		ip_rt_check_expire(void);
 extern void		ip_rt_advice(struct rtable **rp, int advice);
 
diff -u --recursive --new-file v1.3.74/linux/include/net/tcp.h linux/include/net/tcp.h
--- v1.3.74/linux/include/net/tcp.h	Tue Feb 20 14:37:28 1996
+++ linux/include/net/tcp.h	Sat Mar 16 18:21:19 1996
@@ -179,25 +179,33 @@
  *  
  *	1. The window can never be shrunk once it is offered (RFC 793)
  *	2. We limit memory per socket
+ *	3. No reason to raise the window if the other side has
+ *	   lots of room to play with.
  */
 
 static __inline__ unsigned short tcp_raise_window(struct sock *sk)
 {
-	long free_space = sock_rspace(sk);
+	long free_space;
 	long window;
 
-	if (free_space > 1024)
-		free_space &= ~0x3FF; /* make free space a multiple of 1024 */
 
-	if(sk->window_clamp)
-		free_space = min(sk->window_clamp, free_space);
- 
 	/* 
          * compute the actual window i.e. 
-         * old_window - received_bytes_on_that_win 
+         * old_window - received_bytes_on_that_win.
+	 *
+	 * Don't raise the window if we have lots left:
+	 * that only results in unnecessary packets.
 	 */
+	window = sk->window - (sk->acked_seq - sk->lastwin_seq);	
+	if (window >= MAX_WINDOW/2)
+		return 0;
 
-	window = sk->window - (sk->acked_seq - sk->lastwin_seq);
+	free_space = sock_rspace(sk);
+	if (free_space > 1024)
+		free_space &= ~0x3FF; /* make free space a multiple of 1024 */
+
+	if(sk->window_clamp)
+		free_space = min(sk->window_clamp, free_space);
 
 	if (sk->mss == 0)
 		sk->mss = sk->mtu;
diff -u --recursive --new-file v1.3.74/linux/init/main.c linux/init/main.c
--- v1.3.74/linux/init/main.c	Fri Mar 15 16:03:21 1996
+++ linux/init/main.c	Sat Mar 16 13:52:09 1996
@@ -60,6 +60,7 @@
 extern long pci_init(long, long);
 extern void sysctl_init(void);
 
+extern void no_scroll(char *str, int *ints);
 extern void swap_setup(char *str, int *ints);
 extern void buff_setup(char *str, int *ints);
 extern void panic_setup(char *str, int *ints);
@@ -222,6 +223,7 @@
 	{ "swap=", swap_setup },
 	{ "buff=", buff_setup },
 	{ "panic=", panic_setup },
+	{ "no-scroll", no_scroll },
 #ifdef CONFIG_BUGi386
 	{ "no-hlt", no_halt },
 	{ "no387", no_387 },
@@ -794,6 +796,7 @@
 	real_root_dev = ROOT_DEV;
 	real_root_mountflags = root_mountflags;
 	if (initrd_start && mount_initrd) root_mountflags &= ~MS_RDONLY;
+	else mount_initrd =0;
 #endif
 	setup();
 
@@ -823,7 +826,7 @@
 
 #ifdef CONFIG_BLK_DEV_INITRD
 	root_mountflags = real_root_mountflags;
-	if (ROOT_DEV != real_root_dev && ROOT_DEV == MKDEV(RAMDISK_MAJOR,0)) {
+	if (mount_initrd && ROOT_DEV != real_root_dev && ROOT_DEV == MKDEV(RAMDISK_MAJOR,0)) {
 		int error;
 
 		pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
diff -u --recursive --new-file v1.3.74/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.3.74/linux/kernel/ksyms.c	Wed Mar 13 10:09:24 1996
+++ linux/kernel/ksyms.c	Sat Mar 16 13:52:16 1996
@@ -92,6 +92,7 @@
 extern char *get_options(char *str, int *ints);
 extern void set_device_ro(int dev,int flag);
 extern struct file_operations * get_blkfops(unsigned int);
+extern void blkdev_release(struct inode * inode);
 
 extern void *sys_call_table;
 
@@ -227,6 +228,8 @@
 	X(bmap),
 	X(sync_dev),
 	X(get_blkfops),
+	X(blkdev_open),
+	X(blkdev_release),
 #ifdef CONFIG_SERIAL	
 	/* Module creation of serial units */
 	X(register_serial),
@@ -469,9 +472,13 @@
 	X(tr_type_trans),
 #endif
 
-#ifdef CONFIG_BLK_DEV_IDE
+#ifdef CONFIG_BLK_DEV_IDE_PCMCIA
 	X(ide_register),
 	X(ide_unregister),
+#endif
+
+#ifdef CONFIG_BLK_DEV_MD
+	X(disk_name),	/* for md.c */
 #endif
  	
 	/* bimfm_aout */
diff -u --recursive --new-file v1.3.74/linux/kernel/printk.c linux/kernel/printk.c
--- v1.3.74/linux/kernel/printk.c	Tue Mar  5 10:11:13 1996
+++ linux/kernel/printk.c	Fri Mar 15 10:23:04 1996
@@ -34,7 +34,7 @@
 
 /* We show everything that is MORE important than this.. */
 #define MINIMUM_CONSOLE_LOGLEVEL 5 /* Minimum loglevel we let people use */
-#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_NOTICE */
+#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */
 
 unsigned long log_size = 0;
 struct wait_queue * log_wait = NULL;
diff -u --recursive --new-file v1.3.74/linux/kernel/sched.c linux/kernel/sched.c
--- v1.3.74/linux/kernel/sched.c	Wed Mar 13 10:09:24 1996
+++ linux/kernel/sched.c	Sat Mar 16 13:52:11 1996
@@ -27,6 +27,7 @@
 #include <linux/mm.h>
 #include <linux/smp.h>
 
+#include <asm/smp.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/segment.h>
@@ -101,6 +102,9 @@
 
 static inline void add_to_runqueue(struct task_struct * p)
 {
+#ifdef __SMP__
+	int cpu=smp_processor_id();
+#endif	
 #if 1	/* sanity tests */
 	if (p->next_run || p->prev_run) {
 		printk("task already on run-queue\n");
@@ -115,17 +119,34 @@
 	init_task.prev_run = p;
 #ifdef __SMP__
 	/* this is safe only if called with cli()*/
-	while(set_bit(31,&smp_process_available))
-		while(test_bit(31,&smp_process_available));
+	while(set_bit(31,&smp_process_available));
+#if 0	
+	{
+		while(test_bit(31,&smp_process_available))
+		{
+			if(clear_bit(cpu,&smp_invalidate_needed))
+			{
+				local_invalidate();
+				set_bit(cpu,&cpu_callin_map[0]);
+			}
+		}
+	}
+#endif	
 	smp_process_available++;
 	clear_bit(31,&smp_process_available);
-	if ((0!=p->pid) && smp_threads_ready){
-		int i, found=0;
-		for (i=0;i<smp_num_cpus;i++)
-			if (0==current_set[i]->pid) {
+	if ((0!=p->pid) && smp_threads_ready)
+	{
+		int i;
+		for (i=0;i<=smp_top_cpu;i++)
+		{
+			if (cpu_number_map[i]==-1)
+				continue;
+			if (0==current_set[i]->pid) 
+			{
 				smp_message_pass(i, MSG_RESCHEDULE, 0L, 0);
 				break;
 			}
+		}
 	}
 #endif
 }
@@ -885,14 +906,20 @@
 	}
 #else
 	cpu = smp_processor_id();
-	for (i=0;i<(0==smp_num_cpus?1:smp_num_cpus);i++){
+	for (i=0;i<=smp_top_cpu;i++)
+	{
+		if(cpu_number_map[i]==-1)
+			continue;
 #ifdef __SMP_PROF__
-	if (test_bit(i,&smp_idle_map)) smp_idle_count[i]++;
+		if (test_bit(i,&smp_idle_map)) 
+			smp_idle_count[i]++;
 #endif
 		if (((cpu==i) && user_mode(regs)) ||
-			((cpu!=i) && 0==smp_proc_in_lock[i])) {
+			((cpu!=i) && 0==smp_proc_in_lock[i])) 
+		{
 			current_set[i]->utime++;
-			if (current_set[i]->pid) {
+			if (current_set[i]->pid) 
+			{
 				if (current_set[i]->priority < DEF_PRIORITY)
 					kstat.cpu_nice++;
 				else
diff -u --recursive --new-file v1.3.74/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v1.3.74/linux/kernel/sysctl.c	Wed Mar 13 10:09:24 1996
+++ linux/kernel/sysctl.c	Fri Mar 15 10:21:12 1996
@@ -192,7 +192,7 @@
 				    newval, newlen, root_table, &context);
 		if (context)
 			kfree(context);
-		if (error != ENOTDIR)
+		if (error != -ENOTDIR)
 			return error;
 		tmp = tmp->DLIST_NEXT(ctl_entry);
 	} while (tmp != &root_table_header);
diff -u --recursive --new-file v1.3.74/linux/mm/swapfile.c linux/mm/swapfile.c
--- v1.3.74/linux/mm/swapfile.c	Fri Mar 15 16:03:21 1996
+++ linux/mm/swapfile.c	Sat Mar 16 11:55:40 1996
@@ -270,7 +270,7 @@
 	/*
 	 * Go through process' page directory.
 	 */
-	if (!mm)
+	if (!mm || mm == &init_mm)
 		return 0;
 	vma = mm->mmap;
 	while (vma) {
diff -u --recursive --new-file v1.3.74/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v1.3.74/linux/net/ax25/af_ax25.c	Wed Mar 13 10:09:26 1996
+++ linux/net/ax25/af_ax25.c	Sat Mar 16 13:52:11 1996
@@ -524,7 +524,123 @@
 	}
 
 	return -EINVAL;	/*NOTREACHED */
-}	
+}
+
+/*
+ * dl1bke 960311: set parameters for existing AX.25 connections,
+ *		  includes a KILL command to abort any connection.
+ *		  VERY useful for debugging ;-)
+ */
+
+static int ax25_ctl_ioctl(const unsigned int cmd, const unsigned long arg)
+{
+	struct ax25_ctl_struct ax25_ctl;
+	struct device *dev;
+	ax25_cb *ax25;
+	unsigned long flags;
+	int err;
+	
+	if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_ctl))) != 0)
+		return err;
+
+	memcpy_fromfs(&ax25_ctl, (void *) arg, sizeof(ax25_ctl));
+	
+	if ((dev = ax25rtr_get_dev(&ax25_ctl.port_addr)) == NULL)
+		return -ENODEV;
+
+	if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, dev)) == NULL)
+		return -ENOTCONN;
+
+	switch (ax25_ctl.cmd) {
+		case AX25_KILL:
+#ifdef CONFIG_NETROM
+			nr_link_failed(&ax25->dest_addr, ax25->device);
+#endif
+			ax25_clear_queues(ax25);
+			ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
+				
+			ax25->state = AX25_STATE_0;
+			if (ax25->sk != NULL) {
+				ax25->sk->state = TCP_CLOSE;
+				ax25->sk->err   = ENETRESET;
+				if (!ax25->sk->dead)
+					ax25->sk->state_change(ax25->sk);
+				ax25->sk->dead  = 1;
+			}
+
+			ax25_dama_off(ax25);
+			ax25_set_timer(ax25);
+	  		break;
+	  	case AX25_WINDOW:
+	  		if (ax25->modulus == MODULUS) {
+	  			if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7) 
+	  				return -EINVAL;
+	  		} else {
+	  			if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63) 
+	  				return -EINVAL;
+	  		}
+	  		ax25->window = ax25_ctl.arg;
+	  		break;
+	  	case AX25_T1:
+  			if (ax25_ctl.arg < 1) 
+  				return -EINVAL;
+  			ax25->rtt = (ax25_ctl.arg * PR_SLOWHZ) / 2;
+  			ax25->t1 = ax25_ctl.arg * PR_SLOWHZ;
+  			save_flags(flags); cli();
+  			if (ax25->t1timer > ax25->t1)
+  				ax25->t1timer = ax25->t1;
+  			restore_flags(flags);
+  			break;
+	  	case AX25_T2:
+	  		if (ax25_ctl.arg < 1) 
+	  			return -EINVAL;
+	  		save_flags(flags); cli();
+	  		ax25->t2 = ax25_ctl.arg * PR_SLOWHZ;
+	  		if (ax25->t2timer > ax25->t2)
+	  			ax25->t2timer = ax25->t2;
+	  		restore_flags(flags);
+	  		break;
+	  	case AX25_N2:
+	  		if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31) 
+	  			return -EINVAL;
+	  		ax25->n2count = 0;
+	  		ax25->n2 = ax25_ctl.arg;
+	  		break;
+	  	case AX25_T3:
+	  		if (ax25_ctl.arg < 1) 
+	  			return -EINVAL;
+	  		save_flags(flags); cli();
+	  		ax25->t3 = ax25_ctl.arg * PR_SLOWHZ;
+	  		if (ax25->t3timer != 0)
+	  			ax25->t3timer = ax25->t3;
+	  		restore_flags(flags);
+	  		break;
+	  	case AX25_IDLE:
+	  		if (ax25_ctl.arg < 1) 
+	  			return -EINVAL;
+	  		if (ax25->idle == 0)
+	  			return 0;
+			save_flags(flags); cli();
+	  		ax25->idle = ax25_ctl.arg * PR_SLOWHZ * 60;
+	  		if (ax25->idletimer != 0)
+	  			ax25->idletimer = ax25->idle;
+	  		restore_flags(flags);
+	  		break;
+	  	case AX25_PACLEN:
+	  		if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535) 
+	  			return -EINVAL;
+
+	  		if (ax25_ctl.arg > 256) /* we probably want this */
+	  			printk("ax25_ctl_ioctl(): Warning --- huge paclen %d", (int) ax25_ctl.arg);
+	  		ax25->paclen = ax25_ctl.arg;
+	  		break;
+	  	default:
+	  		return -EINVAL;
+	  }
+	  
+	  return 0;
+}
+
 
 /*
  * Create an empty AX.25 control block.
@@ -1501,7 +1617,9 @@
 	/*
 	 *	Send the frame to the AX.25 auto-router
 	 */
+#ifdef notdef	/* dl1bke 960310 */
 	ax25_rt_rx_frame(&src, dev, &dp);
+#endif
 	
 	/*
 	 *	Ours perhaps ?
@@ -1532,16 +1650,19 @@
 
 			if (dev == dev_out && (ax25_dev_get_value(dev, AX25_VALUES_DIGI) & AX25_DIGI_INBAND) == 0) {
 				kfree_skb(skb, FREE_READ);
-				return 0;	/* Hey, Alan: look what you're doing below! You forgot this return! */
+				return 0;
 			}
 
+			ax25_rt_rx_frame(&src, dev, &dp);
+
 			build_ax25_addr(skb->data, &src, &dest, &dp, type, MODULUS);
 #ifdef CONFIG_FIREWALL
 			if (call_fw_firewall(PF_AX25, skb, skb->data) != FW_ACCEPT) {
 				kfree_skb(skb, FREE_READ);
 				return 0;
 			}
-#endif						
+#endif
+
 			skb->arp = 1;
 			ax25_queue_xmit(skb, dev_out, SOPRI_NORMAL);
 		} else {
@@ -1581,12 +1702,14 @@
 		switch (skb->data[1]) {
 #ifdef CONFIG_INET		
 			case AX25_P_IP:
+				ax25_rt_rx_frame(&src, dev, &dp);
 				skb_pull(skb,2);		/* drop PID/CTRL */
 				ax25_ip_mode_set(&src, dev, 'D');
 				ip_rcv(skb, dev, ptype);	/* Note ptype here is the wrong one, fix me later */
 				break;
 
 			case AX25_P_ARP:
+				ax25_rt_rx_frame(&src, dev, &dp);
 				skb_pull(skb,2);
 				arp_rcv(skb, dev, ptype);	/* Note ptype here is wrong... */
 				break;
@@ -1597,6 +1720,7 @@
 					if (sk->rmem_alloc >= sk->rcvbuf) {
 						kfree_skb(skb, FREE_READ);
 					} else {
+						ax25_rt_rx_frame(&src, dev, &dp);
 						/*
 						 *	Remove the control and PID.
 						 */
@@ -1640,6 +1764,7 @@
 		 *	free it immediately. This routine itself wakes the user context layers so we
 		 *	do no further work
 		 */
+		ax25_rt_rx_frame(&src, dev, &dp);
 		if (ax25_process_rx_frame(ax25, skb, type, dama) == 0)
 			kfree_skb(skb, FREE_READ);
 
@@ -1665,6 +1790,7 @@
 	/* b) received SABM(E) */
 	
 	if ((sk = ax25_find_listener(&dest, dev, SOCK_SEQPACKET)) != NULL) {
+		ax25_rt_rx_frame(&src, dev, &dp);
 		if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, dev)) == NULL) {
 			if (mine)
 				ax25_return_dm(dev, &src, &dest, &dp);
@@ -1688,6 +1814,8 @@
 			kfree_skb(skb, FREE_READ);
 			return 0;
 		}
+		
+		ax25_rt_rx_frame(&src, dev, &dp);
 
 		if ((ax25 = ax25_create_cb()) == NULL) {
 			ax25_return_dm(dev, &src, &dest, &dp);
@@ -1698,8 +1826,10 @@
 		ax25_fillin_cb(ax25, dev);
 		ax25->idletimer = ax25->idle = ax25_dev_get_value(ax25->device, AX25_VALUES_IDLE);
 #else
-		if (mine)
+		if (mine) {
+			ax25_rt_rx_frame(&src, dev, &dp);
 			ax25_return_dm(dev, &src, &dest, &dp);
+		}
 
 		kfree_skb(skb, FREE_READ);
 		return 0;
@@ -2104,9 +2234,15 @@
 
 		case SIOCADDRT:
 		case SIOCDELRT:
+		case SIOCAX25OPTRT:
 			if (!suser())
 				return -EPERM;
 			return ax25_rt_ioctl(cmd, (void *)arg);
+			
+		case SIOCAX25CTLCON:
+			if (!suser())
+				return -EPERM;
+			return ax25_ctl_ioctl(cmd, arg);
 
 		case SIOCGIFADDR:
 		case SIOCSIFADDR:
@@ -2128,6 +2264,7 @@
 	return(0);
 }
 
+
 static int ax25_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
 {
 	ax25_cb *ax25;
@@ -2136,7 +2273,8 @@
 	int len = 0;
 	off_t pos = 0;
 	off_t begin = 0;
-  
+	int idletimer;
+
 	cli();
 
 	len += sprintf(buffer, "dest_addr src_addr  dev  st  vs  vr  va    t1     t2     t3      idle   n2  rtt wnd paclen   dama Snd-Q Rcv-Q\n");
@@ -2146,6 +2284,9 @@
 			devname = "???";
 		else
 			devname = dev->name;
+			
+		idletimer = ax25->idletimer / (PR_SLOWHZ * 60);
+		idletimer += (ax25->idletimer && ax25->idletimer < ax25->idle)? 1:0;
 
 		len += sprintf(buffer + len, "%-9s ",
 			ax2asc(&ax25->dest_addr));
@@ -2159,7 +2300,7 @@
 			ax25->t2      / PR_SLOWHZ,
 			ax25->t3timer / PR_SLOWHZ,
 			ax25->t3      / PR_SLOWHZ,
-			(ax25->idletimer / (PR_SLOWHZ*60))+1,
+			idletimer,
 			ax25->idle      / (PR_SLOWHZ*60),
 			ax25->n2count, ax25->n2,
 			ax25->rtt     / PR_SLOWHZ,
@@ -2427,10 +2568,27 @@
 
 	if (bp[16] == AX25_P_IP) {
 		mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev);
-		if (mode == 'V' || mode == 'v' || (mode == ' ' && ax25_dev_get_value(dev, AX25_VALUES_IPDEFMODE) == 'V')) {
-/*			skb_device_unlock(skb); *//* Don't unlock - it might vanish.. TCP will respond correctly to this lock holding */
-			skb_pull(skb, AX25_HEADER_LEN - 1);	/* Keep PID */
-			ax25_send_frame(skb, (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev);
+		if (mode == 'V' || mode == 'v' || (mode == ' ' && ax25_dev_get_value(dev, AX25_VALUES_IPDEFMODE) == 'V')) 
+		{
+			/*
+			 *	This is a workaround to try and keep the device locking
+			 *	straight until skb->free=0 is abolished post 1.4.
+			 *
+			 *	We clone the buffer and release the original thereby
+			 *	keeping it straight
+			 *
+			 *	Note: we report 1 back so the caller will
+			 *	not feed the frame direct to the physical device
+			 *	We don't want that to happen. (It won't be upset
+			 *	as we have pulled the frame from the queue by
+			 *	freeing it).
+			 */
+			struct sk_buff *ourskb=skb_clone(skb, GFP_ATOMIC);
+			dev_kfree_skb(skb, FREE_READ);
+			if(ourskb==NULL)
+				return 1;			
+			skb_pull(ourskb, AX25_HEADER_LEN - 1);	/* Keep PID */
+			ax25_send_frame(ourskb, (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev);
 			return 1;
 		}
 	}
diff -u --recursive --new-file v1.3.74/linux/net/ax25/ax25_in.c linux/net/ax25/ax25_in.c
--- v1.3.74/linux/net/ax25/ax25_in.c	Wed Mar 13 10:09:26 1996
+++ linux/net/ax25/ax25_in.c	Sat Mar 16 13:52:11 1996
@@ -303,7 +303,6 @@
 			ax25_send_control(ax25, UA, pf, C_RESPONSE);
 			if (ax25->dama_slave) {
 				ax25->state = AX25_STATE_0;
-				ax25->dama_slave = 0;
 				ax25_dama_off(ax25);
 
 				if (ax25->sk != NULL) {
@@ -319,7 +318,6 @@
 		case UA:
 			if (pf) {
 				ax25->state = AX25_STATE_0;
-				ax25->dama_slave = 0;
 				ax25_dama_off(ax25);
 
 				if (ax25->sk != NULL) {
@@ -335,7 +333,6 @@
 		case DM:
 			if (pf) {
 				ax25->state = AX25_STATE_0;
-				ax25->dama_slave = 0;
 				ax25_dama_off(ax25);
 					
 				if (ax25->sk != NULL) {
@@ -414,7 +411,6 @@
 			ax25_send_control(ax25, UA, pf, C_RESPONSE);
 			ax25->t3timer = 0;
 			ax25->state   = AX25_STATE_0;
-			ax25->dama_slave = 0;
 			ax25_dama_off(ax25);
 			
 			if (ax25->sk != NULL) {
@@ -430,7 +426,6 @@
 			ax25_clear_queues(ax25);
 			ax25->t3timer = 0;
 			ax25->state   = AX25_STATE_0;
-			ax25->dama_slave = 0;
 			ax25_dama_off(ax25);
 			if (ax25->sk) {
 				ax25->sk->state = TCP_CLOSE;
@@ -612,7 +607,6 @@
 			ax25_send_control(ax25, UA, pf, C_RESPONSE);
 			ax25->t3timer = 0;
 			ax25->state   = AX25_STATE_0;
-			ax25->dama_slave = 0;
 			ax25_dama_off(ax25);
 			
 			if (ax25->sk != NULL) {
@@ -628,7 +622,6 @@
 			ax25_clear_queues(ax25);
 			ax25->t3timer = 0;
 			ax25->state   = AX25_STATE_0;
-			ax25->dama_slave = 0;
 			ax25_dama_off(ax25);
 			
 			if (ax25->sk != NULL) {
diff -u --recursive --new-file v1.3.74/linux/net/ax25/ax25_route.c linux/net/ax25/ax25_route.c
--- v1.3.74/linux/net/ax25/ax25_route.c	Wed Mar 13 10:09:26 1996
+++ linux/net/ax25/ax25_route.c	Sat Mar 16 13:52:11 1996
@@ -34,6 +34,9 @@
  *			Joerg(DL1BKE)	changed routing for IP datagram and VC to use a default
  *					route if available. Does not overwrite default routes
  *					on route-table overflow anymore.
+ *			Joerg(DL1BKE)	fixed AX.25 routing of IP datagram and VC, new ioctl()
+ *					"SIOCAX25OPTRT" to set IP mode and a 'permanent' flag
+ *					on routes.
  */
  
 #include <linux/config.h>
@@ -60,7 +63,7 @@
 #include <linux/mm.h>
 #include <linux/interrupt.h>
 
-#define	AX25_ROUTE_MAX	40
+#define	AX25_ROUTE_MAX	128
 
 static struct ax25_route {
 	struct ax25_route *next;
@@ -70,6 +73,7 @@
 	struct timeval stamp;
 	int n;
 	char ip_mode;
+	char perm;
 } *ax25_route = NULL;
 
 static struct ax25_dev {
@@ -78,13 +82,36 @@
 	unsigned short values[AX25_MAX_VALUES];
 } *ax25_device = NULL;
 
+static struct ax25_route * ax25_find_route(ax25_address *addr);
+
 /*
- * FIXME: heard and routing table should be two lists. The routing table
- *        should be updated by connects only. (WAMPES way of doing it)
+ * small macro to drop non-digipeated digipeaters and reverse path
+ */
+ 
+static inline void ax25_route_invert(ax25_digi *in, ax25_digi *out)
+{
+	int k;
+	for (k = 0; k < in->ndigi; k++)
+		if (!in->repeated[k])
+			break;
+	in->ndigi = k;
+	ax25_digi_invert(in, out);
+
+}
+
+/*
+ * dl1bke 960310: new behaviour:
+ *
+ * * try to find an existing route to 'src', if found:
+ *   - if the route was added manually don't adjust the timestamp
+ *   - if this route is 'permanent' just do some statistics and return
+ *   - overwrite device and digipeater path
+ * * no existing route found:
+ *   - try to alloc a new entry
+ *   - overwrite the oldest, not manually added entry if this fails.
+ *
+ * * updated on receiption of frames directed to us _only_
  *
- *        routing table should accept a new route to the same destination
- *        if the old one was added manually. Nevertheless we should still
- *        be able to add permanent routes. (dl1bke)
  */
 
 void ax25_rt_rx_frame(ax25_address *src, struct device *dev, ax25_digi *digi)
@@ -102,11 +129,30 @@
 		if (count == 0 || oldest->stamp.tv_sec == 0 || (ax25_rt->stamp.tv_sec != 0 && ax25_rt->stamp.tv_sec < oldest->stamp.tv_sec))
 			oldest = ax25_rt;
 		
-		if (ax25cmp(&ax25_rt->callsign, src) == 0 && ax25_rt->dev == dev) {
+		if (ax25cmp(&ax25_rt->callsign, src) == 0) {
 			if (ax25_rt->stamp.tv_sec != 0)
 				ax25_rt->stamp = xtime;
-			ax25_rt->n++;
-			return;			
+
+			if (ax25_rt->perm == AX25_RT_PERMANENT) {
+				ax25_rt->n++;
+				return;
+			}
+			
+			ax25_rt->dev = dev;
+			if (digi == NULL) {
+				/* drop old digipeater list */
+				if (ax25_rt->digipeat != NULL) {
+					kfree_s(ax25_rt->digipeat, sizeof(ax25_digi));
+					ax25_rt->digipeat = NULL;
+				}
+				return;
+			}
+			
+			if (ax25_rt->digipeat == NULL && (ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
+					return;
+			
+			ax25_route_invert(digi, ax25_rt->digipeat);
+			return;
 		}
 
 		count++;
@@ -122,20 +168,23 @@
 		if ((ax25_rt = (struct ax25_route *)kmalloc(sizeof(struct ax25_route), GFP_ATOMIC)) == NULL)
 			return;		/* No space */
 	}
-
+	
 	ax25_rt->callsign = *src;
 	ax25_rt->dev      = dev;
 	ax25_rt->digipeat = NULL;
 	ax25_rt->stamp    = xtime;
 	ax25_rt->n        = 1;
 	ax25_rt->ip_mode  = ' ';
+	ax25_rt->perm	  = AX25_RT_DYNAMIC;
 
 	if (digi != NULL) {
 		if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
 			kfree_s(ax25_rt, sizeof(struct ax25_route));
 			return;
 		}
-		*ax25_rt->digipeat = *digi;
+		
+		ax25_route_invert(digi, ax25_rt->digipeat);
+		/* used to be: *ax25_rt->digipeat = *digi; */
 	}
 
 	if (ax25_rt != oldest) {
@@ -183,6 +232,7 @@
 	unsigned long flags;
 	struct ax25_route *s, *t, *ax25_rt;
 	struct ax25_routes_struct route;
+	struct ax25_route_opt_struct rt_option;
 	struct device *dev;
 	int i, err;
 
@@ -223,6 +273,7 @@
 			ax25_rt->stamp.tv_sec = 0;
 			ax25_rt->n            = 0;
 			ax25_rt->ip_mode      = ' ';
+			ax25_rt->perm	      = AX25_RT_DYNAMIC;
 			if (route.digi_count != 0) {
 				if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
 					kfree_s(ax25_rt, sizeof(struct ax25_route));
@@ -272,6 +323,40 @@
 				}
 			}
 			break;
+		case SIOCAX25OPTRT:
+			if ((err = verify_area(VERIFY_READ, arg, sizeof(rt_option))) != 0)
+				return err;
+			memcpy_fromfs(&rt_option, arg, sizeof(rt_option));
+			if ((dev = ax25rtr_get_dev(&rt_option.port_addr)) == NULL)
+				return -EINVAL;
+			ax25_rt = ax25_route;
+			while (ax25_rt != NULL) {
+				if (ax25_rt->dev == dev && ax25cmp(&rt_option.dest_addr, &ax25_rt->callsign) == 0) {
+					switch(rt_option.cmd) {
+						case AX25_SET_RT_PERMANENT:
+							ax25_rt->perm = (char) rt_option.arg;
+							ax25_rt->stamp.tv_sec = 0;
+							break;
+						case AX25_SET_RT_IPMODE:
+							switch (rt_option.arg) {
+								case AX25_RT_IPMODE_DEFAULT:
+									ax25_rt->ip_mode = ' ';
+									break;
+								case AX25_RT_IPMODE_DATAGRAM:
+									ax25_rt->ip_mode = 'D';
+									break;
+								case AX25_RT_IPMODE_VC:
+									ax25_rt->ip_mode = 'V';
+									break;
+								default:
+									return -EINVAL;
+							}
+							break;
+					}
+				}
+				ax25_rt = ax25_rt->next;
+			}
+			break;
 	}
 
 	return 0;
@@ -288,7 +373,7 @@
   
 	cli();
 
-	len += sprintf(buffer, "callsign  dev  count time      mode digipeaters\n");
+	len += sprintf(buffer, "callsign  dev  count time      mode F digipeaters\n");
 
 	for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
 		if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0)
@@ -314,6 +399,20 @@
 				len += sprintf(buffer + len, "    *");
 				break;
 		}
+		
+		switch (ax25_rt->perm) {
+			case AX25_RT_DYNAMIC:
+				if (ax25_rt->stamp.tv_sec == 0)
+					len += sprintf(buffer + len, " M");
+				else
+					len += sprintf(buffer + len, "  ");
+				break;
+			case AX25_RT_PERMANENT:
+				len += sprintf(buffer + len, " P");
+				break;
+			default:
+				len += sprintf(buffer + len, " ?");
+		}
 
 		if (ax25_rt->digipeat != NULL)
 			for (i = 0; i < ax25_rt->digipeat->ndigi; i++)
@@ -378,15 +477,15 @@
 }
 
 /*
- *	Find which interface to use.
+ *	Find AX.25 route
  */
-int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
+ 
+static struct ax25_route * ax25_find_route(ax25_address *addr)
 {
 	struct ax25_route *ax25_spe_rt = NULL;
 	struct ax25_route *ax25_def_rt = NULL;
 	struct ax25_route *ax25_rt;
-	ax25_address *call;
-
+	
 	/*
 	 *	Bind to the physical interface we heard them on, or the default
 	 *	route if non is found;
@@ -399,12 +498,41 @@
 	}
 
 	if (ax25_spe_rt != NULL)
-		ax25_rt = ax25_spe_rt;
-	else if (ax25_def_rt != NULL)
-		ax25_rt = ax25_def_rt;
-	else
-		return -EHOSTUNREACH;
+		return ax25_spe_rt;
+		
+	return ax25_def_rt;
+}
 
+/*
+ *	Adjust path: If you specify a default route and want to connect
+ *      a target on the digipeater path but w/o having a special route
+ *	set before, the path has to be truncated from your target on.
+ */
+ 
+static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat)
+{
+	int k;
+	
+	for (k = 0; k < digipeat->ndigi; k++) {
+		if (ax25cmp(addr, &digipeat->calls[k]) == 0)
+			break;
+	}
+	
+	digipeat->ndigi = k;
+}
+ 
+
+/*
+ *	Find which interface to use.
+ */
+int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
+{
+	struct ax25_route *ax25_rt;
+	ax25_address *call;
+
+	if ((ax25_rt = ax25_find_route(addr)) == NULL)
+		return -EHOSTUNREACH;
+		
 	if ((call = ax25_findbyuid(current->euid)) == NULL) {
 		if (ax25_uid_policy && !suser())
 			return -EPERM;
@@ -419,6 +547,7 @@
 		if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
 			return -ENOMEM;
 		*ax25->digipeat = *ax25_rt->digipeat;
+		ax25_adjust_path(addr, ax25->digipeat);
 	}
 
 	if (ax25->sk != NULL)
@@ -435,13 +564,7 @@
 {
 	struct ax25_route *ax25_rt;
 	
-	for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
-		if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL)
-			break;
-		
-		if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL)
-			break;
-	}
+	ax25_rt = ax25_find_route(addr);
 	
 	if (ax25_rt == NULL || ax25_rt->digipeat == NULL)
 		return;
@@ -451,30 +574,25 @@
 
 	ax25->device = ax25_rt->dev;
 	*ax25->digipeat = *ax25_rt->digipeat;
+	ax25_adjust_path(addr, ax25->digipeat);
 }
 
 void ax25_dg_build_path(struct sk_buff *skb, ax25_address *addr, struct device *dev)
 {
 	struct ax25_route *ax25_rt;
+	ax25_digi digipeat;
 	ax25_address src, dest;
 	unsigned char *bp;
 	int len;
 	
-	/*
-	 * dl1bke 960301: use the default route if available
-	 *
-	 */
-	
-	for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
-		if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL)
-			break;
-		if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL)
-			break;
-	}
-	
-	
-	if (ax25_rt == NULL ||ax25_rt->digipeat == NULL)
+
+	ax25_rt = ax25_find_route(addr);
+	if (ax25_rt == NULL || ax25_rt->digipeat == NULL)
 		return;
+		
+	digipeat = *ax25_rt->digipeat;
+	
+	ax25_adjust_path(addr, &digipeat);
 
 	len = ax25_rt->digipeat->ndigi * AX25_ADDR_LEN;
 		
@@ -485,6 +603,7 @@
 
 	memcpy(&dest, skb->data + 1, AX25_ADDR_LEN);
 	memcpy(&src,  skb->data + 8, AX25_ADDR_LEN);
+	
 
 	bp = skb_push(skb, len);
 	*bp++ = 0x00;		/* KISS Data */
diff -u --recursive --new-file v1.3.74/linux/net/ax25/ax25_subr.c linux/net/ax25/ax25_subr.c
--- v1.3.74/linux/net/ax25/ax25_subr.c	Wed Mar 13 10:09:26 1996
+++ linux/net/ax25/ax25_subr.c	Sat Mar 16 13:52:11 1996
@@ -513,9 +513,7 @@
 
 void ax25_dama_on(ax25_cb *ax25)
 {
-	int count = ax25_dev_is_dama_slave(ax25->device);
-	
-	if (count == 0) {
+	if (ax25_dev_is_dama_slave(ax25->device) == 0) {
 		if (ax25->sk != NULL && ax25->sk->debug)
 			printk("ax25_dama_on: DAMA on\n");
 		ax25_kiss_cmd(ax25, 5, 1);
@@ -524,9 +522,11 @@
 
 void ax25_dama_off(ax25_cb *ax25)
 {
-	int count = ax25_dev_is_dama_slave(ax25->device);
-	
-	if (count == 0) {
+	if (ax25->dama_slave == 0)
+		return;
+
+	ax25->dama_slave = 0;
+	if (ax25_dev_is_dama_slave(ax25->device) == 0) {
 		if (ax25->sk != NULL && ax25->sk->debug)
 			printk("ax25_dama_off: DAMA off\n");
 		ax25_kiss_cmd(ax25, 5, 0);
diff -u --recursive --new-file v1.3.74/linux/net/ax25/ax25_timer.c linux/net/ax25/ax25_timer.c
--- v1.3.74/linux/net/ax25/ax25_timer.c	Wed Mar 13 10:09:26 1996
+++ linux/net/ax25/ax25_timer.c	Sat Mar 16 13:52:11 1996
@@ -159,7 +159,8 @@
 					ax25->sk->state_change(ax25->sk);
 				ax25->sk->dead  = 1;
 			}
-			
+
+			ax25_reset_timer(ax25);
 			return;
 		}
 		
@@ -191,7 +192,6 @@
 
 		ax25->t3timer = 0;
 		ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
-		ax25->state   = AX25_STATE_2;
 
 		if (ax25->sk != NULL)
 		{
diff -u --recursive --new-file v1.3.74/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v1.3.74/linux/net/core/skbuff.c	Wed Feb 14 14:37:20 1996
+++ linux/net/core/skbuff.c	Sat Mar 16 13:52:11 1996
@@ -12,6 +12,15 @@
  *		Alan Cox	:	destructor hook for AF_UNIX etc.
  *		Linus Torvalds	:	Better skb_clone.
  *		Alan Cox	:	Added skb_copy.
+ *		Alan Cox	:	Added all the changed routines Linus
+ *					only put in the headers
+ *		Ray VanTassle	:	Fixed --skb->lock in free
+ *
+ *	TO FIX:
+ *		The __skb_ routines ought to check interrupts are disabled
+ *	when called, and bitch like crazy if not. Unfortunately I don't think
+ *	we currently have a portable way to check if interrupts are off - 
+ *	Linus ???
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -20,9 +29,7 @@
  */
 
 /*
- *	Note: There are a load of cli()/sti() pairs protecting the net_memory type
- *	variables. Without them for some reason the ++/-- operators do not come out
- *	atomic. Also with gcc 2.4.5 these counts can come out wrong anyway - use 2.5.8!!
+ *	The functions in this file will not compile correctly with gcc 2.4.x
  */
 
 #include <linux/config.h>
@@ -224,6 +231,26 @@
 	restore_flags(flags);
 }
 
+void __skb_queue_head(struct sk_buff_head *list_,struct sk_buff *newsk)
+{
+	struct sk_buff *list = (struct sk_buff *)list_;
+
+
+	IS_SKB(newsk);
+	IS_SKB_HEAD(list);
+	if (newsk->next || newsk->prev)
+		printk("Suspicious queue head: sk_buff on list!\n");
+
+	newsk->next = list->next;
+	newsk->prev = list;
+
+	newsk->next->prev = newsk;
+	newsk->prev->next = newsk;
+	newsk->list = list_;
+	list_->qlen++;
+
+}
+
 /*
  *	Insert an sk_buff at the end of a list.
  */
@@ -252,6 +279,26 @@
 	restore_flags(flags);
 }
 
+void __skb_queue_tail(struct sk_buff_head *list_, struct sk_buff *newsk)
+{
+	unsigned long flags;
+	struct sk_buff *list = (struct sk_buff *)list_;
+
+	if (newsk->next || newsk->prev)
+		printk("Suspicious queue tail: sk_buff on list!\n");
+	IS_SKB(newsk);
+	IS_SKB_HEAD(list);
+
+	newsk->next = list;
+	newsk->prev = list->prev;
+
+	newsk->next->prev = newsk;
+	newsk->prev->next = newsk;
+	
+	newsk->list = list_;
+	list_->qlen++;
+}
+
 /*
  *	Remove an sk_buff from a list. This routine is also interrupt safe
  *	so you can grab read and free buffers as another process adds them.
@@ -288,6 +335,30 @@
 	return result;
 }
 
+struct sk_buff *__skb_dequeue(struct sk_buff_head *list_)
+{
+	struct sk_buff *result;
+	struct sk_buff *list = (struct sk_buff *)list_;
+
+	IS_SKB_HEAD(list);
+
+	result = list->next;
+	if (result == list) {
+		return NULL;
+	}
+
+	result->next->prev = list;
+	list->next = result->next;
+
+	result->next = NULL;
+	result->prev = NULL;
+	list_->qlen--;
+	result->list = NULL;
+	
+	IS_SKB(result);
+	return result;
+}
+
 /*
  *	Insert a packet before another one in a list.
  */
@@ -316,6 +387,29 @@
 }
 
 /*
+ *	Insert a packet before another one in a list.
+ */
+
+void __skb_insert(struct sk_buff *old, struct sk_buff *newsk)
+{
+	IS_SKB(old);
+	IS_SKB(newsk);
+
+	if(!old->next || !old->prev)
+		printk("insert before unlisted item!\n");
+	if(newsk->next || newsk->prev)
+		printk("inserted item is already on a list.\n");
+
+	newsk->next = old;
+	newsk->prev = old->prev;
+	old->prev = newsk;
+	newsk->prev->next = newsk;
+	newsk->list = old->list;
+	newsk->list->qlen++;
+
+}
+
+/*
  *	Place a packet after a given packet in a list.
  */
 void skb_append(struct sk_buff *old, struct sk_buff *newsk)
@@ -343,6 +437,25 @@
 	restore_flags(flags);
 }
 
+void __skb_append(struct sk_buff *old, struct sk_buff *newsk)
+{
+	IS_SKB(old);
+	IS_SKB(newsk);
+
+	if(!old->next || !old->prev)
+		printk("append before unlisted item!\n");
+	if(newsk->next || newsk->prev)
+		printk("append item is already on a list.\n");
+
+	newsk->prev = old;
+	newsk->next = old->next;
+	newsk->next->prev = newsk;
+	old->next = newsk;
+	newsk->list = old->list;
+	newsk->list->qlen++;
+
+}
+
 /*
  *	Remove an sk_buff from its list. Works even without knowing the list it
  *	is sitting on, which can be handy at times. It also means that THE LIST
@@ -374,6 +487,25 @@
 	restore_flags(flags);
 }
 
+void __skb_unlink(struct sk_buff *skb)
+{
+	IS_SKB(skb);
+
+	if(skb->list)
+	{
+		skb->list->qlen--;
+		skb->next->prev = skb->prev;
+		skb->prev->next = skb->next;
+		skb->next = NULL;
+		skb->prev = NULL;
+		skb->list = NULL;
+	}
+#ifdef PARANOID_BUGHUNT_MODE	/* This is legal but we sometimes want to watch it */
+	else
+		printk("skb_unlink: not a linked element\n");
+#endif
+}
+
 /*
  *	Add data to an sk_buff
  */
@@ -649,7 +781,6 @@
 	n->next = n->prev = n->link3 = NULL;
 	n->list = NULL;
 	n->sk = NULL;
-	n->truesize = sizeof(*n);
 	n->free = 1;
 	n->tries = 0;
 	n->lock = 0;
@@ -745,10 +876,12 @@
 
 	save_flags(flags);
 	cli();
-	if(skb->lock==1)
+	if(skb->lock)
+	{
 		net_locked--;
-
-	if (!--skb->lock && (skb->free == 1 || skb->free == 3))
+		skb->lock--;
+	}
+	if (!skb->lock && (skb->free == 1 || skb->free == 3))
 	{
 		restore_flags(flags);
 		kfree_skb(skb,mode);
diff -u --recursive --new-file v1.3.74/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v1.3.74/linux/net/ipv4/af_inet.c	Wed Mar 13 10:09:26 1996
+++ linux/net/ipv4/af_inet.c	Sat Mar 16 13:52:11 1996
@@ -673,7 +673,7 @@
 	skb_queue_head_init(&sk->back_log);
 	sock->data =(void *) sk;
 	sk->dummy_th.doff = sizeof(sk->dummy_th)/4;
-	sk->ip_ttl=64;
+	sk->ip_ttl=ip_statistics.IpDefaultTTL;
 	if(sk->type==SOCK_RAW && protocol==IPPROTO_RAW)
 		sk->ip_hdrincl=1;
 	else
diff -u --recursive --new-file v1.3.74/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v1.3.74/linux/net/ipv4/arp.c	Tue Mar  5 10:11:15 1996
+++ linux/net/ipv4/arp.c	Sat Mar 16 13:52:11 1996
@@ -52,6 +52,7 @@
  *		Alan Cox	:	Missing unlock in device events.
  *		Eckes		:	ARP ioctl control errors.
  *		Alexey Kuznetsov:	Arp free fix.
+ *		Manuel Rodriguez:	Gratutious ARP.
  */
 
 /* RFC1122 Status:
@@ -783,7 +784,7 @@
 	unsigned char *arp_ptr= (unsigned char *)(arp+1);
 	struct arp_table *entry;
 	struct arp_table *proxy_entry;
-	unsigned long hash;
+	unsigned long hash, grat=0;
 	unsigned char ha[MAX_ADDR_LEN];	/* So we can enable ints again. */
 	unsigned char *sha,*tha;
 	u32 sip,tip;
@@ -928,6 +929,7 @@
  * 	check if that someone else is one of our proxies.  If it isn't,
  * 	we can toss it.
  */
+			grat = (sip == tip) && (sha == tha);
 			arp_fast_lock();
 
 			for (proxy_entry=arp_proxy_list;
@@ -949,6 +951,12 @@
 			}
 			if (proxy_entry)
 			{
+				if (grat)
+				{
+					if(!(proxy_entry->flags&ATF_PERM))
+						arp_destroy(proxy_entry);
+					goto gratuitous;
+				}
 				memcpy(ha, proxy_entry->ha, dev->addr_len);
 				arp_unlock();
 				arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha, sha);
@@ -957,6 +965,8 @@
 			}
 			else
 			{
+				if (grat) 
+					goto gratuitous;
 				arp_unlock();
 				kfree_skb(skb, FREE_READ);
 				return 0;
@@ -989,6 +999,8 @@
 
 	arp_fast_lock();
 
+gratuitous:
+
 	hash = HASH(sip);
 
 	for (entry=arp_tables[hash]; entry; entry=entry->next)
@@ -1026,6 +1038,10 @@
 /*
  * 	No entry found.  Need to add a new entry to the arp table.
  */
+
+		if (grat) 
+			goto end;
+
 		entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
 		if(entry == NULL)
 		{
@@ -1072,6 +1088,8 @@
 /*
  *	Replies have been sent, and entries have been added.  All done.
  */
+
+end:
 	kfree_skb(skb, FREE_READ);
 	arp_unlock();
 	return 0;
diff -u --recursive --new-file v1.3.74/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v1.3.74/linux/net/ipv4/icmp.c	Sat Mar  2 10:43:46 1996
+++ linux/net/ipv4/icmp.c	Sat Mar 16 13:52:11 1996
@@ -358,7 +358,7 @@
 				break;
 			case ICMP_PROT_UNREACH:
 /*				printk(KERN_INFO "ICMP: %s:%d: protocol unreachable.\n",
-					in_ntoa(iph->daddr), ntohs(iph->protocol));*/
+					in_ntoa(iph->daddr), (int)iph->protocol);*/
 				break;
 			case ICMP_PORT_UNREACH:
 				break;
diff -u --recursive --new-file v1.3.74/linux/net/ipv4/packet.c linux/net/ipv4/packet.c
--- v1.3.74/linux/net/ipv4/packet.c	Wed Feb 28 11:50:17 1996
+++ linux/net/ipv4/packet.c	Sat Mar 16 13:52:11 1996
@@ -5,6 +5,9 @@
  *
  *		PACKET - implements raw packet sockets.
  *
+ *		Doesn't belong in IP but its currently too hooked into ip
+ *		to seperate.
+ *
  * Version:	@(#)packet.c	1.0.6	05/25/93
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
@@ -30,6 +33,7 @@
  *	Richard Kooijman	:	Timestamp fixes.
  *		Alan Cox	:	New buffers. Use sk->mac.raw.
  *		Alan Cox	:	sendmsg/recvmsg support.
+ *		Alan Cox	:	Protocol setting support
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -46,6 +50,7 @@
 #include <linux/in.h>
 #include <linux/inet.h>
 #include <linux/netdevice.h>
+#include <linux/if_packet.h>
 #include <net/ip.h>
 #include <net/protocol.h>
 #include <linux/skbuff.h>
@@ -124,7 +129,8 @@
 {
 	struct sk_buff *skb;
 	struct device *dev;
-	struct sockaddr *saddr=(struct sockaddr *)msg->msg_name;
+	struct sockaddr_pkt *saddr=(struct sockaddr_pkt *)msg->msg_name;
+	unsigned short proto=0;
 
 	/*
 	 *	Check the flags. 
@@ -139,8 +145,10 @@
 	 
 	if (saddr) 
 	{
-		if (msg->msg_namelen < sizeof(*saddr)) 
+		if (msg->msg_namelen < sizeof(struct sockaddr)) 
 			return(-EINVAL);
+		if (msg->msg_namelen==sizeof(struct sockaddr_pkt))
+			proto=saddr->spkt_protocol;
 	} 
 	else
 		return(-ENOTCONN);	/* SOCK_PACKET must be sent giving an address */
@@ -149,8 +157,8 @@
 	 *	Find the device first to size check it 
 	 */
 
-	saddr->sa_data[13] = 0;
-	dev = dev_get(saddr->sa_data);
+	saddr->spkt_device[13] = 0;
+	dev = dev_get(saddr->spkt_device);
 	if (dev == NULL) 
 	{
 		return(-ENODEV);
@@ -185,6 +193,7 @@
 	skb->free = 1;
 	memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
 	skb->arp = 1;		/* No ARP needs doing on this (complete) frame */
+	skb->protocol = proto;
 
 	/*
 	 *	Now send it
@@ -393,7 +402,7 @@
 {
 	int copied=0;
 	struct sk_buff *skb;
-	struct sockaddr *saddr=(struct sockaddr *)msg->msg_name;
+	struct sockaddr_pkt *saddr=(struct sockaddr_pkt *)msg->msg_name;
 	int err;
 
 	if (sk->shutdown & RCV_SHUTDOWN) 
@@ -447,8 +456,9 @@
 	 
 	if (saddr) 
 	{
-		saddr->sa_family = skb->dev->type;
-		strncpy(saddr->sa_data,skb->dev->name, 15);
+		saddr->spkt_family = skb->dev->type;
+		strncpy(saddr->spkt_device,skb->dev->name, 15);
+		saddr->spkt_protocol = skb->protocol;
 	}
 	
 	/*
diff -u --recursive --new-file v1.3.74/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v1.3.74/linux/net/ipv4/route.c	Sat Mar  2 10:43:46 1996
+++ linux/net/ipv4/route.c	Sat Mar 16 13:52:12 1996
@@ -40,6 +40,7 @@
  *		
  *		Olaf Erb	:	irtt wasnt being copied right.
  *		Bjorn Ekwall	:	Kerneld route support.
+ *		Alan Cox	:	Multicast fixed (I hope)
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -1556,6 +1557,11 @@
 
 	if (!(rth->rt_flags & RTF_GATEWAY))
 		rth->rt_gateway = rth->rt_dst;
+	/*
+	 *	Multicast is never gatewayed.
+	 */
+	if (MULTICAST(daddr))
+		rth->rt_gateway = rth->rt_dst;
 
 	if (ip_rt_lock == 1)
 		rt_cache_add(hash, rth);
@@ -1714,7 +1720,7 @@
  *	Remove a route, as requested by the user.
  */
 
-static int rt_kill(struct rtentry *r)
+int ip_rt_kill(struct rtentry *r)
 {
 	struct sockaddr_in *trg;
 	struct sockaddr_in *msk;
@@ -1764,7 +1770,7 @@
 			if (err)
 				return err;
 			memcpy_fromfs(&rt, arg, sizeof(struct rtentry));
-			return (cmd == SIOCDELRT) ? rt_kill(&rt) : ip_rt_new(&rt);
+			return (cmd == SIOCDELRT) ? ip_rt_kill(&rt) : ip_rt_new(&rt);
 	}
 
 	return -EINVAL;
diff -u --recursive --new-file v1.3.74/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v1.3.74/linux/net/ipv4/tcp.c	Wed Mar 13 10:09:27 1996
+++ linux/net/ipv4/tcp.c	Fri Mar 15 09:32:32 1996
@@ -1042,6 +1042,11 @@
 				copy = sk->mss;
 			if (copy > seglen)
 				copy = seglen;
+			if (copy <= 0) 
+			{
+				printk("TCP: **bug**: copy=%d, sk->mss=%d\n", copy, sk->mss);
+		  		return -EFAULT;
+			}
 
 		/*
 		 *	We should really check the window here also. 
diff -u --recursive --new-file v1.3.74/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v1.3.74/linux/net/ipv4/tcp_input.c	Wed Mar 13 10:09:27 1996
+++ linux/net/ipv4/tcp_input.c	Sat Mar 16 19:12:31 1996
@@ -26,6 +26,8 @@
 #include <linux/config.h>
 #include <net/tcp.h>
 
+#include <linux/interrupt.h>
+
 /*
  *	Policy code extracted so its now seperate
  */
@@ -856,10 +858,10 @@
 	 */
 	if (skb_peek(&sk->write_queue) != NULL) 
 	{
-		if (after (sk->window_seq+1, sk->write_queue.next->end_seq) &&
+		if (!before(sk->window_seq, sk->write_queue.next->end_seq) &&
 			(sk->retransmits == 0 || 
 			 sk->ip_xmit_timeout != TIME_WRITE ||
-			 before(sk->write_queue.next->end_seq, sk->rcv_ack_seq + 1))
+			 !after(sk->write_queue.next->end_seq, sk->rcv_ack_seq))
 			&& sk->packets_out < sk->cong_window) 
 		{
 			/*
@@ -1249,25 +1251,34 @@
 		 * Ok, we found new data, update acked_seq as
 		 * necessary (and possibly send the actual
 		 * ACK packet).
-		 *
+		 */
+		sk->acked_seq = ack_seq;
+
+		/*
 		 *      rules for delaying an ack:
 		 *      - delay time <= 0.5 HZ
-		 *      - we don't have a window update to send
 		 *      - must send at least every 2 full sized packets
+		 *      - we don't have a window update to send
+		 *
+		 * We handle the window update in the actual read
+		 * side, so we only have to worry about the first two.
 		 */
-		sk->acked_seq = ack_seq;
-		if (!sk->delay_acks ||
-		    /* sk->ack_backlog >= sk->max_ack_backlog || */
-		    sk->bytes_rcv > sk->max_unacked || th->fin ||
-		    sk->ato > HZ/2) {
+		if (!sk->delay_acks || th->fin) {
 			tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
 		}
-		else 
-		{	
+		else
+		{
+			int timeout = sk->ato;
+			if (timeout > HZ/2)
+				timeout = HZ/2;
+			if (sk->bytes_rcv > sk->max_unacked) {
+				timeout = 0;
+				mark_bh(TIMER_BH);
+			}
 			sk->ack_backlog++;
-			if(sk->debug)				
+			if(sk->debug)
 				printk("Ack queued.\n");
-			tcp_reset_xmit_timer(sk, TIME_WRITE, sk->ato);	
+			tcp_reset_xmit_timer(sk, TIME_WRITE, timeout);
 		}		
 	}
 }
diff -u --recursive --new-file v1.3.74/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v1.3.74/linux/net/ipv4/tcp_output.c	Wed Mar 13 10:09:27 1996
+++ linux/net/ipv4/tcp_output.c	Fri Mar 15 09:58:40 1996
@@ -230,10 +230,10 @@
 	 */
 	 
 	while((skb = skb_peek(&sk->write_queue)) != NULL &&
-		before(skb->end_seq, sk->window_seq + 1) &&
+		!after(skb->end_seq, sk->window_seq) &&
 		(sk->retransmits == 0 ||
 		 sk->ip_xmit_timeout != TIME_WRITE ||
-		 before(skb->end_seq, sk->rcv_ack_seq + 1))
+		 !after(skb->end_seq, sk->rcv_ack_seq))
 		&& sk->packets_out < sk->cong_window) 
 	{
 		IS_SKB(skb);
diff -u --recursive --new-file v1.3.74/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v1.3.74/linux/net/unix/af_unix.c	Fri Mar 15 16:03:22 1996
+++ linux/net/unix/af_unix.c	Sat Mar 16 13:52:12 1996
@@ -24,12 +24,12 @@
  *		Alan Cox	:	Sorted out a proper draft version of
  *					file descriptor passing hacked up from
  *					Mike Shaver's work.
- *
+ *		Marty Leisner	:	Fixes to fd passing
+ *		Nick Nevin	:	recvmsg bugfix.
  *
  * Known differences from reference BSD that was tested:
  *
  *	[TO FIX]
- *	No fd passing yet.
  *	ECONNREFUSED is not returned from one end of a connected() socket to the
  *		other the moment one end closes.
  *	fstat() doesn't return st_dev=NODEV, and give the blksize as high water mark
@@ -40,6 +40,7 @@
  *	accept() returns 0 length path for an unbound connector. BSD returns 16
  *		and a null first byte in the path (but not for gethost/peername - BSD bug ??)
  *	socketpair(...SOCK_RAW..) doesnt panic the kernel.
+ *	BSD af_unix apprently has connect forgetting to block properly.
  */
 
 #include <linux/config.h>
@@ -1156,7 +1157,7 @@
 						*addr_len=sizeof(short);
 			}
 
-			num=min(skb->len,size-copied);
+			num=min(skb->len,len-done);
 			memcpy_tofs(sp, skb->data, num);
 
 			if (skb->h.filp!=NULL)
diff -u --recursive --new-file v1.3.74/linux/scripts/header.tk linux/scripts/header.tk
--- v1.3.74/linux/scripts/header.tk	Fri Mar 15 16:03:22 1996
+++ linux/scripts/header.tk	Sat Mar 16 13:52:12 1996
@@ -287,7 +287,7 @@
 	frame $w.x$line
 	option_name $w $mnum $line $text $variable
 	menubutton $w.x$line.x -textvariable $variable -menu \
-		$w.x$line.x.menu -relief raised -indicatoron 1 \
+		$w.x$line.x.menu -relief raised \
 		-width 15 -anchor w
 	pack $w.x$line.x -anchor w -side right -fill y
 	pack $w.x$line -anchor w -fill both -expand on
@@ -301,70 +301,7 @@
 #nothing to do for now.
 }
 
-proc dohelp_old_version_ignore_me {w varname }  {
-	catch {destroy $w}
-	toplevel $w -class Dialog
-
-	set filefound 0
-	set found 0
-	set lineno 0
-	if { [file readable Documentation/Configure.help] == 1} then {
-		set f [open Documentation/Configure.help r]
-		set filefound 1	
-		while {![eof $f]} {
-			gets $f line
-		        set line1 [string trim $line]
-			if { $line1 == $varname } then {
-				set found 1
-			} else {
-				if { $found == 0 } continue 
-			}
-			if { [string length $line1 ] == 0 } break
-			if { [string  range $line1 0 0 ] == "#" } continue
-			if { $lineno == 0 } then {
-			    append message $line1
- 			    append message ":"
-			} else {
- 			    append message " "
-			    append message $line1
-			}
-			set lineno [expr $lineno + 1]
-		}
-		close $f
-	}
-
-	if { $found == 0 } then {
-		if { $filefound == 0 } then {
-		message $w.m -width 750 -aspect 300 -text \
-			"No help available - unable to open file Documentation/Configure.help.  This file is available from http://math-www.uni-paderborn.de/~axel/config_help.html or ftp://sunsite.unc.edu/pub/Linux/kernel/config/krnl_cnfg_hlp_1.X.XX.tgz"  -relief raised 
-		} else {
-		message $w.m -width 400 -aspect 300 -text \
-			"No help available for $varname"  -relief raised 
-		}
-		label $w.bm -bitmap error
-		pack $w.bm $w.m -pady 10 -side top -padx 10
-		wm title $w "RTFM" 
-	} else {
-		message $w.m -width 400 -aspect 300 -text $message \
-			 -relief raised 
-		label $w.bm -bitmap info
-		pack $w.bm $w.m -pady 10 -side top -padx 10
-		wm title $w "Configuration help" 
-	}
-	set oldFocus [focus]
-	frame $w.f
-	button $w.f.back -text "OK" -activebackground green \
-		-width 10 -command "destroy $w; focus $oldFocus"
-	pack $w.f.back -side bottom -pady 10 -anchor s
-	pack $w.f -pady 10 -side top -padx 10 -anchor s
-	focus $w
-	global winx; global winy
-	set winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30]
-	wm geometry $w +$winx+$winy
-}
-
-
-proc dohelp {w varname }  {
+proc dohelp {w var }  {
 	catch {destroy $w}
 	toplevel $w -class Dialog
 
@@ -374,22 +311,21 @@
 
 	if { [file readable Documentation/Configure.help] == 1} then {
 		set filefound 1
-		set message [exec awk "
-			BEGIN {
-				start=0;
-			}
-			/^$varname\[ 	\]*\$/ {
-				start=1;
-				printf(\"%s:\\n\\n\",\$0);
-				next;
-			}
-			!/^\[ 	\]/ {
-				start=0;
-			}
-			/^\[ 	\]*/ && start==1 {
-				gsub(\"^\[ 	\]*\",\"\");
-				printf(\"%s \",\$0);
-			}
+		set message [exec sed -n "
+			/^$var\[ 	\]*\$/,\${
+				/^$var\[ 	\]*\$/c\\
+${var}:\\
+
+				/^#.*/d
+				/^\[ 	\]*\$/bL
+                		H
+			}
+			d
+			:L x
+			s/\\n  //
+			s/\\n  / /g
+			p
+			q
 			" Documentation/Configure.help]
 		set found [expr [string length "$message"] > 0]
 	}
@@ -402,7 +338,7 @@
 			"No help available - unable to open file Documentation/Configure.help.  This file should have come with your kernel."
 		} else {
 		message $w.f1.m -width 400 -aspect 300 -relief flat -text \
-			"No help available for $varname"
+			"No help available for $var"
 		}
 		label $w.f1.bm -bitmap error
 		wm title $w "RTFM"
diff -u --recursive --new-file v1.3.74/linux/scripts/tkcond.c linux/scripts/tkcond.c
--- v1.3.74/linux/scripts/tkcond.c	Wed Mar 13 10:09:28 1996
+++ linux/scripts/tkcond.c	Sat Mar 16 13:52:12 1996
@@ -123,7 +123,7 @@
 	   * be one operator inbetween.
 	   */
 	  cond2 = cond->next->next;
-	  sprintf(tmpbuf, "%d", cond->variable.cfg->choice_value);
+	  strcpy(tmpbuf, cond->variable.cfg->label);
 
 	  if( strcmp(cond2->variable.str, "y") == 0 )
 	    {
diff -u --recursive --new-file v1.3.74/linux/scripts/tkgen.c linux/scripts/tkgen.c
--- v1.3.74/linux/scripts/tkgen.c	Wed Mar 13 10:09:29 1996
+++ linux/scripts/tkgen.c	Sat Mar 16 13:52:12 1996
@@ -14,9 +14,15 @@
  * Avery Pennarun - The int and hex config.in commands work right.
  *                - Choice buttons are more user-friendly.
  *                - Disabling a text entry line greys it out properly.
- *		  - dep_tristate now works like in Configure. (not pretty)
+ *                - dep_tristate now works like in Configure. (not pretty)
  *                - No warnings in gcc -Wall. (Fixed some "interesting" bugs.)
  *                - Faster/prettier "Help" lookups.
+ *
+ * 1996 03 15
+ * Avery Pennarun - Added new sed script from Axel Boldt to make help even
+ *                  faster. (Actually awk is downright slow on some machines.)
+ *                - Fixed a bug I introduced into Choice dependencies.  Thanks
+ *                  to Robert Krawitz for pointing this out.
  *
  * TO DO:
  *   - clean up - there are useless ifdef's everywhere.
