diff -u --recursive --new-file v1.3.60/linux/CREDITS linux/CREDITS
--- v1.3.60/linux/CREDITS	Wed Feb  7 15:11:17 1996
+++ linux/CREDITS	Fri Feb  9 07:50:27 1996
@@ -562,6 +562,11 @@
 S: NL-5237 KA 's-Hertogenbosch
 S: The Netherlands
 
+N: Savio Lam
+E: lam836@cs.cuhk.hk
+D: Author of the dialog utility, foundation
+D: for Menuconfig's lxdialog.
+
 N: Volker Lendecke
 E: lendecke@namu01.gwdg.de
 D: Kernel smbfs (to mount WfW, NT and OS/2 network drives.)
@@ -857,6 +862,14 @@
 D: Keeper of the Jargon File and curator of the Retrocomputing Museum
 D: Author, Emacs VC and GUD modes
 
+N: William E. Roadcap
+E: roadcapw@cfw.com
+W: http://www.cfw.com/~roadcapw
+D: Author of menu based configuration tool, Menuconfig.
+S: 1407 Broad Street
+S: Waynesboro, Va  22980
+S: USA
+
 N: Florian La Roche
 E: rzsfl@rz.uni-sb.de
 E: flla@stud.uni-sb.de
@@ -1082,6 +1095,18 @@
 S: 4130 Upson Hall
 S: Ithaca NY 14850
 S: USA
+
+N: Greg Wettstein
+E: greg@wind.rmcc.com
+D: Filesystem valid flag for MINIX filesystem.
+D: Minor kernel debugging.
+D: Monitoring of development kernels for long-term stability.
+D: Early implementations of Linux in a commercial environment.
+S: Dr. Greg Wettstein, Ph.D.
+S: Oncology Research Division Computing Facility
+S: Roger Maris Cancer Center
+S: 820 4th St. N.
+S: Fargo, ND  58122
 
 N: Marco van Wieringen
 E: mvw@mercury.mcs.nl.mugnet.org
diff -u --recursive --new-file v1.3.60/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.60/linux/Documentation/Configure.help	Wed Feb  7 15:11:18 1996
+++ linux/Documentation/Configure.help	Fri Feb  9 16:59:04 1996
@@ -342,6 +342,21 @@
   kernel in ELF by saying Y here and editing the variables CC
   and LD in the toplevel Makefile.
 
+Kernel support for A.OUT binaries
+CONFIG_BINFMT_AOUT
+  A.OUT (Assembler.OUTput) format is a format for libraries and
+  executables used in the earliest versions of UNIX. Linux used this
+  format until it was replaced with the ELF format.
+  As more and more programs are converted to ELF, the use for A.OUT
+  will gradually diminish. If you disable this option it will reduce
+  your kernel by one page. This is not much and by itself does not
+  warrant removing support. However its removal is a good idea when
+  you wish to ensure that absolutely none of your programs will use
+  this older executable format. If you don't know what to answer at
+  this point then answer Y. You may answer M for module support and
+  later load the module when you find a program which needs a.out
+  format.
+
 Processor type
 CONFIG_M386
   This is the processor type of your CPU. It is used for optimizing
@@ -1116,6 +1131,11 @@
   available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO, explains how to configure
   CSLIP. This won't enlarge your kernel.
+
+Keepalive and linefill
+CONFIG_SLIP_SMART
+  Adds additional capabilities to the SLIP driver to support the RELCOM
+  line fill and keepalive monitoring. Ideal on poor quality analogue lines.
 
 PPP (point-to-point) support
 CONFIG_PPP
diff -u --recursive --new-file v1.3.60/linux/Documentation/smp.tex linux/Documentation/smp.tex
--- v1.3.60/linux/Documentation/smp.tex	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/smp.tex	Fri Feb  9 16:59:04 1996
@@ -0,0 +1,345 @@
+From: michael@Physik.Uni-Dortmund.DE (Michael Dirkmann)
+
+thanks for your information. Attached is the tex-code of your
+SMP-documentation :
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+\documentclass[]{article}
+\parindent0.0cm
+\parskip0.2cm
+
+\begin{document}
+
+\begin{center}
+\LARGE \bf
+An Implementation Of Multiprocessor Linux
+\normalsize
+\end{center}
+
+{ \it
+This document describes the implementation of a simple SMP 
+Linux kernel extension and how to use this to develop SMP Linux kernels for 
+architectures other than the Intel MP v1.1 architecture for Pentium and 486 
+processors.}
+
+\hfill Alan Cox, 1995
+
+
+The author wishes to thank Caldera Inc whose donation of an ASUS dual 
+pentium board made this project possible, and Thomas Radke, whose initial 
+work on multiprocessor Linux formed the backbone of this project.
+
+\section{Background: The Intel MP specification.}
+Most IBM PC style multiprocessor motherboards combine Intel 486 or Pentium 
+processors and glue chipsets with a hardware/software specification. The 
+specification places much of the onus for hard work on the chipset and 
+hardware rather than the operating system.
+
+The Intel pentium processors have a wide variety of inbuilt facilities for 
+supporting multiprocessing, including hardware cache coherency, built in 
+interprocessor interrupt handling and a set of atomic test and set, 
+exchange and similar operations. The cache coherency in paticular makes the 
+operating systems job far easier.
+
+The specification defines a detailed configuration structure in ROM that 
+the boot up processor can read to find the full configuration of the 
+processors and busses. It also defines a procedure for starting up the 
+other processors.
+
+
+\section{Mutual Exclusion Within A Single Processor Linux Kernel}
+For any kernel to function in a sane manner it has to provide internal 
+locking and protection of its own tables to prevent two processes updating 
+them at once and for example allocating the same memory block. There are 
+two strategies for this within current Unix and Unixlike kernels. 
+Traditional unix systems from the earliest of days use a scheme of 'Coarse 
+Grained Locking' where the entire kernel is protected as a small number of 
+locks only. Some modern systems use fine grained locking. Because fine 
+grained locking has more overhead it is normally used only on 
+multiprocessor kernels and real time kernels. In a real time kernel the 
+fine grained locking reduces the amount of time locks are held and reduces 
+the critical (to real time programming at least) latency times.
+
+Within the Linux kernel certain guarantees are made. No process running in 
+kernel mode will be pre-empted by another kernel mode process unless it 
+voluntarily sleeps.  This ensures that blocks of kernel code are 
+effectively atomic with respect to other processes and greatly simplifies 
+many operation. Secondly interrupts may pre-empt a kernel running process, 
+but will always return to that process. A process in kernel mode may 
+disable interrupts on the processor and guarantee such an interruption will 
+not occur. The final guarantee is that an interrupt will not bne pre-empted 
+by a kernel task. That is interrupts will run to completion or be 
+pre-empted by other interrupts only.
+
+The SMP kernel chooses to continue these basic guarantees in order to make 
+initial implementation and deployment easier.  A single lock is maintained 
+across all processors. This lock is required to access the kernel space. 
+Any processor may hold it and once it is held may also re-enter the kernel 
+for interrupts and other services whenever it likes until the lock is 
+relinquished. This lock ensures that a kernel mode process will not be 
+pre-empted and ensures that blocking interrupts in kernel mode behaves 
+correctly. This is guaranteed because only the processor holding the lock 
+can be in kernel mode, only kernel mode processes can disable interrupts 
+and only the processor holding the lock may handle an interrupt.
+
+Such a choice is however poor for performance. In the longer term it is 
+neccessary to move to finer grained parallelisn in order to get the best 
+system performance. This can be done heirarchically by gradually refining 
+the locks to cover smaller areas. With the current kernel highly CPU bound 
+process sets perform well but I/O bound task sets can easily degenerate to 
+near single processor performance levels. This refinement will be needed to 
+get the best from Linux/SMP.
+
+\subsection{Changes To The Portable Kernel Components}
+The kernel changes are split into generic SMP support changes and 
+architecture specific changes neccessary to accomodate each different 
+processor type Linux is ported to.
+
+
+\subsubsection{Initialisation}
+The first problem with a multiprocessor kernel is starting the other 
+processors up. Linux/SMP defines that a single processor enters the normal 
+kernel entry point start\_kernel(). Other processors are assumed not to be 
+started or to have been captured elsewhere. The first processor begins the 
+normal Linux initialisation sequences and sets up paging, interrupts and 
+trap handlers. After it has obtained the processor information about the 
+boot CPU, the architecture specific function 
+
+
+{\tt \bf{void smp\_store\_cpu\_info(int processor\_id) }}
+
+is called to store any information about the processor into a per processor 
+array. This includes things like the bogomips speed ratings.
+
+Having completed the kernel initialisation the architecture specific 
+function
+
+{\tt \bf void smp\_boot\_cpus(void) }
+
+is called and is expected to start up each other processor and cause it to 
+enter start\_kernel() with its paging registers and other control 
+information correctly loaded. Each other processor skips the setup except 
+for calling the trap and irq initialisation functions that are needed on 
+some processors to set each CPU up correctly.  These functions will 
+probably need to be modified in existing kernels to cope with this.
+
+
+Each additional CPU the calls the architecture specific function
+
+{\tt \bf void smp\_callin(void)}
+
+which does any final setup and then spins the processor while the boot 
+up processor forks off enough idle threads for each processor. This is 
+neccessary because the scheduler assumes there is always something to run. 
+Having generated these threads and forked init the architecture specific 
+
+{\tt \bf void smp\_commence(void)}
+
+function is invoked. This does any final setup and indicates to the system 
+that multiprocessor mode is now active. All the processors spinning in the 
+smp\_callin() function are now released to run the idle processes, which 
+they will run when they have no real work to process.
+
+
+\subsubsection{Scheduling}
+The kernel scheduler implements a simple but very and effective task 
+scheduler. The basic structure of this scheduler is unchanged in the 
+multiprocessor kernel. A processor field is added to each task, and this 
+maintains the number of the processor executing a given task, or a magic 
+constant (NO\_PROC\_ID)  indicating the job is not allocated to a processor. 
+	 
+Each processor executes the scheduler itself and will select the next task 
+to run from all runnable processes not allocated to a different processor. 
+The algorithm used by the selection is otherwise unchanged. This is 
+actually inadequate for the final system because there are advantages to 
+keeping a process on the same CPU, especially on processor boards with per 
+processor second level caches.
+
+Throughout the kernel the variable 'current' is used as a global for the 
+current process. In Linux/SMP this becomes a macro which expands to 
+current\_set[smp\_processor\_id()]. This enables almost the entire kernel to 
+be unaware of the array of running processors, but still allows the SMP 
+aware kernel modules to see all of the running processes.
+
+The fork system call is modified to generate multiple processes with a 
+process id of zero until the SMP kernel starts up properly. This is 
+neccessary because process number 1 must be init, and it is desirable that 
+all the system threads are process 0. 
+
+The final area within the scheduling of processes that does cause problems 
+is the fact the uniprocessor kernel hard codes tests for the idle threads 
+as task[0] and the init process as task[1]. Because there are multiple idle 
+threads it is neccessary to replace these with tests that the process id is 
+0 and a search for process ID 1, respectively.
+
+\subsubsection{Memory Management}
+The memory management core of the existing Linux system functions 
+adequately within the multiprocessor framework providing the locking is 
+used. Certain processor specific areas do need changing, in paticular 
+invalidate() must invalidate the TLB's of all processors before it returns.
+
+
+\subsubsection{Miscellaneous Functions}
+The portable SMP code rests on a small set of functions and variables 
+that are provided by the processor specification functionality. These are
+
+{\tt \bf int smp\_processor\_id(void) }
+
+which returns the identity of the process the call is executed upon. This 
+call is assumed to be valid at all times. This may mean additional tests 
+are needed during initialisation.
+
+
+{\tt \bf int smp\_num\_cpus;}
+
+This is the number of processors in the system. \
+
+{\tt \bf void smp\_message\_pass(int target, int msg, unsigned long data,
+		int wait)}
+
+This function passes messages between processors. At the moment it is not 
+sufficiently defined to sensibly document and needs cleaning up and further 
+work. Refer to the processor specific code documentation for more details.
+
+
+\subsection{Architecture Specific Code For the Intel MP Port}
+The architecture specific code for the intel port splits fairly cleanly 
+into four sections. Firstly the initialisation code used to boot the 
+system, secondly the message handling and support code, thirdly the 
+interrupt and kernel syscall entry function handling and finally the 
+extensions to standard kernel facilities to cope with multiple processors.
+
+\subsubsection{Initialisation}	
+The intel MP architecture captures all the processors except for a single 
+processor known as the 'boot processor' in the BIOS at boot time. Thus a 
+single processor enters the kernel bootup code. The first processor 
+executes the bootstrap code, loads and uncompresses the kernel. Having 
+unpacked the kernel it sets up the paging and control registers then enters 
+the C kernel startup.
+
+The assembler startup code for the kernel is modified so that it can be 
+used by the other processors to do the processor identification and various 
+other low level configurations but does not execute those parts of the 
+startup code that would damage the running system (such as clearing the BSS 
+segment). 
+
+In the initialisation done by the first processor the arch/i386/mm/init 
+code is modified to scan the low page, top page and BIOS for intel MP 
+signature blocks. This is neccessary because the MP signature blocks must 
+be read and processed before the kernel is allowed to allocate and destroy 
+the page at the top of low memory. Having established the number of 
+processors it reserves a set of pages to provide a stack come boot up area 
+for each processor in the system. These must be allocated at startup to 
+ensure they fall below the 1Mb boundary.
+
+Further processors are started up in smp\_boot\_cpus() by programming the 
+APIC controller registers and sending an inter-processor interrupt (IPI) to 
+the processor. This message causes the target processor to begin executing 
+code at the start of any page of memory within the lowest 1Mb, in 16bit 
+real mode. The kernel uses the single page it allocated for each processor 
+to use as stack. Before booting a given CPU the relocatable code from 
+trampoline.S and trampoline32.S is copied to the bottom of its stack page 
+and used as the target for the startup. 
+
+The trampoline code calculates the desired stack base from the code 
+segment (since the code segment on startup is the bottom of the stack), 
+ enters 32bit mode and jumps to the kernel entry assembler. This as 
+described above is modified to only execute the parts necessary for each 
+processor, and then to enter start\_kernel(). On entering the kernel the 
+processor initialises its trap and interrupt handlers before entering 
+smp\_callin(), where it reports its status and sets a flag that causes the 
+boot processor to continue and look for further processors. The processor 
+then spins until smp\_commence() is invoked.
+
+Having started each processor up the smp\_commence( ) function flips a 
+flag. Each processor spinning in smp\_callin() then loads the task register 
+with the task state segment (TSS) of its idle thread as is needed for task 
+switching.
+
+\subsubsection{Message Handling and Support Code}
+The architecture specific code implements the smp\_processor\_id() function 
+by querying the APIC logical identity register. Because the APIC isnt 
+mapped into the kernel address space at boot, the initial value returned is 
+rigged by setting the APIC base pointer to point at a suitable constant. 
+Once the system starts doing the SMP setup (in smp\_boot\_cpus()), the APIC 
+is mapped with a vremap() call and the apic pointer is adjusted 
+appropriately. From then on the real APIC logical identity register is 
+read.
+
+Message passing is accomplished using a pair of IPI's on interrupt 13 
+(unused by the 80486 FPU's in SMP mode) and interrupt 16. Two are used in 
+order to seperate messages that cannot be processed until the receiver 
+obtains the kernel spinlock from messages that can be processed 
+immediately. In effect IRQ 13 is a fast IRQ handler that does not obtain 
+the locks, and cannot cause a reschedule, while IRQ 16 is a slow IRQ that 
+must acquire the kernel spinlocks and can cause a reschedule. This 
+interrupt is used for passing on slave timer messages from the processor 
+that receives the timer interrupt to the rest of the processors, so that 
+they can reschedule running tasks.
+
+
+\subsubsection{Entry And Exit Code}
+A single spinlock protects the entire kernel. The interrupt handlers, the 
+syscall entry code and the exception handlers all acquire the lock before 
+entering the kernel proper. When the processor is trying to acquire the 
+spinlock it spins continually on the lock with interrupts disabled. This 
+causes a specific deadlock problem. The lock owner may need to send an 
+invalidate request to the rest of the processors and wait for these to 
+complete before continuing. A processor spinning on the lock would not be 
+able to do thus. Thus the loop of the spinlock tests and handles invalidate 
+requests. If the invalidate bit for the spinning CPU is set the processor 
+invalidates its TLB and atomically clears the bit. When the spinlock is 
+obtained that processor will take an IPI and in the IPI test the bit and 
+skip the invalidate as the bit is clear.
+
+One complexity of the spinlock is that a process running in kernel mode 
+can sleep voluntarily and be pre-empted. A switch from such a process to a 
+process executing in user space may reduce the lock count. To track this 
+the kernel uses a syscall\_count and a per process lock\_depth parameter to 
+track the kernel lock state. The switch\_to() function is modified in SMP 
+mode to adjust the lock appropriately.
+
+The final problem is the idle thread. In the single processor kernel the 
+idle thread executes 'hlt' instructions. This saves power and reduces the 
+running temperature of the processors when they are idle. However it means 
+the process spends all its time in kernel mode and would thus hold the 
+kernel spinlock. The SMP idle thread continually reschedules a new task and 
+returns to user mode. This is far from ideal and will be modified to use 
+'hlt' instructions and release the spinlock soon. Using 'hlt' is even more 
+beneficial on a multiprocessor system as it almost completely takes an idle 
+processor off the bus.
+
+Interrupts are distributed by an i82489 APIC. This chip is set up to work 
+as an emulation of the traditional PC interrupt controllers when the 
+machine boots (so that an Intel MP machine boots one CPU and PC 
+compatible). The kernel has all the relevant locks but does not yet 
+reprogram the 82489 to deliver interrupts to arbitary processors as it 
+should. This requires further modification of the standard Linux interrupt 
+handling code, and is paticularly messy as the interrupt handler behaviour 
+has to change as soon as the 82489 is switched into SMP mode.
+
+
+\subsubsection{Extensions To Standard Facilities}
+The kernel maintains a set of per processor control information such as 
+the speed of the processor for delay loops. These functions on the SMP 
+kernel look the values up in a per processor array that is set up from the 
+data generated at boot up by the smp\_store\_cpu\_info() function. This 
+includes other facts such as whether there is an FPU on the processor. The 
+current kernel does not handle floating point correctly, this requires some 
+changes to the techniques the single CPU kernel uses to minimise floating 
+point processor reloads.
+
+The highly useful atomic bit operations are prefixed with the 'lock' 
+prefix in the SMP kernel to maintain their atomic properties when used 
+outside of (and by) the spinlock and message code. Amongst other things 
+this is needed for the invalidate handler, as all  CPU's will invalidate at 
+the same time without any locks.
+
+Interrupt 13 floating point error reporting is removed. This facility is 
+not usable on a multiprocessor board, nor relevant to the Intel MP 
+architecture which does not cover the 80386/80387 processor pair. \
+
+The /proc filesystem support is changed so that the /proc/cpuinfo file 
+contains a column for each processor present. This information is extracted 
+from the data save by smp\_store\_cpu\_info().
+
+\end{document}
diff -u --recursive --new-file v1.3.60/linux/Makefile linux/Makefile
--- v1.3.60/linux/Makefile	Wed Feb  7 15:11:19 1996
+++ linux/Makefile	Fri Feb  9 16:59:04 1996
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 60
+SUBLEVEL = 61
 
 ARCH = i386
 
diff -u --recursive --new-file v1.3.60/linux/Rules.make linux/Rules.make
--- v1.3.60/linux/Rules.make	Tue Jan 23 21:15:36 1996
+++ linux/Rules.make	Thu Feb  8 05:03:03 1996
@@ -34,7 +34,8 @@
 #
 # Get things started.
 #
-first_rule: sub_dirs $(O_TARGET) $(L_TARGET)
+first_rule: sub_dirs
+	$(MAKE) all_targets
 
 #
 # Common rules
@@ -48,6 +49,11 @@
 
 %.o: %.s
 	$(AS) $(ASFLAGS) $(EXTRA_CFLAGS) -o $@ $<
+
+#
+#
+#
+all_targets: $(O_TARGET) $(L_TARGET)
 
 #
 # Rule to compile a set of .o files into one .o file
diff -u --recursive --new-file v1.3.60/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v1.3.60/linux/arch/alpha/config.in	Tue Jan 23 21:15:36 1996
+++ linux/arch/alpha/config.in	Thu Feb  8 06:19:52 1996
@@ -57,6 +57,7 @@
 fi
 bool 'Networking support' CONFIG_NET
 bool 'System V IPC' CONFIG_SYSVIPC
+tristate 'Kernel support for a.out (ECOFF) binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 endmenu
 
diff -u --recursive --new-file v1.3.60/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
--- v1.3.60/linux/arch/alpha/defconfig	Thu Jan  4 21:54:53 1996
+++ linux/arch/alpha/defconfig	Fri Feb  9 07:31:22 1996
@@ -15,10 +15,10 @@
 # CONFIG_ALPHA_AVANTI is not set
 # CONFIG_ALPHA_JENSEN is not set
 # CONFIG_ALPHA_NONAME is not set
-CONFIG_ALPHA_CABRIOLET=y
+# CONFIG_ALPHA_CABRIOLET is not set
 # CONFIG_ALPHA_EB66 is not set
 # CONFIG_ALPHA_EB66P is not set
-# CONFIG_ALPHA_EB64P is not set
+CONFIG_ALPHA_EB64P=y
 # CONFIG_ALPHA_SRM is not set
 CONFIG_PCI=y
 CONFIG_ALPHA_APECS=y
@@ -28,10 +28,11 @@
 CONFIG_PCI_OPTIMIZE=y
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
+CONFIG_BINFMT_AOUT=y
 # CONFIG_BINFMT_ELF is not set
 
 #
-# block devices
+# Block devices
 #
 CONFIG_BLK_DEV_FD=y
 # CONFIG_BLK_DEV_RAM is not set
@@ -42,7 +43,9 @@
 #
 # CONFIG_BLK_DEV_HD is not set
 CONFIG_BLK_DEV_IDE=y
-# CONFIG_BLK_DEV_IDEATAPI is not set
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_TRITON is not set
 # CONFIG_BLK_DEV_XD is not set
 
@@ -65,6 +68,10 @@
 # CONFIG_TCP_NAGLE_OFF is not set
 CONFIG_IP_NOSR=y
 CONFIG_SKB_LARGE=y
+
+#
+#  
+#
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
 # CONFIG_AX25 is not set
@@ -92,6 +99,7 @@
 #
 # SCSI low-level drivers
 #
+# CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_AHA152X is not set
 # CONFIG_SCSI_AHA1542 is not set
 # CONFIG_SCSI_AHA1740 is not set
@@ -112,6 +120,7 @@
 # CONFIG_SCSI_7000FASST is not set
 # CONFIG_SCSI_EATA is not set
 # CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_AM53C974 is not set
 
 #
 # Network device support
@@ -149,17 +158,19 @@
 # CONFIG_EXT_FS is not set
 CONFIG_EXT2_FS=y
 # CONFIG_XIA_FS is not set
+CONFIG_FAT_FS=y
 CONFIG_MSDOS_FS=y
+# CONFIG_VFAT_FS is not set
 # CONFIG_UMSDOS_FS is not set
 CONFIG_PROC_FS=y
-# CONFIG_NFS_FS is not set
+CONFIG_NFS_FS=y
 # CONFIG_SMB_FS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
 
 #
-# character devices
+# Character devices
 #
 # CONFIG_CYCLADES is not set
 # CONFIG_STALDRV is not set
diff -u --recursive --new-file v1.3.60/linux/arch/alpha/kernel/ksyms.c linux/arch/alpha/kernel/ksyms.c
--- v1.3.60/linux/arch/alpha/kernel/ksyms.c	Tue Jan 23 21:15:36 1996
+++ linux/arch/alpha/kernel/ksyms.c	Thu Feb  8 09:34:41 1996
@@ -5,7 +5,7 @@
  * modules.
  */
 
-#include <linux/config.h>
+#include <linux/string.h>
 #include <linux/module.h>
 # include <asm/io.h>
 # include <asm/hwrpb.h>
diff -u --recursive --new-file v1.3.60/linux/arch/i386/Makefile linux/arch/i386/Makefile
--- v1.3.60/linux/arch/i386/Makefile	Wed Feb  7 15:11:19 1996
+++ linux/arch/i386/Makefile	Fri Feb  9 08:26:30 1996
@@ -23,15 +23,16 @@
 #
 #
 
+AS86    =$(CROSS_COMPILE)as86 -0 -a
+AS386   =$(CROSS_COMPILE)as86 -3
+LD86    =$(CROSS_COMPILE)ld86 -0
+
 ifdef CONFIG_KERNEL_ELF
 
 LD=$(CROSS_COMPILE)ld -m elf_i386
 CPP=$(CC) -E -D__ELF__
 OBJDUMP =$(CROSS_COMPILE)objdump
 ENCAPS=$(CROSS_COMPILE)encaps
-AS86    =$(CROSS_COMPILE)as86 -0 -a
-AS386   =$(CROSS_COMPILE)as86 -3
-LD86    =$(CROSS_COMPILE)ld86 -0
 OBJDUMP_FLAGS=-k -q 
 ZLDFLAGS=-e startup_32 
 LDFLAGS=-e stext
@@ -51,15 +52,19 @@
 CFLAGS := $(CFLAGS) -pipe
 
 ifdef CONFIG_M386
-CFLAGS := $(CFLAGS) -m386
+CFLAGS := $(CFLAGS) -m386 -DCPU=386
 endif
 
 ifdef CONFIG_M486
-CFLAGS := $(CFLAGS) -m486
+CFLAGS := $(CFLAGS) -m486 -DCPU=486
 endif
 
 ifdef CONFIG_M586
-CFLAGS := $(CFLAGS) -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2
+CFLAGS := $(CFLAGS) -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=586
+endif
+
+ifdef CONFIG_M686
+CFLAGS := $(CFLAGS) -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=686
 endif
 
 ifdef SMP
diff -u --recursive --new-file v1.3.60/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.3.60/linux/arch/i386/config.in	Wed Feb  7 15:11:20 1996
+++ linux/arch/i386/config.in	Fri Feb  9 07:35:32 1996
@@ -24,6 +24,7 @@
   bool '   PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
 fi
 bool 'System V IPC' CONFIG_SYSVIPC
+tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 if [ "$CONFIG_BINFMT_ELF" = "y" ]; then
   bool 'Compile kernel as ELF - if your GCC is ELF-GCC' CONFIG_KERNEL_ELF
@@ -31,7 +32,8 @@
 choice 'Processor type' \
 	"386		CONFIG_M386	\
 	 486		CONFIG_M486	\
-	 Pentium	CONFIG_M586" Pentium
+	 Pentium	CONFIG_M586	\
+	 PPro		CONFIG_M686" Pentium
 endmenu
 
 source drivers/block/Config.in
diff -u --recursive --new-file v1.3.60/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v1.3.60/linux/arch/i386/defconfig	Wed Feb  7 15:11:20 1996
+++ linux/arch/i386/defconfig	Fri Feb  9 08:24:31 1996
@@ -18,11 +18,13 @@
 CONFIG_PCI=y
 CONFIG_PCI_OPTIMIZE=y
 CONFIG_SYSVIPC=y
+CONFIG_BINFMT_AOUT=y
 CONFIG_BINFMT_ELF=y
 CONFIG_KERNEL_ELF=y
 # CONFIG_M386 is not set
 # CONFIG_M486 is not set
 CONFIG_M586=y
+# CONFIG_M686 is not set
 
 #
 # Block devices
@@ -38,8 +40,10 @@
 CONFIG_BLK_DEV_IDE=y
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
+CONFIG_BLK_DEV_RZ1000=y
 CONFIG_BLK_DEV_CMD640=y
 # CONFIG_BLK_DEV_TRITON is not set
+# CONFIG_IDE_CHIPSETS is not set
 # CONFIG_BLK_DEV_XD is not set
 
 #
diff -u --recursive --new-file v1.3.60/linux/arch/i386/kernel/ksyms.c linux/arch/i386/kernel/ksyms.c
--- v1.3.60/linux/arch/i386/kernel/ksyms.c	Tue Jan 23 21:15:36 1996
+++ linux/arch/i386/kernel/ksyms.c	Thu Feb  8 04:47:28 1996
@@ -7,6 +7,7 @@
 	/* platform dependent support */
 #ifdef __SMP__
 	X(apic_reg),		/* Needed internally for the I386 inlines */
+	X(cpu_data),
 #endif
 #include <linux/symtab_end.h>
 };
diff -u --recursive --new-file v1.3.60/linux/arch/mips/config.in linux/arch/mips/config.in
--- v1.3.60/linux/arch/mips/config.in	Tue Jan 23 21:15:36 1996
+++ linux/arch/mips/config.in	Thu Feb  8 05:41:50 1996
@@ -36,6 +36,7 @@
 fi
 
 define_bool CONFIG_BINFMT_ELF y
+define_bool CONFIG_BINFMT_AOUT y
 bool 'Compile the kernel into the ELF object format' CONFIG_ELF_KERNEL
 if [ "$CONFIG_ELF_KERNEL" = "y" ]; then
   bool 'Is your ELF compiler an extra compiler' CONFIG_EXTRA_ELF_COMPILER
diff -u --recursive --new-file v1.3.60/linux/arch/mips/defconfig linux/arch/mips/defconfig
--- v1.3.60/linux/arch/mips/defconfig	Sun Dec 17 11:43:10 1995
+++ linux/arch/mips/defconfig	Thu Feb  8 05:41:50 1996
@@ -21,6 +21,7 @@
 # CONFIG_CPU_R10000 is not set
 CONFIG_TLB_SHUTDOWN=y
 CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=y
 # CONFIG_ELF_KERNEL is not set
 CONFIG_CPU_LITTLE_ENDIAN=y
 # CONFIG_NET is not set
diff -u --recursive --new-file v1.3.60/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v1.3.60/linux/arch/ppc/config.in	Sun Jan 14 16:30:10 1996
+++ linux/arch/ppc/config.in	Thu Feb  8 05:41:50 1996
@@ -39,6 +39,7 @@
 if [ "$CONFIG_BINFMT_ELF" = "y" ]; then
 bool 'Compile kernel as ELF - if your GCC is ELF-GCC' CONFIG_KERNEL_ELF y
 fi
+tristate 'Kernel support for A.OUT binaries' CONFIG_BINFMT_AOUT y
 #bool 'Use -mpentium flag for Pentium-specific optimizations' CONFIG_M586 n
 #if [ "$CONFIG_M586" = "n" ]; then
 #bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
diff -u --recursive --new-file v1.3.60/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v1.3.60/linux/arch/sparc/config.in	Tue Jan 23 21:15:36 1996
+++ linux/arch/sparc/config.in	Thu Feb  8 05:41:50 1996
@@ -17,6 +17,7 @@
 bool 'Networking support' CONFIG_NET
 bool 'System V IPC' CONFIG_SYSVIPC
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
+tristate 'Kernel support for A.OUT binaries' CONFIG_BINFMT_AOUT
 endmenu
 
 source drivers/block/Config.in
diff -u --recursive --new-file v1.3.60/linux/arch/sparc/defconfig linux/arch/sparc/defconfig
--- v1.3.60/linux/arch/sparc/defconfig	Sat Nov 25 19:04:36 1995
+++ linux/arch/sparc/defconfig	Thu Feb  8 05:41:50 1996
@@ -13,6 +13,7 @@
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
 # CONFIG_BINFMT_ELF is not set
+CONFIG_BINFMT_AOUT=y
 
 #
 # block devices
diff -u --recursive --new-file v1.3.60/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v1.3.60/linux/drivers/block/Config.in	Wed Feb  7 15:11:21 1996
+++ linux/drivers/block/Config.in	Fri Feb  9 07:31:22 1996
@@ -18,9 +18,19 @@
   if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
     bool '   Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD
     bool '   Include (ALPHA) IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE
-    bool '   Special CMD640 chipset support' CONFIG_BLK_DEV_CMD640
+    bool '   RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000
+    bool '   CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640
     if [ "$CONFIG_PCI" = "y" ]; then
-      bool '   PCI Triton IDE Bus Master DMA support' CONFIG_BLK_DEV_TRITON
+      bool '   TRITON chipset DMA support' CONFIG_BLK_DEV_TRITON
+    fi
+    bool '   Other IDE chipset support' CONFIG_IDE_CHIPSETS
+    if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
+      comment 'Note: most of these also require special kernel boot parameters'
+      bool '     DTC2278 chipset support' CONFIG_BLK_DEV_DTC2278
+      bool '     HT6560B chipset support' CONFIG_BLK_DEV_HT6560B
+      bool '     QD6580  chipset support' CONFIG_BLK_DEV_QD6580
+      bool '     UMC8672 chipset support' CONFIG_BLK_DEV_UMC8672
+      bool '     ALI14XX chipset support' CONFIG_BLK_DEV_ALI14XX
     fi
   fi
 fi
diff -u --recursive --new-file v1.3.60/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v1.3.60/linux/drivers/block/Makefile	Tue Jan 23 21:15:36 1996
+++ linux/drivers/block/Makefile	Fri Feb  9 07:31:22 1996
@@ -44,12 +44,36 @@
 L_OBJS += ide.o
 endif
 
-ifeq ($(CONFIG_BLK_DEV_TRITON),y)
-L_OBJS += triton.o
+ifeq ($(CONFIG_BLK_DEV_RZ1000),y)
+L_OBJS += rz1000.o
 endif
 
 ifeq ($(CONFIG_BLK_DEV_CMD640),y)
 L_OBJS += cmd640.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_TRITON),y)
+L_OBJS += triton.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_DTC2278),y)
+L_OBJS += dtc2278.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HT6560B),y)
+L_OBJS += ht6560b.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_QD6580),y)
+L_OBJS += qd6580.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_UMC8672),y)
+L_OBJS += umc8672.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_ALI14XX),y)
+L_OBJS += ali14xx.o
 endif
 
 ifeq ($(CONFIG_BLK_DEV_IDECD),y)
diff -u --recursive --new-file v1.3.60/linux/drivers/block/README.ide linux/drivers/block/README.ide
--- v1.3.60/linux/drivers/block/README.ide	Tue Jan 23 21:15:37 1996
+++ linux/drivers/block/README.ide	Fri Feb  9 07:31:22 1996
@@ -13,7 +13,7 @@
 Major features of ide.c & ide-cd.c:
 
 NEW!	- support for IDE ATAPI *tape* drives, courtesy of Gadi Oxman
-		(run MAKEDEV.ide to create the tape device entries in /dev/)
+		(re-run MAKEDEV.ide to create the tape device entries in /dev/)
 NEW!	- support for up to *four* IDE interfaces on one or more IRQs
 NEW!	- support for any mix of up to *eight* disk and/or cdrom drives
 	- support for reading IDE ATAPI cdrom drives (NEC,MITSUMI,VERTOS,SONY)
@@ -31,28 +31,27 @@
 	- can co-exist with hd.c controlling the first interface
 	- run-time selectable 32bit interface support (using hdparm-2.3)
 NEW!	- support for reliable operation of buggy RZ1000 interfaces
-		- PCI support is automatic
+		- PCI support is automatic when rz1000 support is configured
 NEW!	- support for reliable operation of buggy CMD-640 interfaces
-		- PCI support is automatic
+		- PCI support is automatic when cmd640 support is configured
 		- for VLB, use kernel command line option:   ide0=cmd640_vlb
 		- this support also enables the secondary i/f on most cards
 		- experimental interface timing parameter support
 NEW!	- experimental support for UMC 8672 interfaces
 NEW!	- support for secondary interface on the FGI/Holtek HT-6560B VLB i/f
-		- use kernel command line option:   ide1=ht6560
-NEW!	- experimental "fast" speed support for QD6580 interfaces
-		- use kernel command line option:   ide0=qd6580
-NEW!	- experimental support for DTC-2278D interfaces
-		- use kernel command line option:   ide1=dtc2278
+		- use kernel command line option:   ide0=ht6560
+NEW!	- experimental support for various IDE chipsets
+		- use appropriate kernel command line option from list below
 NEW!	- support for drives with a stuck WRERR_STAT bit
 NEW!	- support for removeable devices, including door lock/unlock
 NEW!	- transparent support for DiskManager 6.0x and "Dynamic Disk Overlay"
 	- works with Linux fdisk, LILO, loadlin, bootln, etc..
-NEW!	- mostly transparent support for EZ-Drive
+NEW!	- mostly transparent support for EZ-Drive disk translation software
 NEW!		- to use LILO with EZ, install LILO on the linux partition
 		  rather than on the master boot record, and then mark the
 		  linux partition as "bootable" or "active" using fdisk.
 		  (courtesy of Juha Laiho <jlaiho@ichaos.nullnet.fi>).
+NEW!	- auto-detect of disk translations by examining partition table
 NEW!	- ide-cd.c now compiles separate from ide.c
 NEW!	- Bus-Master DMA support for Intel PCI Triton chipset IDE interfaces
 		- for details, see comments at top of triton.c
@@ -222,36 +221,48 @@
 
 Summary of ide driver parameters for kernel "command line":
 ----------------------------------------------------------
+ "hdx="  is recognized for all "x" from "a" to "h", such as "hdc".
+ "idex=" is recognized for all "x" from "0" to "3", such as "ide1".
+
+ "hdx=noprobe"		: drive may be present, but do not probe for it
+ "hdx=nowerr"		: ignore the WRERR_STAT bit on this drive
+ "hdx=cdrom"		: drive is present, and is a cdrom drive
+ "hdx=cyl,head,sect"	: disk drive is present, with specified geometry
+ "hdx=autotune"		: driver will attempt to tune interface speed
+				to the fastest PIO mode supported,
+				if possible for this drive only.
+				Not fully supported by all chipset types,
+				and quite likely to cause trouble with
+				older/odd IDE drives.
+
+ "idex=noprobe"		: do not attempt to access/use this interface
+ "idex=base"		: probe for an interface at the addr specified,
+				where "base" is usually 0x1f0 or 0x170
+				and "ctl" is assumed to be "base"+0x206
+ "idex=base,ctl"	: specify both base and ctl
+ "idex=base,ctl,irq"	: specify base, ctl, and irq number
+ "idex=autotune"	: driver will attempt to tune interface speed
+				to the fastest PIO mode supported,
+				for all drives on this interface.
+				Not fully supported by all chipset types,
+				and quite likely to cause trouble with
+				older/odd IDE drives.
+ "idex=noautotune"	: driver will NOT attempt to tune interface speed
+				This is the default for most chipsets,
+				except the cmd640.
+
+ 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
+			  (not for PCI -- automatically detected)
+ "ide0=qd6580"		: probe/support qd6580 interface
+ "ide0=ali14xx"		: probe/support ali14xx chipsets (ALI M1439, M1443, M1445)
+ "ide0=umc8672"		: probe/support umc8672 chipsets
 
-  "hdx="  is recognized for all "x" from "a" to "h", such as "hdc".
-  "idex=" is recognized for all "x" from "0" to "3", such as "ide1".
- 
-  "hdx=noprobe"        : drive may be present, but do not probe for it
-  "hdx=nowerr"         : ignore the WRERR_STAT bit on this drive
-  "hdx=cdrom"          : drive is present, and is a cdrom drive
-  "hdx=cyl,head,sect"  : disk drive is present, with specified geometry
- 
-  "idex=noprobe"       : do not attempt to access/use this interface
-  "idex=base"          : probe for an interface at the addr specified,
-                               where "base" is usually 0x1f0 or 0x170
-                               and "ctl" is assumed to be "base"+0x206
-  "idex=base,ctl"      : specify both base and ctl
-  "idex=base,ctl,irq"  : specify base, ctl, and irq number
- 
-  The following two are valid ONLY on ide0 or ide1,
-  and the defaults for the base,ctl ports must not be altered.
- 
-  "idex=serialize"     : do not overlap operations on ide0 and ide1.
-  "idex=dtc2278"       : enables use of DTC2278 secondary i/f
-  "idex=ht6560b"       : enables use of HT6560B secondary i/f
-  "idex=cmd640_vlb"    : required for VLB cards with the CMD640 chip
-                         (not for PCI -- automatically detected)
- 
-  This option is valid ONLY on ide0, and the defaults for the base,ctl ports 
-  must not be altered.
-  
-  "ide0=qd6580"        : select "fast" interface speed on a qd6580 interface
- 
 Everything else is rejected with a "BAD OPTION" message.
 
 ================================================================================
diff -u --recursive --new-file v1.3.60/linux/drivers/block/ali14xx.c linux/drivers/block/ali14xx.c
--- v1.3.60/linux/drivers/block/ali14xx.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/ali14xx.c	Fri Feb  9 07:31:22 1996
@@ -0,0 +1,222 @@
+/*
+ *  linux/drivers/block/ali14xx.c       Version 0.01  Feb 06, 1996
+ *
+ *  Copyright (C) 1996  Linus Torvalds & author (see below)
+ */
+
+/*
+ * ALI M14xx chipset EIDE controller
+ *
+ * Adapted from code developed by derekn@vw.ece.cmu.edu.  -ml
+ * Derek's notes follow:
+ *
+ * I think the code should be pretty understandable,
+ * but I'll be happy to (try to) answer questions.
+ *
+ * The critical part is in the setupDrive function.  The initRegisters
+ * function doesn't seem to be necessary, but the DOS driver does it, so
+ * I threw it in.
+ *
+ * I've only tested this on my system, which only has one disk.  I posted
+ * it to comp.sys.linux.hardware, so maybe some other people will try it
+ * out.
+ *
+ * Derek Noonburg  (derekn@ece.cmu.edu)
+ * 95-sep-26
+ */
+
+#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"
+
+#define ALI_14xx_BUS_SPEED	40	/* PCI / VLB bus speed */
+
+/* port addresses for auto-detection */
+#define ALI_NUM_PORTS 4
+static int ports[ALI_NUM_PORTS] = {0x074, 0x0f4, 0x034, 0x0e4};
+
+/* register initialization data */
+typedef struct { byte reg, data; } RegInitializer;
+
+static RegInitializer initData[] = {
+	{0x01, 0x0f}, {0x02, 0x00}, {0x03, 0x00}, {0x04, 0x00},
+	{0x05, 0x00}, {0x06, 0x00}, {0x07, 0x2b}, {0x0a, 0x0f},
+	{0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00}, {0x28, 0x00},
+	{0x29, 0x00}, {0x2a, 0x00}, {0x2f, 0x00}, {0x2b, 0x00},
+	{0x2c, 0x00}, {0x2d, 0x00}, {0x2e, 0x00}, {0x30, 0x00},
+	{0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00}, {0x34, 0xff},
+	{0x35, 0x03}, {0x00, 0x00}
+};
+
+/* default timing parameters for each PIO mode */
+static struct { int time1, time2; } timeTab[4] = {
+	{600, 165},	/* PIO 0 */
+	{383, 125},	/* PIO 1 */
+	{240, 100},	/* PIO 2 */
+	{180,  80}	/* PIO 3 */
+};
+
+/* timing parameter registers for each drive */
+static struct { byte reg1, reg2, reg3, reg4; } regTab[4] = {
+	{0x03, 0x26, 0x04, 0x27},     /* drive 0 */
+	{0x05, 0x28, 0x06, 0x29},     /* drive 1 */
+	{0x2b, 0x30, 0x2c, 0x31},     /* drive 2 */
+	{0x2d, 0x32, 0x2e, 0x33},     /* drive 3 */
+};
+
+static int basePort = 0;	/* base port address */
+static int regPort = 0;		/* port for register number */
+static int dataPort = 0;	/* port for register data */
+static byte regOn;	/* output to base port to access registers */
+static byte regOff;	/* output to base port to close registers */
+
+/*------------------------------------------------------------------------*/
+
+/*
+ * Read a controller register.
+ */
+static inline byte inReg (byte reg)
+{
+	outb_p(reg, regPort);
+	return inb(dataPort);
+}
+
+/*
+ * Write a controller register.
+ */
+static void outReg (byte data, byte reg)
+{
+	outb_p(reg, regPort);
+	outb_p(data, dataPort);
+}
+
+/*
+ * Set PIO mode for the specified drive.
+ * This function computes timing parameters
+ * and sets controller registers accordingly.
+ */
+static void ali14xx_tune_drive (ide_drive_t *drive, byte pio)
+{
+	int driveNum;
+	int time1, time2, time1a;
+	byte param1, param2, param3, param4;
+	struct hd_driveid *id = drive->id;
+	unsigned long flags;
+
+	if (pio == 255)  {	/* auto-tune */
+		pio = id->tPIO;
+		if ((id->field_valid & 0x02) && (id->eide_pio_modes & 0x01))
+			pio = 3;
+	}
+	/* calculate timing, according to PIO mode */
+	time1 = timeTab[pio].time1;
+	time2 = timeTab[pio].time2;
+	if (pio == 3) {
+		time1a = (id->capability & 0x08) ? id->eide_pio_iordy : id->eide_pio;
+		if (time1a != 0 && time1a < time1)
+			time1 = time1a;
+	}
+	param3 = param1 = (time2 * ALI_14xx_BUS_SPEED + 999) / 1000;
+	param4 = param2 = (time1 * ALI_14xx_BUS_SPEED + 999) / 1000 - param1;
+	if (pio != 3) {
+		param3 += 8;
+		param4 += 8;
+	}
+	printk("%s: PIO mode%d, t1=%dns, t2=%dns, cycles = %d+%d, %d+%d\n",
+		drive->name, pio, time1, time2, param1, param2, param3, param4);
+
+	/* stuff timing parameters into controller registers */
+	driveNum = (HWIF(drive)->index << 1) + drive->select.b.unit;
+	save_flags(flags);
+	cli();
+	outb_p(regOn, basePort);
+	outReg(param1, regTab[driveNum].reg1);
+	outReg(param2, regTab[driveNum].reg2);
+	outReg(param3, regTab[driveNum].reg3);
+	outReg(param4, regTab[driveNum].reg4);
+	outb_p(regOff, basePort);
+	restore_flags(flags);
+}
+
+/*
+ * Auto-detect the IDE controller port.
+ */
+static int findPort (void)
+{
+	int i;
+	byte t;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < ALI_NUM_PORTS; ++i) {
+		basePort = ports[i];
+		regOff = inb(basePort);
+		for (regOn = 0x30; regOn <= 0x33; ++regOn) {
+			outb_p(regOn, basePort);
+			if (inb(basePort) == regOn) {
+				regPort = basePort + 4;
+				dataPort = basePort + 8;
+				t = inReg(0) & 0xf0;
+				outb_p(regOff, basePort);
+				restore_flags(flags);
+				if (t != 0x50)
+					return 0;
+				return 1;  /* success */
+			}
+		}
+		outb_p(regOff, basePort);
+	}
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * Initialize controller registers with default values.
+ */
+static int initRegisters (void) {
+	RegInitializer *p;
+	byte t;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outb_p(regOn, basePort);
+	for (p = initData; p->reg != 0; ++p)
+		outReg(p->data, p->reg);
+	outb_p(0x01, regPort);
+	t = inb(regPort) & 0x01;
+	outb_p(regOff, basePort);
+	restore_flags(flags);
+	return t;
+}
+
+void init_ali14xx (void)
+{
+	/* auto-detect IDE controller port */
+	if (!findPort()) {
+		printk("ali14xx: not found\n");
+		return;
+	}
+
+	printk("ali14xx: base= 0x%03x, regOn = 0x%02x\n", basePort, regOn);
+	ide_hwifs[0].chipset = ide_ali14xx;
+	ide_hwifs[1].chipset = ide_ali14xx;
+	ide_hwifs[0].tuneproc = &ali14xx_tune_drive;
+	ide_hwifs[1].tuneproc = &ali14xx_tune_drive;
+
+	/* initialize controller registers */
+	if (!initRegisters()) {
+		printk("ali14xx: Chip initialization failed\n");
+		return;
+	}
+}
diff -u --recursive --new-file v1.3.60/linux/drivers/block/cmd640.c linux/drivers/block/cmd640.c
--- v1.3.60/linux/drivers/block/cmd640.c	Tue Jan 23 21:15:37 1996
+++ linux/drivers/block/cmd640.c	Fri Feb  9 07:31:22 1996
@@ -1,11 +1,11 @@
 /*
- *  linux/drivers/block/cmd640.c	Version 0.04  Jan 11, 1996
+ *  linux/drivers/block/cmd640.c	Version 0.07  Jan 27, 1996
  *
  *  Copyright (C) 1995-1996  Linus Torvalds & author (see below)
  */
 
 /*
- *  Principal Author/Maintainer:  abramov@cecmow.enet.dec.com (Igor)
+ *  Principal Author/Maintainer:  abramov@cecmow.enet.dec.com (Igor Abramov)
  *
  *  This file provides support for the advanced features and bugs
  *  of IDE interfaces using the CMD Technologies 0640 IDE interface chip.
@@ -23,26 +23,27 @@
  *			with read ahead mode. Separate function for setting
  *			readahead is added, possibly it will be called some
  *			day from ioctl processing code.
+ *  
+ *  Version 0.04	Now configs/compiles separate from ide.c  -ml 
  *
- *  Version 0.04	Now configs/compiles separate from ide.c  -ml
- */
-
-/*
- *  There is a known problem with current version of this driver.
- *  If the only device on secondary interface is CD-ROM, at some
- *  computers it is not recognized. In all reported cases CD-ROM
- *  was 2x or 4x speed Mitsumi drive.
+ *  Version 0.05	Major rewrite of interface timing code.
+ *			Added new function cmd640_set_mode to set PIO mode
+ *			from ioctl call. New drives added to black list.
  *
- *  The following workarounds could work:
+ *  Version 0.06	More code cleanup. Readahead is enabled only for
+ *			detected hard drives, not included in readahed
+ *			black list.
  * 
- *    1. put CD-ROM as slave on primary interface
+ *  Version 0.07	Changed to more conservative drive tuning policy.
+ *			Unknown drives, which report PIO < 4 are set to 
+ *			(reported_PIO - 1) if it is supported, or to PIO0.
+ *			List of known drives extended by info provided by
+ *			CMD at their ftp site.
  *
- *    2. or define symbol at next line as 0
- * 
+ *  Version 0.08	Added autotune/noautotune support.  -ml
+ *			
  */
 
-#define CMD640_NORMAL_INIT 1
-
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
 
 #include <linux/types.h>
@@ -54,11 +55,8 @@
 #include <linux/blkdev.h>
 #include <linux/hdreg.h>
 #include <asm/io.h>
-
 #include "ide.h"
 
-extern ide_hwif_t ide_hwifs[];
-
 int cmd640_vlb = 0;
 
 /*
@@ -104,63 +102,64 @@
 #define DRWTIM23	0x58
 #define BRST		0x59
 
+static ide_tuneproc_t cmd640_tune_drive;
+
 /* Interface to access cmd640x registers */
-static void (*put_cmd640_reg)(int key, int reg_no, int val);
-static byte (*get_cmd640_reg)(int key, int reg_no);
+static void (*put_cmd640_reg)(int reg_no, int val);
+static byte (*get_cmd640_reg)(int reg_no);
 
 enum { none, vlb, pci1, pci2 };
 static int	bus_type = none;
 static int	cmd640_chip_version;
 static int	cmd640_key;
-static byte	is_cmd640[MAX_HWIFS];
 static int 	bus_speed; /* MHz */
 
 /*
  * For some unknown reasons pcibios functions which read and write registers
- * do not always work with cmd640. We use direct io instead.
+ * do not always work with cmd640. We use direct IO instead.
  */
 
 /* PCI method 1 access */
 
-static void put_cmd640_reg_pci1(int key, int reg_no, int val)
+static void put_cmd640_reg_pci1(int reg_no, int val)
 {
 	unsigned long flags;
 
 	save_flags(flags);
 	cli();
-	outl_p((reg_no & 0xfc) | key, 0xcf8);
+	outl_p((reg_no & 0xfc) | cmd640_key, 0xcf8);
 	outb_p(val, (reg_no & 3) + 0xcfc);
 	restore_flags(flags);
 }
 
-static byte get_cmd640_reg_pci1(int key, int reg_no)
+static byte get_cmd640_reg_pci1(int reg_no)
 {
 	byte b;
 	unsigned long flags;
 
 	save_flags(flags);
 	cli();
-	outl_p((reg_no & 0xfc) | key, 0xcf8);
+	outl_p((reg_no & 0xfc) | cmd640_key, 0xcf8);
 	b = inb_p(0xcfc + (reg_no & 3));
 	restore_flags(flags);
 	return b;
 }
 
 /* PCI method 2 access (from CMD datasheet) */
-
-static void put_cmd640_reg_pci2(int key, int reg_no, int val)
+ 
+static void put_cmd640_reg_pci2(int reg_no, int val)
 {
 	unsigned long flags;
 
 	save_flags(flags);
 	cli();
 	outb_p(0x10, 0xcf8);
-	outb_p(val, key + reg_no);
+	outb_p(val, cmd640_key + reg_no);
 	outb_p(0, 0xcf8);
 	restore_flags(flags);
 }
 
-static byte get_cmd640_reg_pci2(int key, int reg_no)
+static byte get_cmd640_reg_pci2(int reg_no)
 {
 	byte b;
 	unsigned long flags;
@@ -168,7 +167,7 @@
 	save_flags(flags);
 	cli();
 	outb_p(0x10, 0xcf8);
-	b = inb_p(key + reg_no);
+	b = inb_p(cmd640_key + reg_no);
 	outb_p(0, 0xcf8);
 	restore_flags(flags);
 	return b;
@@ -176,26 +175,26 @@
 
 /* VLB access */
 
-static void put_cmd640_reg_vlb(int key, int reg_no, int val)
+static void put_cmd640_reg_vlb(int reg_no, int val)
 {
 	unsigned long flags;
 
 	save_flags(flags);
 	cli();
-	outb_p(reg_no, key + 8);
-	outb_p(val, key + 0xc);
+	outb_p(reg_no, cmd640_key + 8);
+	outb_p(val, cmd640_key + 0xc);
 	restore_flags(flags);
 }
 
-static byte get_cmd640_reg_vlb(int key, int reg_no)
+static byte get_cmd640_reg_vlb(int reg_no)
 {
 	byte b;
 	unsigned long flags;
 
 	save_flags(flags);
 	cli();
-	outb_p(reg_no, key + 8);
-	b = inb_p(key + 0xc);
+	outb_p(reg_no, cmd640_key + 8);
+	b = inb_p(cmd640_key + 0xc);
 	restore_flags(flags);
 	return b;
 }
@@ -305,13 +304,8 @@
 
 int ide_probe_for_cmd640x(void)
 {
-	int  i;
 	int  second_port;
-	int  cmd_read_ahead;
-	byte b;
-
-	for (i = 0; i < MAX_HWIFS; i++)
-		is_cmd640[i] = 0;
+        byte b;
 
 	if (probe_for_cmd640_pci1()) {
 		bus_type = pci1;
@@ -324,28 +318,46 @@
 		return 0;
 	}
 
+	ide_hwifs[0].serialized = 1;	/* ensure this *always* gets set */
+	
+#if 0	
+	/* Dump initial state of chip registers */
+	for (b = 0; b != 0xff; b++) {
+		printk(" %2x%c", get_cmd640_reg(b),
+				((b&0xf) == 0xf) ? '\n' : ',');
+	}
+
+#endif
+
 	/*
 	 * Undocumented magic. (There is no 0x5b port in specs)
 	 */
 
-	put_cmd640_reg(cmd640_key, 0x5b, 0xbd);
-	if (get_cmd640_reg(cmd640_key, 0x5b) != 0xbd) {
+	put_cmd640_reg(0x5b, 0xbd);
+	if (get_cmd640_reg(0x5b) != 0xbd) {
 		printk("ide: can't initialize cmd640 -- wrong value in 0x5b\n");
 		return 0;
 	}
-	put_cmd640_reg(cmd640_key, 0x5b, 0);
+	put_cmd640_reg(0x5b, 0);
 
 	/*
 	 * Documented magic.
 	 */
 
-	cmd640_chip_version = get_cmd640_reg(cmd640_key, CFR) & CFR_DEVREV;
+	cmd640_chip_version = get_cmd640_reg(CFR) & CFR_DEVREV;
 	if (cmd640_chip_version == 0) {
 		printk ("ide: wrong CMD640 version -- 0\n");
 		return 0;
 	}
 
 	/*
+	 * Setup the most conservative timings for all drives,
+	 */
+	put_cmd640_reg(ARTTIM0, 0xc0);
+	put_cmd640_reg(ARTTIM1, 0xc0);
+	put_cmd640_reg(ARTTIM23, 0xcc); /* 0xc0? */
+
+	/*
 	 * Do not initialize secondary controller for vlbus
 	 */
 	second_port = (bus_type != vlb);
@@ -357,45 +369,51 @@
 	 */
 	bus_speed = (bus_type == vlb) ? 50 : 40; 
 
-	/*
-	 * Enable readahead for versions above 'A'
-	 */
-	cmd_read_ahead = (cmd640_chip_version > 1);
-
-	/*
+        /*
 	 * Setup Control Register
 	 */
-	b = get_cmd640_reg(cmd640_key, CNTRL);	
+	b = get_cmd640_reg(CNTRL);	
 
-#if CMD640_NORMAL_INIT
 	if (second_port)
 		b |= CNTRL_ENA_2ND;
 	else
 		b &= ~CNTRL_ENA_2ND;
-#endif 
 
-	if (cmd_read_ahead)
-		b &= ~(CNTRL_DIS_RA0 | CNTRL_DIS_RA1);
-	else
-		b |= (CNTRL_DIS_RA0 | CNTRL_DIS_RA1);
+	/*
+	 * Disable readahead for drives at primary interface
+	 */
+	b |= (CNTRL_DIS_RA0 | CNTRL_DIS_RA1);
 
+        put_cmd640_reg(CNTRL, b);
 
-	put_cmd640_reg(cmd640_key, CNTRL, b);
+	/*
+	 * Note that we assume that the first interface is at 0x1f0,
+	 * and that the second interface, if enabled, is at 0x170.
+	 */
+	ide_hwifs[0].chipset = ide_cmd640;
+	ide_hwifs[0].tuneproc = &cmd640_tune_drive;
+	if (ide_hwifs[0].drives[0].autotune == 0)
+		ide_hwifs[0].drives[0].autotune = 1;
+	if (ide_hwifs[0].drives[1].autotune == 0)
+		ide_hwifs[0].drives[1].autotune = 1;
 
 	/*
 	 * Initialize 2nd IDE port, if required
 	 */
 	if (second_port) {
-		/* We reset timings, and setup read-ahead */
-		b = cmd_read_ahead ? 0 : (DIS_RA2 | DIS_RA3);
-		put_cmd640_reg(cmd640_key, ARTTIM23, b);
-		put_cmd640_reg(cmd640_key, DRWTIM23, 0);
+		ide_hwifs[1].chipset = ide_cmd640;
+		ide_hwifs[1].tuneproc = &cmd640_tune_drive;
+		if (ide_hwifs[1].drives[0].autotune == 0)
+			ide_hwifs[1].drives[0].autotune = 1;
+		if (ide_hwifs[1].drives[1].autotune == 0)
+			ide_hwifs[1].drives[1].autotune = 1;
+		/* We reset timings, and disable read-ahead */
+		put_cmd640_reg(ARTTIM23, (DIS_RA2 | DIS_RA3));
+		put_cmd640_reg(DRWTIM23, 0);
 
 		cmd640_reset_controller(1);
 	}
 
-	ide_hwifs[0].serialized = 1;
-
 	printk("ide: buggy CMD640%c interface at ", 
 	       'A' - 1 + cmd640_chip_version);
 	switch (bus_type) {
@@ -410,28 +428,28 @@
 			break;
 	}
 
-	is_cmd640[0] = is_cmd640[1] = 1;
-
 	/*
 	 * Reset interface timings
 	 */
-	put_cmd640_reg(cmd640_key, CMDTIM, 0);
+	put_cmd640_reg(CMDTIM, 0);
 
-	printk("\n ... serialized, %s read-ahead, secondary interface %s\n",
-	       cmd_read_ahead ? "enabled" : "disabled",
+	printk("\n ... serialized, secondary interface %s\n",
 	       second_port ? "enabled" : "disabled");
 
 	return 1;
 }
 
-static int as_clocks(int a) {
-	switch (a & 0xc0) {
-		case 0 :	return 4;
-		case 0x40 :	return 2;
-		case 0x80 :	return 3;
-		case 0xc0 :	return 5;
-		default :	return -1;
-	}
+int cmd640_off(void) {
+	static int a = 0;
+	byte b;
+
+	if (bus_type == none || a == 1)
+		return 0;
+	a = 1;
+	b = get_cmd640_reg(CNTRL);	
+	b &= ~CNTRL_ENA_2ND;
+	put_cmd640_reg(CNTRL, b);
+	return 1;
 }
 
 /*
@@ -451,72 +469,122 @@
 	int mask = masks[if_num][dr_num];
 	byte b;
 
-	b = get_cmd640_reg(cmd640_key, port);
+	b = get_cmd640_reg(port);
 	if (mode)
-		b &= mask; /* Enable readahead for specific drive */
+		b &= ~mask;	/* Enable readahead for specific drive */
 	else
-		b |= mask; /* Disable readahed for specific drive */
-	put_cmd640_reg(cmd640_key, port, b);
+		b |= mask;	/* Disable readahed for specific drive */
+	put_cmd640_reg(port, b);
 }		
 
 static struct readahead_black_list {
 	const char* 	name;
 	int		mode;	
 } drives_ra[] = {
-	{ "ST3655A",	0 },
+        { "ST3655A",	0 },
+        { "SAMSUNG",	0 },	/* Be conservative */
  	{ NULL, 0 }
 };	
 
+static int strmatch(const char* pattern, const char* name) {
+	char c1, c2;
+
+	while (1) {
+		c1 = *pattern++;
+		c2 = *name++;
+		if (c1 == 0) {
+			return 0;
+		}
+		if (c1 != c2)
+			return 1;
+	}
+}
+
 static int known_drive_readahead(char* name) {
 	int i;
 
-	for (i = 0; drives_ra[i].name != NULL; i++)
-		if (strcmp(name, drives_ra[i].name) == 0)
+	for (i = 0; drives_ra[i].name != NULL; i++) {
+		if (strmatch(drives_ra[i].name, name) == 0) {
 			return drives_ra[i].mode;
-	return -1;
+		}
+	}
+        return -1;
 }
 
+static int arttim[4]  = {2, 2, 2, 2};	/* Address setup count (in clocks) */
+static int a_count[4] = {1, 1, 1, 1};	/* Active count   (encoded) */
+static int r_count[4] = {1, 1, 1, 1};	/* Recovery count (encoded) */
+
 /*
- * Tuning of drive parameters
+ * Convert address setup count from number of clocks 
+ * to representation used by controller
  */
 
-static void cmd640_set_timing(int if_num, int dr_num, int r1, int r2) {
-	int  b_reg;
-	byte b;
-	int  r52;
-	static int a = 0;
+inline static int pack_arttim(int clocks)
+{
+	if (clocks <= 2) return 0x40;
+	else if (clocks == 3) return 0x80;
+	else if (clocks == 4) return 0x00;
+	else return 0xc0;
+}
+
+/*
+ * Pack active and recovery counts into single byte representation
+ * used by controller
+ */
+
+inline static int pack_counts(int act_count, int rec_count)
+{
+	return ((act_count & 0x0f)<<4) | (rec_count & 0x0f);
+}
+
+inline int max(int a, int b) { return a > b ? a : b; }
+inline int max4(int *p) { return max(p[0], max(p[1], max(p[2], p[3]))); }
+
+/*
+ * Set timing parameters
+ */
+
+static void cmd640_set_timing(int if_num, int dr_num)
+{
+	int	b_reg;
+	int	ac, rc, at;
 
-	b_reg = if_num ? ARTTIM23 : dr_num ? ARTTIM1 : ARTTIM0;
+	/*
+	 * Set address setup count and drive read/write timing registers.
+	 * Primary interface has individual count/timing registers for
+	 * each drive. Secondary interface has common set of registers, and
+	 * we should set timings for the slowest drive.
+	 */
 
 	if (if_num == 0) {
-		put_cmd640_reg(cmd640_key, b_reg, r1);
-		put_cmd640_reg(cmd640_key, b_reg + 1, r2);
+		b_reg = dr_num ? ARTTIM1 : ARTTIM0;
+		at = arttim[dr_num];
+		ac = a_count[dr_num];
+		rc = r_count[dr_num];
 	} else {
-		b = get_cmd640_reg(cmd640_key, b_reg);
-		if (a == 0 || as_clocks(b) < as_clocks(r1))
-			put_cmd640_reg(cmd640_key, b_reg, (b & 0xc0) | r1);
-		
-		if (a == 0) {
-			put_cmd640_reg(cmd640_key, b_reg + 1, r2);
-		} else {
-			b = get_cmd640_reg(cmd640_key, b_reg + 1);
-			r52 =  (b&0x0f) < (r2&0x0f) ? (r2&0x0f) : (b&0x0f);
-			r52 |= (b&0xf0) < (r2&0xf0) ? (r2&0xf0) : (b&0xf0);
-			put_cmd640_reg(cmd640_key, b_reg+1, r52);
-		}
-		a = 1;
+		b_reg = ARTTIM23;
+		at = max(arttim[2], arttim[3]);
+		ac = max(a_count[2], a_count[3]);
+		rc = max(r_count[2], r_count[3]);
 	}
 
-	b = get_cmd640_reg(cmd640_key, CMDTIM);
-	if (b == 0) {
-		put_cmd640_reg(cmd640_key, CMDTIM, r2);
-	} else {
-		r52  = (b&0x0f) < (r2&0x0f) ? (r2&0x0f) : (b&0x0f);
-		r52 |= (b&0xf0) < (r2&0xf0) ? (r2&0xf0) : (b&0xf0);
-		put_cmd640_reg(cmd640_key, CMDTIM, r52);
-	}
+	put_cmd640_reg(b_reg, pack_arttim(at));
+	put_cmd640_reg(b_reg + 1, pack_counts(ac, rc));
+
+	/*
+	 * Update CMDTIM (IDE Command Block Timing Register) 
+	 */
+
+	ac = max4(r_count);
+	rc = max4(a_count);
+	put_cmd640_reg(CMDTIM, pack_counts(ac, rc));
 }
 
+/*
+ * Standard timings for PIO modes
+ */
+
 static struct pio_timing {
 	int	mc_time;	/* Minimal cycle time (ns) */
 	int	av_time;	/* Address valid to DIOR-/DIOW- setup (ns) */
@@ -527,23 +595,74 @@
 	{ 30,	100,	240 },	/* PIO Mode 2 */
 	{ 30,	80,	180 },	/* PIO Mode 3 */
 	{ 25,	70,	125 },	/* PIO Mode 4 */
-	{ 20,	50,	100 }	/* PIO Mode ? */
+	{ 20,	50,	100 }	/* PIO Mode ? (nonstandard) */
 };
 
+/*
+ * Black list. Some drives incorrectly report their maximal PIO modes, at least
+ * in respect to CMD640. Here we keep info on some known drives.
+ */
+
 static struct drive_pio_info {
 	const char	*name;
 	int		pio;
 } drive_pios[] = {
+/*	{ "Conner Peripherals 1275MB - CFS1275A", 4 }, */
+
+	{ "WDC AC2700",  3 },
+	{ "WDC AC2540",  3 },
+	{ "WDC AC2420",  3 },
+	{ "WDC AC2340",  3 },
+	{ "WDC AC2250",  0 },
+	{ "WDC AC2200",  0 },
+	{ "WDC AC2120",  0 },
+	{ "WDC AC2850",  3 },
+	{ "WDC AC1270",  3 },
+	{ "WDC AC1170",  3 },
+	{ "WDC AC1210",  1 },
+	{ "WDC AC280",   0 },
+/*	{ "WDC AC21000", 4 }, */
+	{ "WDC AC31000", 3 },
+/*	{ "WDC AC21200", 4 }, */
+	{ "WDC AC31200", 3 },
+/*	{ "WDC AC31600", 4 }, */
+
 	{ "Maxtor 7131 AT", 1 },
 	{ "Maxtor 7171 AT", 1 },
 	{ "Maxtor 7213 AT", 1 },
 	{ "Maxtor 7245 AT", 1 },
+	{ "Maxtor 7345 AT", 1 },
+	{ "Maxtor 7546 AT", 3 },
+	{ "Maxtor 7540 AV", 3 },
+
+	{ "SAMSUNG SHD-3121A", 1 },
 	{ "SAMSUNG SHD-3122A", 1 },
+	{ "SAMSUNG SHD-3172A", 1 },
+
+/*	{ "ST51080A", 4 },
+ *	{ "ST51270A", 4 },
+ *	{ "ST31220A", 4 },
+ *	{ "ST31640A", 4 },
+ *	{ "ST32140A", 4 },
+ *	{ "ST3780A",  4 },
+ */	{ "ST5660A",  3 },
+	{ "ST3660A",  3 },
+	{ "ST3630A",  3 },
+	{ "ST3655A",  3 },
+	{ "ST3391A",  3 },
+	{ "ST3390A",  1 },
+	{ "ST3600A",  1 },
+	{ "ST3290A",  0 },
+	{ "ST3144A",  0 },
+
 	{ "QUANTUM ELS127A", 0 },
+	{ "QUANTUM ELS170A", 0 },
 	{ "QUANTUM LPS240A", 0 },
+	{ "QUANTUM LPS210A", 3 },
 	{ "QUANTUM LPS270A", 3 },
+	{ "QUANTUM LPS365A", 3 },
 	{ "QUANTUM LPS540A", 3 },
-	{ "QUANTUM FIREBALL1080A", 3 },
+	{ "QUANTUM FIREBALL", 3 }, /* For models 540/640/1080/1280 */
 	{ NULL,	0 }
 };
 
@@ -551,23 +670,18 @@
 	struct drive_pio_info* pi;
 
 	for (pi = drive_pios; pi->name != NULL; pi++) {
-		if (strcmp(pi->name, name) == 0)
+		if (strmatch(pi->name, name) == 0)
 			return pi->pio;
 	}
 	return -1;
 }
 
-static void cmd640_timings_to_regvals(int mc_time, int av_time, int ds_time,
-				int clock_time,
-				int* r1, int* r2)
+static void cmd640_timings_to_clocks(int mc_time, int av_time, int ds_time,
+				int clock_time, int drv_idx)
 {
 	int a, b;
 
-	a = (mc_time + clock_time - 1)/clock_time;
-	if (a <= 2) *r1 = 0x40;
-	else if (a == 3) *r1 = 0x80;
-	else if (a == 4) *r1 = 0;
-	else *r1 = 0xc0;
+	arttim[drv_idx] = (mc_time + clock_time - 1)/clock_time;
 
 	a = (av_time + clock_time - 1)/clock_time;
 	if (a < 2)
@@ -579,13 +693,15 @@
 		a += b - 0x11;
 		b = 0x11;
 	}
-	if (a > 0xf)
-		a = 0;
+	if (a > 0x10)
+		a = 0x10;
 	if (cmd640_chip_version > 1)
 		b -= 1;
-	if (b > 0xf)
-		b = 0;
-	*r2 = (a << 4) | b;
+	if (b > 0x10)
+		b = 0x10;
+
+	a_count[drv_idx] = a;
+	r_count[drv_idx] = b;
 }
 
 static void set_pio_mode(int if_num, int drv_num, int mode_num) {
@@ -601,34 +717,57 @@
 		udelay(10000);
 }
 
-void cmd640_tune_drive(ide_drive_t* drive) {
+/*
+ * Set a specific pio_mode for a drive
+ */
+
+static void cmd640_set_mode(ide_drive_t* drive, int pio_mode) {
+	int interface_number;
+	int drive_number;
+	int clock_time; /* ns */
+	int mc_time, av_time, ds_time;
+
+	interface_number = HWIF(drive)->index;
+	drive_number = drive->select.b.unit;
+	clock_time = 1000/bus_speed;
+
+	mc_time = pio_timings[pio_mode].mc_time;
+	av_time = pio_timings[pio_mode].av_time;
+	ds_time = pio_timings[pio_mode].ds_time;
+
+	cmd640_timings_to_clocks(mc_time, av_time, ds_time, clock_time,
+				interface_number*2 + drive_number);
+	set_pio_mode(interface_number, drive_number, pio_mode);
+	cmd640_set_timing(interface_number, drive_number);
+}
+
+/*
+ * Drive PIO mode "autoconfiguration".
+ * Ideally, this code should *always* call cmd640_set_mode(), but it doesn't.
+ */
+
+static void cmd640_tune_drive(ide_drive_t *drive, byte pio_mode) {
 	int interface_number;
 	int drive_number;
 	int clock_time; /* ns */
 	int max_pio;
 	int mc_time, av_time, ds_time;
 	struct hd_driveid* id;
-	int r1, r2;
-	int mode;
+	int readahead;	/* there is a global named read_ahead */
 
-	/*
-	 * Determine if drive is under cmd640 control
-	 */
-	interface_number = HWIF(drive) - ide_hwifs;
-	if (!is_cmd640[interface_number])
+	if (pio_mode != 255) {
+		cmd640_set_mode(drive, pio_mode);
 		return;
+	}
 
-	drive_number = drive - HWIF(drive)->drives;
+	interface_number = HWIF(drive)->index;
+	drive_number = drive->select.b.unit;
 	clock_time = 1000/bus_speed;
 	id = drive->id;
 	if ((max_pio = known_drive_pio(id->model)) != -1) {
-		mc_time = pio_timings[max_pio].mc_time;
-		av_time = pio_timings[max_pio].av_time;
 		ds_time = pio_timings[max_pio].ds_time;
 	} else {
 		max_pio = id->tPIO;
-		mc_time = pio_timings[max_pio].mc_time;
-		av_time = pio_timings[max_pio].av_time;
 		ds_time = pio_timings[max_pio].ds_time;
 		if (id->field_valid & 2) {
 			if ((id->capability & 8) && (id->eide_pio_modes & 7)) {
@@ -636,26 +775,41 @@
 				else if (id->eide_pio_modes & 2) max_pio = 4;
 				else max_pio = 3;
 				ds_time = id->eide_pio_iordy;
-				mc_time = pio_timings[max_pio].mc_time;
-				av_time = pio_timings[max_pio].av_time;
 			} else {
 				ds_time = id->eide_pio;
 			}
 			if (ds_time == 0)
 				ds_time = pio_timings[max_pio].ds_time;
 		}
+
+		/*
+		 * Conservative "downgrade"
+		 */
+		if (max_pio < 4 && max_pio != 0) {
+			max_pio -= 1;
+			ds_time = pio_timings[max_pio].ds_time;		
+		}
 	}
-	cmd640_timings_to_regvals(mc_time, av_time, ds_time, clock_time,
-				&r1, &r2);
+	mc_time = pio_timings[max_pio].mc_time;
+	av_time = pio_timings[max_pio].av_time;
+	cmd640_timings_to_clocks(mc_time, av_time, ds_time, clock_time,
+				interface_number*2 + drive_number);
 	set_pio_mode(interface_number, drive_number, max_pio);
-	cmd640_set_timing(interface_number, drive_number, r1, r2);
+	cmd640_set_timing(interface_number, drive_number);
 
 	/*
-	 * Disable (or set) readahead mode for known drive
+	 * Disable (or set) readahead mode
 	 */
-	if ((mode = known_drive_readahead(id->model)) != -1) {
-		set_readahead_mode(mode, interface_number, drive_number);
-		printk("Readahead %s,", mode ? "enabled" : "disabled");
-	}
-	printk ("Mode and Timing set to PIO%d (0x%x 0x%x)\n", max_pio, r1, r2);
+
+	readahead = 0;
+	if (cmd640_chip_version > 1) {	/* Mmmm.. probably should be > 2 ?? */
+		readahead = known_drive_readahead(id->model);
+		if (readahead == -1)
+	        	readahead = 1;	/* Mmmm.. probably be 0 ?? */
+		set_readahead_mode(readahead, interface_number, drive_number);
+	}   
+
+	printk ("Mode and Timing set to PIO%d, Readahead is %s\n", 
+		max_pio, readahead ? "enabled" : "disabled");
 }
+
diff -u --recursive --new-file v1.3.60/linux/drivers/block/dtc2278.c linux/drivers/block/dtc2278.c
--- v1.3.60/linux/drivers/block/dtc2278.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/dtc2278.c	Fri Feb  9 07:31:22 1996
@@ -0,0 +1,92 @@
+/*
+ *  linux/drivers/block/dtc2278.c       Version 0.01  Feb 06, 1996
+ *
+ *  Copyright (C) 1996  Linus Torvalds & author (see below)
+ */
+
+#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"
+
+/*
+ * From: andy@cercle.cts.com (Dyan Wile)
+ *
+ * Below is a patch for DTC-2278 - alike software-programmable controllers
+ * The code enables the secondary IDE controller and the PIO4 (3?) timings on
+ * the primary (EIDE). You may probably have to enable the 32-bit support to
+ * get the full speed. You better get the disk interrupts disabled ( hdparm -u0
+ * /dev/hd.. ) for the drives connected to the EIDE interface. (I get my
+ * filesystem  corrupted with -u1, but under heavy disk load only :-)
+ *
+ * From: mlord@bnr.ca -- this chipset is now forced to use the "serialize" feature,
+ * which hopefully will make it more reliable to use.. maybe it has the same bugs
+ * as the CMD640B and RZ1000 ??
+ */
+
+static void sub22 (char b, char c)
+{
+	int i;
+
+	for(i = 0; i < 3; ++i) {
+		inb(0x3f6);
+		outb_p(b,0xb0);
+		inb(0x3f6);
+		outb_p(c,0xb4);
+		inb(0x3f6);
+		if(inb(0xb4) == c) {
+			outb_p(7,0xb0);
+			inb(0x3f6);
+			return;	/* success */
+		}
+	}
+}
+
+static void tune_dtc2278 (ide_drive_t *drive, byte pio_mode)
+{
+	unsigned long flags;
+
+	if (pio_mode != 255) {	/* auto-tune not yet supported here */
+		if (pio_mode >= 3) {
+			save_flags(flags);
+			cli();
+			/*
+			 * This enables PIO mode4 (3?) on the first interface
+			 */
+			sub22(1,0xc3);
+			sub22(0,0xa0);
+			restore_flags(flags);
+		} else {
+			/* we don't know how to set it back again.. */
+		}
+	}
+}
+
+void init_dtc2278 (void)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	/*
+	 * This enables the second interface
+	 */
+	outb_p(4,0xb0);
+	inb(0x3f6);
+	outb_p(0x20,0xb4);
+	inb(0x3f6);
+	restore_flags(flags);
+
+	ide_hwifs[0].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.60/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v1.3.60/linux/drivers/block/floppy.c	Wed Feb  7 15:11:21 1996
+++ linux/drivers/block/floppy.c	Thu Feb  8 04:43:10 1996
@@ -3682,7 +3682,7 @@
 		case 0x0:
 			output_byte(FD_SAVE);
 			r = result();
-			if (r != 17) {
+			if (r != 16) {
 				printk("FDC %d init: SAVE: unexpected return of %d bytes.\n", fdc, r);
 				return FDC_UNKNOWN;
 			}
diff -u --recursive --new-file v1.3.60/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v1.3.60/linux/drivers/block/genhd.c	Wed Dec 27 09:12:13 1995
+++ linux/drivers/block/genhd.c	Fri Feb  9 07:31:22 1996
@@ -14,6 +14,8 @@
  *  /dev/hda *must* have a "DOS" type 0x51 partition in the first slot (hda1).
  *
  *  More flexible handling of extended partitions - aeb, 950831
+ *
+ *  Check partition table on IDE disks for common CHS translations
  */
 
 #include <linux/config.h>
@@ -198,7 +200,7 @@
 	unsigned char *data;
 	int mask = (1 << hd->minor_shift) - 1;
 #ifdef CONFIG_BLK_DEV_IDE
-	int tested_for_dm6 = 0;
+	int tested_for_xlate = 0;
 
 read_mbr:
 #endif
@@ -221,12 +223,12 @@
 	p = (struct partition *) (0x1be + data);
 
 #ifdef CONFIG_BLK_DEV_IDE
-	/*
-	 *  Check for Disk Manager v6.0x (or EZ-DRIVE) with geometry translation
-	 */
-	if (!tested_for_dm6++) {	/* only check for DM6 *once* */
+	if (!tested_for_xlate++) {	/* Do this only once per disk */
+		/*
+		 * Look for various forms of IDE disk geometry translation
+		 */
 		extern int ide_xlate_1024(kdev_t, int, const char *);
-		/* check for various "disk managers" which do strange things */
+		unsigned int sig = *(unsigned short *)(data + 2);
 		if (p->sys_ind == EZD_PARTITION) {
 			/*
 			 * The remainder of the disk must be accessed using
@@ -256,20 +258,33 @@
 				brelse(bh);
 				goto read_mbr;	/* start over with new MBR */
 			}
-		} else {
-			/* look for DM6 signature in MBR, courtesy of OnTrack */
-			unsigned int sig = *(unsigned short *)(data + 2);
-			if (sig <= 0x1ae
-			 && *(unsigned short *)(data + sig) == 0x55AA
+		} else if (sig <= 0x1ae && *(unsigned short *)(data + sig) == 0x55AA
 			 && (1 & *(unsigned char *)(data + sig + 2)) ) 
-			{
-				(void) ide_xlate_1024 (dev, 0, " [DM6:MBR]");
-			} else {
-				/* look for DM6 AUX partition type in slot 1 */
-				if (p->sys_ind == DM6_AUX1PARTITION
-				 || p->sys_ind == DM6_AUX3PARTITION)
-				{
-					(void)ide_xlate_1024(dev, 0, " [DM6:AUX]");
+		{
+			/*
+			 * DM6 signature in MBR, courtesy of OnTrack
+			 */
+			(void) ide_xlate_1024 (dev, 0, " [DM6:MBR]");
+		} else if (p->sys_ind == DM6_AUX1PARTITION || p->sys_ind == DM6_AUX3PARTITION) {
+			/*
+			 * DM6 on other than the first (boot) drive
+			 */
+			(void) ide_xlate_1024(dev, 0, " [DM6:AUX]");
+		} else {
+			/*
+			 * Examine the partition table for common translations.
+			 * This is necessary for drives for situations where
+			 * the translated geometry is unavailable from the BIOS.
+			 */
+			for (i = 0; i < 4 ; i++) {
+				struct partition *q = &p[i];
+				if (NR_SECTS(q) && q->sector == 1 && q->end_sector == 63) {
+					unsigned int heads = q->end_head + 1;
+					if (heads == 32 || heads == 64 || heads == 128) {
+
+						(void) ide_xlate_1024(dev, heads, " [PTBL]");
+						break;
+					}
 				}
 			}
 		}
diff -u --recursive --new-file v1.3.60/linux/drivers/block/ht6560b.c linux/drivers/block/ht6560b.c
--- v1.3.60/linux/drivers/block/ht6560b.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/ht6560b.c	Fri Feb  9 07:31:22 1996
@@ -0,0 +1,137 @@
+/*
+ *  linux/drivers/block/ht6580.c       Version 0.01  Feb 06, 1996
+ *
+ *  Copyright (C) 1995-1996  Linus Torvalds & author (see below)
+ */
+
+#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"
+
+/*
+ * This routine handles interface switching for the peculiar hardware design
+ * on the F.G.I./Holtek HT-6560B VLB IDE interface.
+ * The HT-6560B can only enable one IDE port at a time, and requires a
+ * silly sequence (below) whenever we switch between primary and secondary.
+ *
+ * This stuff is courtesy of malafoss@snakemail.hut.fi
+ *
+ * At least one user has reported that this code can confuse the floppy
+ * controller and/or driver -- perhaps this should be changed to use
+ * a read-modify-write sequence, so as not to disturb other bits in the reg?
+ */
+
+/*
+ * We don't know what all of the bits are for, but we *do* know about these:
+ *	bit5 (0x20): "1" selects slower speed (?)
+ *	bit0 (0x01): "1" selects second interface
+ */
+static byte qd6560b_selects [2][MAX_DRIVES] = {{0x3c,0x3c}, {0x3d,0x3d}};
+
+static void qd6560b_selectproc (ide_drive_t *drive)	/* called from ide.c */
+{
+	static byte current_select = 0;
+	byte drive_select = qd6560b_selects[HWIF(drive)->index][drive->select.b.unit];
+
+	if (drive_select != current_select) {
+		byte t;
+		unsigned long flags;
+		save_flags (flags);
+		cli();
+		current_select = drive_select;
+		(void) inb(0x3e6);
+		(void) inb(0x3e6);
+		(void) inb(0x3e6);
+		/*
+		 * Note: input bits are reversed to output bits!!
+		 */
+		t = inb(0x3e6) ^ 0x3f;
+		t &= (~0x21);
+		t |= (current_select & 0x21);
+		outb(t,0x3e6);
+		restore_flags (flags);
+	}
+}
+
+/*
+ * Autodetection and initialization of ht6560b
+ */
+int try_to_init_ht6560b(void)
+{
+	byte orig_value;
+	int i;
+
+	/* Autodetect ht6560b */
+	if ((orig_value=inb(0x3e6)) == 0xff)
+		return 0;
+
+	for (i=3;i>0;i--) {
+		outb(0x00,0x3e6);
+		if (!( (~inb(0x3e6)) & 0x3f )) {
+			  outb(orig_value,0x3e6);
+			  return 0;
+		}
+	}
+	outb(0x00,0x3e6);
+	if ((~inb(0x3e6))& 0x3f) {
+		outb(orig_value,0x3e6);
+		return 0;
+	}
+	/* 
+	 * Ht6560b autodetected:
+	 *     reverse input bits to output bits
+	 *     initialize bit1 to 0
+	 */
+	outb((orig_value ^ 0x3f) & 0xfd,0x3e6);
+	printk("ht6560b: detected and initialized\n");
+	return 1;
+}
+
+static void tune_ht6560b (ide_drive_t *drive, byte pio)
+{
+	unsigned int hwif, unit;
+
+	if (pio == 255)  {	/* auto-tune */
+		if (drive->media != ide_disk) {
+			pio = 0; /* cdroms don't like our fast mode */
+		} else {
+			struct hd_driveid *id = drive->id;
+			pio = id->tPIO;
+			if ((id->field_valid & 0x02) && (id->eide_pio_modes & 0x01))
+				pio = 3;
+		}
+	}
+	hwif = HWIF(drive)->index;
+	unit = drive->select.b.unit;
+	if (pio < 3)
+		qd6560b_selects[hwif][unit] |= 0x20;
+	else
+		qd6560b_selects[hwif][unit] &= ~0x20;
+}
+
+void init_ht6560b (void)
+{
+	if (check_region(0x3e6,1)) {
+		printk("\nht6560b: PORT 0x3e6 ALREADY IN USE\n");
+	} else {
+		if (try_to_init_ht6560b()) {
+			request_region(0x3e6, 1, "ht6560b");
+			ide_hwifs[0].chipset = ide_ht6560b;
+			ide_hwifs[1].chipset = ide_ht6560b;
+			ide_hwifs[0].selectproc = &qd6560b_selectproc;
+			ide_hwifs[1].selectproc = &qd6560b_selectproc;
+			ide_hwifs[0].tuneproc = &tune_ht6560b;
+			ide_hwifs[1].tuneproc = &tune_ht6560b;
+			ide_hwifs[0].serialized = 1;
+		}
+	}
+}
diff -u --recursive --new-file v1.3.60/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v1.3.60/linux/drivers/block/ide.c	Tue Jan 23 21:15:37 1996
+++ linux/drivers/block/ide.c	Fri Feb  9 07:31:22 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 5.25  Jan 11, 1996
+ *  linux/drivers/block/ide.c	Version 5.27  Feb  8, 1996
  *
  *  Copyright (C) 1994-1996  Linus Torvalds & authors (see below)
  */
@@ -183,17 +183,30 @@
  *  Version 5.24	fix #if's for SUPPORT_CMD640
  *  Version 5.25	more touch-ups, fix cdrom resets, ...
  *			cmd640.c now configs/compiles separate from ide.c
+ *  Version 5.26	keep_settings now maintains the using_dma flag
+ *			fix [EZD] remap message to only output at boot time
+ *			fix "bad /dev/ entry" message to say hdc, not hdc0
+ *			fix ide_xlate_1024() to respect user specified CHS
+ *			use CHS from partn table if it looks translated
+ *			re-merged flags chipset,vlb_32bit,vlb_sync into io_32bit
+ *			keep track of interface chipset type, when known
+ *			add generic PIO mode "tuneproc" mechanism
+ *			fix cmd640_vlb option
+ *			fix ht6560b support (was completely broken)
+ *			umc8672.c now configures/compiles separate from ide.c
+ *			move dtc2278 support to dtc2278.c
+ *			move ht6560b support to ht6560b.c
+ *			move qd6580  support to qd6580.c
+ *			add  ali14xx support in ali14xx.c
+ * Version 5.27		add [no]autotune parameters to help cmd640
+ *			move rz1000  support to rz1000.c
  *
- *  Driver compile-time options are in ide.h
+ *  Some additional driver compile-time options are in ide.h
  *
  *  To do, in likely order of completion:
- *	- make umc8672.c compile separately from ide.c
- *	- add ALI M1443/1445 chipset support from derekn@vw.ece.cmu.edu
- *	- add ioctls to get/set interface timings on various interfaces
- *	- add Promise Caching controller support from peterd@pnd-pc.demon.co.uk
+ *	- 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
- *	- add new HT6560B code from malafoss@snakemail.hut.fi
- */
+*/
 
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
 
@@ -225,13 +238,12 @@
 
 #include "ide.h"
 
-       ide_hwif_t	ide_hwifs[MAX_HWIFS];		/* hwif info */
 static ide_hwgroup_t	*irq_to_hwgroup [16];
 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};
 static const byte	default_irqs[MAX_HWIFS]     = {14, 15, 11, 10};
-static int		disallow_unmask = 0;	/* for buggy hardware */
+       int		ide_disallow_unmask = 0;	/* for buggy hardware */
 
 #if (DISK_RECOVERY_TIME > 0)
 /*
@@ -303,6 +315,7 @@
 		ide_hwif_t *hwif = &ide_hwifs[h];
 
 		/* fill in any non-zero initial values */
+		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;
@@ -357,10 +370,11 @@
 {
 	unsigned short io_base  = HWIF(drive)->io_base;
 	unsigned short data_reg = io_base+IDE_DATA_OFFSET;
+	byte io_32bit = drive->io_32bit;
 
-	if (drive->vlb_32bit) {
+	if (io_32bit) {
 #ifdef VLB_SYNC
-		if (drive->vlb_sync) {
+		if (io_32bit & 2) {
 			cli();
 			do_vlb_sync(io_base+IDE_NSECTOR_OFFSET);
 			insl(data_reg, buffer, wcount);
@@ -380,10 +394,11 @@
 {
 	unsigned short io_base  = HWIF(drive)->io_base;
 	unsigned short data_reg = io_base+IDE_DATA_OFFSET;
+	byte io_32bit = drive->io_32bit;
 
-	if (drive->vlb_32bit) {
+	if (io_32bit) {
 #ifdef VLB_SYNC
-		if (drive->vlb_sync) {
+		if (io_32bit & 2) {
 			cli();
 			do_vlb_sync(io_base+IDE_NSECTOR_OFFSET);
 			outsl(data_reg, buffer, wcount);
@@ -396,50 +411,6 @@
 		outsw(data_reg, buffer, wcount<<1);
 }
 
-#if SUPPORT_HT6560B
-/*
- * This routine handles interface switching for the peculiar hardware design
- * on the F.G.I./Holtek HT-6560B VLB IDE interface.
- * The HT-6560B can only enable one IDE port at a time, and requires a
- * silly sequence (below) whenever we switch between primary and secondary.
- *
- * Apparently, systems with multiple CMD640 chips may need something similar..
- *
- * This algorithm courtesy of malafoss@snakemail.hut.fi
- *
- * At least one user has reported that this code can confuse the floppy
- * controller and/or driver -- perhaps this should be changed to use
- * a read-modify-write sequence, so as not to disturb other bits in the reg?
- */
-
-void ide_hwif_select (ide_hwif_t *hwif)
-{
-	static byte current_select = 0;
-
-	if (hwif->select != current_select) {
-		byte t;
-		unsigned long flags;
-		save_flags (flags);
-		cli();
-		current_select = hwif->select;
-		(void) inb(0x3e6);
-		(void) inb(0x3e6);
-		(void) inb(0x3e6);
-		/*
-		 * Avoid clobbering existing bits at 0x3e6:
-		 *	bit5 (0x20) - disables fast interface speed
-		 *	bit0 (0x01) - enables secondary interface
-		 *	we don't touch any other bits
-		 */
-		t = inb(0x3e6);
-		t &= (~0x21);
-		t |= (current_select & 0x21);
-		outb(t,0x3e6);
-		restore_flags (flags);
-	}
-}
-#endif /* SUPPORT_HT6560B */
-
 /*
  * This should get invoked any time we exit the driver to
  * wait for an interrupt response from a drive.  handler() points
@@ -713,12 +684,13 @@
 	 */
 	for (unit = 0; unit < MAX_DRIVES; ++unit) {
 		ide_drive_t *rdrive = &hwif->drives[unit];
+		rdrive->special.all = 0;
 		rdrive->special.b.set_geometry = 1;
 		rdrive->special.b.recalibrate  = 1;
-		rdrive->special.b.set_multmode = 0;
 		if (OK_TO_RESET_CONTROLLER)
 			rdrive->mult_count = 0;
 		if (!rdrive->keep_settings) {
+			rdrive->using_dma = 0;
 			rdrive->mult_req = 0;
 			rdrive->unmask = 0;
 		}
@@ -900,14 +872,6 @@
 	if (GET_STAT() & (BUSY_STAT|DRQ_STAT))
 		rq->errors |= ERROR_RESET;	/* Mmmm.. timing problem */
 
-#ifdef CONFIG_BLK_DEV_TRITON
-	if (rq->errors > 3 && drive->using_dma) {	/* DMA troubles? */
-		drive->using_dma = 0;
-		printk("%s: DMA disabled\n", drive->name);
-		--rq->errors;
-		return;
-	}
-#endif /* CONFIG_BLK_DEV_TRITON */
 	if (rq->errors >= ERROR_MAX)
 		ide_end_request(0, HWGROUP(drive));
 	else {
@@ -1035,7 +999,7 @@
 }
 
 /*
- * write_intr() is the handler for disk multwrite interrupts
+ * multwrite_intr() is the handler for disk multwrite interrupts
  */
 static void multwrite_intr (ide_drive_t *drive)
 {
@@ -1134,12 +1098,12 @@
 
 /*
  * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT
- * commands to a drive.  It used to do much more, but has been scaled back
- * in recent updates, and could be completely eliminated with a bit more effort.
+ * commands to a drive.  It used to do much more, but has been scaled back.
  */
 static inline void do_special (ide_drive_t *drive)
 {
 	special_t *s = &drive->special;
+next:
 #ifdef DEBUG
 	printk("%s: do_special: 0x%02x\n", drive->name, s->all);
 #endif
@@ -1157,6 +1121,12 @@
 		if (drive->media == ide_disk) {
 			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;
+		if (tuneproc != NULL)
+			tuneproc(drive, drive->pio_req);
+		goto next;
 	} else if (s->b.set_multmode) {
 		s->b.set_multmode = 0;
 		if (drive->media == ide_disk) {
@@ -1342,19 +1312,17 @@
 	}
 	block += drive->part[minor&PARTN_MASK].start_sect + drive->sect0;
 #if FAKE_FDISK_FOR_EZDRIVE
-	if (block == 0 && drive->ezdrive) {
-		block = 1;
-		printk("%s: [EZD] accessing sector 1 in place of sector 0\n", drive->name);
-	}
+	if (block == 0 && drive->remap_0_to_1)
+		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
-#if SUPPORT_HT6560B
-	if (hwif->select)
-		ide_hwif_select (hwif);
-#endif
 
 #ifdef CONFIG_BLK_DEV_IDETAPE
 	POLL_HWIF_TAPE_DRIVE;	/* macro from ide-tape.h */
@@ -1377,7 +1345,6 @@
 				ide_do_rw_cdrom (drive, block);
 				return;
 #endif /* CONFIG_BLK_DEV_IDECD */
-
 #ifdef CONFIG_BLK_DEV_IDETAPE
 			case ide_tape:
 				idetape_do_request (drive, rq, block);
@@ -1546,14 +1513,14 @@
 	 */
 	do {
 		if (hwif->irq == irq) {
-#if SUPPORT_HT6560B
-			if (hwif->select)
-				ide_hwif_select (hwif);
-#endif
 			for (unit = 0; unit < MAX_DRIVES; ++unit) {
 				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 */
 				if (!OK_STAT(stat=GET_STAT(), drive->ready_stat, BAD_STAT))
 					(void) ide_dump_status(drive, "unexpected_intr", stat);
 				if ((stat & DRQ_STAT))
@@ -1561,6 +1528,10 @@
 			}
 		}
 	} while ((hwif = hwif->next) != hwgroup->hwif);
+#ifdef CONFIG_BLK_DEV_HT6560B
+	if (hwif->selectproc)
+		hwif->selectproc (hwgroup->drive);
+#endif /* CONFIG_BLK_DEV_HT6560B */
 }
 
 /*
@@ -1607,8 +1578,7 @@
 				if (drive->present)
 					return drive;
 			} else if (major == IDE0_MAJOR && unit < 4) {
-				printk("ide: probable bad entry for /dev/hd%c%d\n",
-				 'a' + unit, MINOR(i_rdev) & PARTN_MASK);
+				printk("ide: probable bad entry for /dev/hd%c\n", 'a'+unit);
 				printk("ide: to fix it, run:  /usr/src/linux/drivers/block/MAKEDEV.ide\n");
 			}
 			break;
@@ -1889,8 +1859,8 @@
 		case HDIO_GET_DMA:
 			return write_fs_long(arg, drive->using_dma);
 
-		case HDIO_GET_CHIPSET:
-			return write_fs_long(arg, drive->chipset);
+		case HDIO_GET_32BIT:
+			return write_fs_long(arg, drive->io_32bit);
 
 		case HDIO_GET_MULTCOUNT:
 			return write_fs_long(arg, drive->mult_count);
@@ -1918,7 +1888,7 @@
 		case HDIO_SET_NOWERR:
 			if (arg > 1)
 				return -EINVAL;
-		case HDIO_SET_CHIPSET:
+		case HDIO_SET_32BIT:
 			if (!suser())
 				return -EACCES;
 			if ((MINOR(inode->i_rdev) & PARTN_MASK))
@@ -1937,7 +1907,7 @@
 					drive->keep_settings = arg;
 					break;
 				case HDIO_SET_UNMASKINTR:
-					if (arg && disallow_unmask) {
+					if (arg && ide_disallow_unmask) {
 						restore_flags(flags);
 						return -EPERM;
 					}
@@ -1946,12 +1916,10 @@
 				case HDIO_SET_NOWERR:
 					drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
 					break;
-				case HDIO_SET_CHIPSET:
-					drive->chipset   = arg;
-					drive->vlb_32bit = (arg & 1);
-					drive->vlb_sync  = (arg & 2) >> 1;
+				case HDIO_SET_32BIT:
+					drive->io_32bit = arg;
 #ifndef VLB_SYNC
-					if (drive->vlb_sync)
+					if (arg & 2)
 						printk("%s: VLB_SYNC not supported by this kernel\n", drive->name);
 #endif
 					break;
@@ -1997,6 +1965,19 @@
 			}
 			return err;
 		}
+		case HDIO_SET_PIO_MODE:
+			if (!suser())
+				return -EACCES;
+			if (MINOR(inode->i_rdev) & PARTN_MASK)
+				return -EINVAL;
+			if (!HWIF(drive)->tuneproc)
+				return -ENOSYS;
+			save_flags(flags);
+			cli();
+			drive->pio_req = (int) arg;
+			drive->special.b.set_pio = 1;
+			restore_flags(flags);
+			return 0;
 
 		RO_IOCTLS(inode->i_rdev, arg);
 
@@ -2098,7 +2079,7 @@
 		byte type = (id->config >> 8) & 0x1f;
 		printk("%s: %s, ATAPI ", drive->name, id->model);
 		switch (type) {
-			case 0:				/* Early cdrom models used zero */
+			case 0:		/* Early cdrom models used zero */
 			case 5:
 #ifdef CONFIG_BLK_DEV_IDECD
 				printk ("CDROM drive\n");
@@ -2128,9 +2109,11 @@
 				break;
 #endif /* CONFIG_BLK_DEV_IDETAPE */
 			default:
+				drive->present = 0;
 				printk("Type %d - Unknown device\n", type);
 				return;
 		}
+		drive->present = 0;
 		printk("- not supported by this kernel\n");
 		return;
 	}
@@ -2208,17 +2191,12 @@
 			printk(", DMA");
 	}
 	printk("\n");
-#ifdef CONFIG_BLK_DEV_CMD640
-	{
-		extern void cmd640_tune_drive (ide_drive_t *);
-		cmd640_tune_drive(drive);	/* but can we tune a fish? */
-	}
-#endif
 }
 
 /*
  * Delay for *at least* 10ms.  As we don't know how much time is left
  * until the next tick occurs, we wait an extra tick to be safe.
+ * This is used only during the probing/polling for drives at boot time.
  */
 static void delay_10ms (void)
 {
@@ -2271,7 +2249,12 @@
 	if (OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) {
 		cli();			/* some systems need this */
 		do_identify(drive, cmd); /* drive returned ID */
-		rc = 0;			/* success */
+		if (drive->present && (drive->media == ide_disk || drive->media == ide_cdrom)) {
+			ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
+			if (tuneproc != NULL && drive->autotune == 1)
+				tuneproc(drive, 255);	/* auto-tune PIO mode */
+		}
+		rc = 0;			/* drive responded with ID */
 	} else
 		rc = 2;			/* drive refused ID */
 	if (!HWIF(drive)->irq) {
@@ -2314,10 +2297,10 @@
 		drive->name, drive->present, drive->media,
 		(cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
 #endif
-#if SUPPORT_HT6560B
-	if (HWIF(drive)->select)
-		ide_hwif_select (HWIF(drive));
-#endif
+#ifdef CONFIG_BLK_DEV_HT6560B
+	if (HWIF(drive)->selectproc)
+		HWIF(drive)->selectproc (drive);
+#endif /* CONFIG_BLK_DEV_HT6560B */
 	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) {
@@ -2435,99 +2418,6 @@
 	}
 }
 
-#if SUPPORT_DTC2278
-/*
- * From: andy@cercle.cts.com (Dyan Wile)
- *
- * Below is a patch for DTC-2278 - alike software-programmable controllers
- * The code enables the secondary IDE controller and the PIO4 (3?) timings on
- * the primary (EIDE). You may probably have to enable the 32-bit support to
- * get the full speed. You better get the disk interrupts disabled ( hdparm -u0
- * /dev/hd.. ) for the drives connected to the EIDE interface. (I get my
- * filesystem  corrupted with -u1, but under heavy disk load only :-)
- *
- * From: mlord@bnr.ca -- this chipset is now forced to use the "serialize" feature,
- * which hopefully will make it more reliable to use.. maybe it has the same bugs
- * as the CMD640B and RZ1000 ??
- */
-
-#if SET_DTC2278_MODE4
-static void sub22 (char b, char c)
-{
-	int i;
-
-	for(i = 0; i < 3; ++i) {
-		inb(0x3f6);
-		outb_p(b,0xb0);
-		inb(0x3f6);
-		outb_p(c,0xb4);
-		inb(0x3f6);
-		if(inb(0xb4) == c) {
-			outb_p(7,0xb0);
-			inb(0x3f6);
-			return;	/* success */
-		}
-	}
-}
-#endif /* SET_DTC2278_MODE4 */
-
-static void init_dtc2278 (void)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-#if SET_DTC2278_MODE4
-	/*
-	 * This enables PIO mode4 (3?) on the first interface
-	 */
-	sub22(1,0xc3);
-	sub22(0,0xa0);
-#endif /* SET_DTC2278_MODE4 */
-	/*
-	 * This enables the second interface
-	 */
-	outb_p(4,0xb0);
-	inb(0x3f6);
-	outb_p(0x20,0xb4);
-	inb(0x3f6);
-	restore_flags(flags);
-}
-#endif /* SUPPORT_DTC2278 */
-
-#ifdef SUPPORT_QD6580
-/*
- * QDI QD6580 EIDE controller fast support by Colten Edwards.
- * no net access but I can be reached at pje120@cs.usask.ca
- *
- * I suppose that a IOCTL could be used for this and other
- * cards like it to modify the speed using hdparm.  Someday..
- */
-static void init_qd6580 (void)
-{
-	unsigned long flags;
-
-	/* looks like   0x4f is fast
-	 *              0x3f is medium
-	 *              0x2f is slower
-	 *              0x1f is slower yet
-	 *              ports are 0xb0 0xb2 and 0xb3
-	 */
-
-	save_flags(flags);
-	cli();
-	outb_p(0x8d,0xb0);
-	outb_p(0x0 ,0xb2);
-	outb_p(0x4f,0xb3);	/* select "fast" 0x4f */
-	inb(0x3f6);
-	restore_flags(flags);
-}
-#endif /* SUPPORT_QD6580 */
-
-#ifdef SUPPORT_UMC8672
-#include "umc8672.c"	/* until we tidy up the interface some more */
-#endif
-
 /*
  * stridx() returns the offset of c within s,
  * or -1 if c is '\0' or not found within s.
@@ -2602,6 +2492,12 @@
  * "hdx=nowerr"		: ignore the WRERR_STAT bit on this drive
  * "hdx=cdrom"		: drive is present, and is a cdrom drive
  * "hdx=cyl,head,sect"	: disk drive is present, with specified geometry
+ * "hdx=autotune"	: driver will attempt to tune interface speed
+ *				to the fastest PIO mode supported,
+ *				if possible for this drive only.
+ *				Not fully supported by all chipset types,
+ *				and quite likely to cause trouble with
+ *				older/odd IDE drives.
  *
  * "idex=noprobe"	: do not attempt to access/use this interface
  * "idex=base"		: probe for an interface at the addr specified,
@@ -2609,24 +2505,31 @@
  *				and "ctl" is assumed to be "base"+0x206
  * "idex=base,ctl"	: specify both base and ctl
  * "idex=base,ctl,irq"	: specify base, ctl, and irq number
+ * "idex=autotune"	: driver will attempt to tune interface speed
+ *				to the fastest PIO mode supported,
+ *				for all drives on this interface.
+ *				Not fully supported by all chipset types,
+ *				and quite likely to cause trouble with
+ *				older/odd IDE drives.
+ * "idex=noautotune"	: driver will NOT attempt to tune interface speed
+ *				This is the default for most chipsets,
+ *				except the cmd640.
  *
- * The following two are valid ONLY on ide0 or ide1,
+ * The following two are valid ONLY on ide0,
  * and the defaults for the base,ctl ports must not be altered.
  *
- * "idex=serialize"	: do not overlap operations on ide0 and ide1.
- * "idex=dtc2278"	: enables use of DTC2278 secondary i/f
- * "idex=ht6560b"	: enables use of HT6560B secondary i/f
- * "idex=cmd640_vlb"	: required for VLB cards with the CMD640 chip
+ * "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
  *			  (not for PCI -- automatically detected)
- *
- * This option is valid ONLY on ide0, and the defaults for the base,ctl ports
- * must not be altered.
- *
- * "ide0=qd6580"	: select "fast" interface speed on a qd6580 interface
+ * "ide0=qd6580"	: probe/support qd6580 interface
+ * "ide0=ali14xx"	: probe/support ali14xx chipsets (ALI M1439, M1443, M1445)
+ * "ide0=umc8672"	: probe/support umc8672 chipsets
  */
 void ide_setup (char *s)
 {
-	int vals[3];
+	int i, vals[3];
 	ide_hwif_t *hwif;
 	ide_drive_t *drive;
 	unsigned int hw, unit;
@@ -2640,7 +2543,8 @@
 	 * Look for drive options:  "hdx="
 	 */
 	if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
-		const char *hd_words[] = {"noprobe", "nowerr", "cdrom", "serialize", NULL};
+		const char *hd_words[] = {"noprobe", "nowerr", "cdrom", "serialize",
+						"autotune", "noautotune", NULL};
 		unit = s[2] - 'a';
 		hw   = unit / MAX_DRIVES;
 		unit = unit % MAX_DRIVES;
@@ -2662,12 +2566,19 @@
 			case -4: /* "serialize" */
 				printk(" -- USE \"ide%c=serialize\" INSTEAD", '0'+hw);
 				goto do_serialize;
+			case -5: /* "autotune" */
+				drive->autotune = 1;
+				goto done;
+			case -6: /* "noautotune" */
+				drive->autotune = 2;
+				goto done;
 			case 3: /* cyl,head,sect */
 				drive->media	= ide_disk;
 				drive->cyl	= drive->bios_cyl  = vals[0];
 				drive->head	= drive->bios_head = vals[1];
 				drive->sect	= drive->bios_sect = vals[2];
 				drive->present	= 1;
+				drive->forced_geom = 1;
 				hwif->noprobe = 0;
 				goto done;
 			default:
@@ -2678,69 +2589,97 @@
 	 * Look for interface options:  "idex="
 	 */
 	if (s[0] == 'i' && s[1] == 'd' && s[2] == 'e' && s[3] >= '0' && s[3] <= max_hwif) {
-		const char *ide_words[] = {"noprobe", "serialize", "dtc2278", "ht6560b",
-					"cmd640_vlb", "qd6580", "umc8672", NULL};
+		/*
+		 * 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};
 		hw = s[3] - '0';
 		hwif = &ide_hwifs[hw];
+		i = match_parm(&s[4], ide_words, vals, 3);
+
+		/*
+		 * Cryptic check to ensure chipset not already set for hwif:
+		 */
+		if (i != -1 && i != -2) {
+			if (hwif->chipset != ide_unknown)
+				goto bad_option;
+			if (i < 0 && ide_hwifs[1].chipset != ide_unknown)
+				goto bad_option;
+		}
+		/*
+		 * Interface keywords work only for ide0:
+		 */
+		if (i <= -6 && hw != 0)
+			goto bad_hwif;
 
-		switch (match_parm(&s[4], ide_words, vals, 3)) {
-#if SUPPORT_UMC8672
-			case -7: /* "umc8672" */
-				if (hw != 0) goto bad_hwif;
+		switch (i) {
+#ifdef CONFIG_BLK_DEV_ALI14XX
+			case -10: /* "ali14xx" */
+			{
+				extern void init_ali14xx (void);
+				init_ali14xx();
+				goto done;
+			}
+#endif /* CONFIG_BLK_DEV_ALI14XX */
+#ifdef CONFIG_BLK_DEV_UMC8672
+			case -9: /* "umc8672" */
+			{
+				extern void init_umc8672 (void);
 				init_umc8672();
 				goto done;
-#endif /* SUPPORT_UMC8672 */
-#if SUPPORT_QD6580
-			case -6: /* "qd6580" */
-				if (hw != 0) goto bad_hwif;
-				init_qd6580();
+			}
+#endif /* CONFIG_BLK_DEV_UMC8672 */
+#ifdef CONFIG_BLK_DEV_DTC2278
+			case -8: /* "dtc2278" */
+			{
+				extern void init_dtc2278 (void);
+				init_dtc2278();
 				goto done;
-#endif /* SUPPORT_QD6580 */
+			}
+#endif /* CONFIG_BLK_DEV_DTC2278 */
 #ifdef CONFIG_BLK_DEV_CMD640
-			case -5: /* "cmd640_vlb" */
-				{
-				extern int cmd640_vlb;
-				if (hw > 1) goto bad_hwif;
-					cmd640_vlb = 1;
-				}
-				break;
+			case -7: /* "cmd640_vlb" */
+			{
+				extern int cmd640_vlb; /* flag for cmd640.c */
+				cmd640_vlb = 1;
+				goto done;
+			}
 #endif /* CONFIG_BLK_DEV_CMD640 */
-#if SUPPORT_HT6560B
-			case -4: /* "ht6560b" */
-				if (hw > 1) goto bad_hwif;
-				/*
-				 * Using 0x1c and 0x1d apparently selects a
-				 * faster interface speed than 0x3c and 0x3d.
-				 * (bit5 (0x20) selects fast speed when set)
-				 * (bit0 (0x01) selects second interface)
-				 *
-				 * Need to set these per-drive, rather than
-				 * per-hwif, and also add an ioctl to select
-				 * between them.
-				 */
-				if (check_region(0x3e6,1)) {
-					printk(" -- HT6560 PORT 0x3e6 ALREADY IN USE");
-					goto done;
-				}
-				request_region(0x3e6, 1, hwif->name);
-				ide_hwifs[0].select = 0x1c;
-				ide_hwifs[1].select = 0x3d;
-				goto do_serialize;
-#endif /* SUPPORT_HT6560B */
-#if SUPPORT_DTC2278
-			case -3: /* "dtc2278" */
-				if (hw > 1) goto bad_hwif;
-				init_dtc2278();
-				goto do_serialize;
-#endif /* SUPPORT_DTC2278 */
+#ifdef CONFIG_BLK_DEV_HT6560B
+			case -6: /* "ht6560b" */
+			{
+				extern void init_ht6560b (void);
+				init_ht6560b();
+				goto done;
+			}
+#endif /* CONFIG_BLK_DEV_HT6560B */
+#if CONFIG_BLK_DEV_QD6580
+			case -5: /* "qd6580" (no secondary i/f) */
+			{
+				extern void init_qd6580 (void);
+				init_qd6580();
+				goto done;
+			}
+#endif /* CONFIG_BLK_DEV_QD6580 */
+			case -4: /* "noautotune" */
+				hwif->drives[0].autotune = 2;
+				hwif->drives[1].autotune = 2;
+				goto done;
+			case -3: /* "autotune" */
+				hwif->drives[0].autotune = 1;
+				hwif->drives[1].autotune = 1;
+				goto done;
 			case -2: /* "serialize" */
 			do_serialize:
 				if (hw > 1) goto bad_hwif;
 				ide_hwifs[0].serialized = 1;
 				goto done;
+
 			case -1: /* "noprobe" */
 				hwif->noprobe = 1;
 				goto done;
+
 			case 1:	/* base */
 				vals[1] = vals[0] + 0x206; /* default ctl */
 			case 2: /* base,ctl */
@@ -2749,8 +2688,14 @@
 				hwif->io_base  = vals[0];
 				hwif->ctl_port = vals[1];
 				hwif->irq      = vals[2];
-				hwif->noprobe = 0;
+				hwif->noprobe  = 0;
+				hwif->chipset  = ide_generic;
 				goto done;
+
+			case 0: goto bad_option;
+			default:
+				printk(" -- SUPPORT NOT CONFIGURED IN THIS KERNEL\n");
+				return;
 		}
 	}
 bad_option:
@@ -2764,19 +2709,35 @@
 
 /*
  * This routine is called from the partition-table code in genhd.c
- * to "convert" a drive to a logical geometry with fewer than 1024 cyls
- * It mimics the method used by Ontrack Disk Manager.
+ * to "convert" a drive to a logical geometry with fewer than 1024 cyls.
+ *
+ * The second parameter, "xparm", determines exactly how the translation 
+ * will be handled:
+ *		 0 = convert to CHS with fewer than 1024 cyls
+ *			using the same method as Ontrack DiskManager.
+ *		 1 = same as "0", plus offset everything by 63 sectors.
+ *		-1 = similar to "0", plus redirect sector 0 to sector 1.
+ *		>1 = convert to a CHS geometry with "xparm" heads.
+ *
+ * Returns 0 if the translation was not possible, if the device was not 
+ * an IDE disk drive, or if a geometry was "forced" on the commandline.
+ * Returns 1 if the geometry translation was successful.
  */
-int ide_xlate_1024 (kdev_t i_rdev, int offset, const char *msg)
+int ide_xlate_1024 (kdev_t i_rdev, int xparm, const char *msg)
 {
 	ide_drive_t *drive;
 	static const byte head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
 	const byte *heads = head_vals;
 	unsigned long tracks;
 
-	if ((drive = get_info_ptr(i_rdev)) == NULL)
+	if ((drive = get_info_ptr(i_rdev)) == NULL || drive->forced_geom)
 		return 0;
 
+	if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63)
+		return 0;		/* we already have a translation */
+
+	printk("%s ", msg);
+
 	if (drive->id) {
 		drive->cyl  = drive->id->cyls;
 		drive->head = drive->id->heads;
@@ -2789,25 +2750,31 @@
 
 	tracks = drive->bios_cyl * drive->bios_head * drive->bios_sect / 63;
 	drive->bios_sect = 63;
-	while (drive->bios_cyl >= 1024) {
-		drive->bios_head = *heads;
+	if (xparm > 1) {
+		drive->bios_head = xparm;
 		drive->bios_cyl = tracks / drive->bios_head;
-		if (0 == *++heads)
-			break;
-	}
-	if (offset) {
+	} else {
+		while (drive->bios_cyl >= 1024) {
+			drive->bios_head = *heads;
+			drive->bios_cyl = tracks / drive->bios_head;
+			if (0 == *++heads)
+				break;
+		}
 #if FAKE_FDISK_FOR_EZDRIVE
-		if (offset == -1)
-			drive->ezdrive = 1;
-		else
+		if (xparm == -1) {
+			drive->remap_0_to_1 = 1;
+			msg = "0->1";
+		} else
 #endif /* FAKE_FDISK_FOR_EZDRIVE */
-		{
+		if (xparm == 1) {
 			drive->sect0 = 63;
 			drive->bios_cyl = (tracks - 1) / drive->bios_head;
+			msg = "+63";
 		}
+		printk("[remap %s] ", msg);
 	}
 	drive->part[0].nr_sects = current_capacity(drive);
-	printk("%s [%d/%d/%d]", msg, drive->bios_cyl, drive->bios_head, drive->bios_sect);
+	printk("[%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect);
 	return 1;
 }
 
@@ -2940,37 +2907,7 @@
 };
 
 #ifdef CONFIG_PCI
-
-#if SUPPORT_RZ1000
-
-static void ide_pci_access_error (int rc)
-{
-	printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc));
-}
-
-static void init_rz1000 (byte bus, byte fn)
-{
-	int rc;
-	unsigned short reg;
-
-	printk("ide: buggy RZ1000 interface: ");
-	if ((rc = pcibios_read_config_word (bus, fn, PCI_COMMAND, &reg))) {
-		ide_pci_access_error (rc);
-	} else if (!(reg & 1)) {
-		printk("not enabled\n");
-	} else {
-		if ((rc = pcibios_read_config_word(bus, fn, 0x40, &reg))
-		 || (rc =  pcibios_write_config_word(bus, fn, 0x40, reg & 0xdfff)))
-		{
-			ide_pci_access_error (rc);
-			ide_hwifs[0].serialized = 1;
-			disallow_unmask = 1;
-			printk("serialized, disabled unmasking\n");
-		} else
-			printk("disabled read-ahead\n");
-	}
-}
-#endif /* SUPPORT_RZ1000 */
+#if defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON)
 
 typedef void (ide_pci_init_proc_t)(byte, byte);
 
@@ -2992,27 +2929,44 @@
 	restore_flags(flags);
 }
 
+#endif /* defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) */
+#endif /* CONFIG_PCI */
+
 /*
  * ide_init_pci() finds/initializes "known" PCI IDE interfaces
  *
  * This routine should ideally be using pcibios_find_class() to find
  * all IDE interfaces, but that function causes some systems to "go weird".
  */
-static void ide_init_pci (void)
+static void probe_for_hwifs (void)
 {
-#if SUPPORT_RZ1000
-	ide_probe_pci (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, &init_rz1000, 0);
-#endif
-#ifdef CONFIG_BLK_DEV_TRITON
+#ifdef CONFIG_PCI
 	/*
-	 * Apparently the BIOS32 services on Intel motherboards are buggy,
-	 * and won't find the PCI_DEVICE_ID_INTEL_82371_1 for us.
-	 * So instead, we search for PCI_DEVICE_ID_INTEL_82371_0, and then add 1.
+	 * Find/initialize PCI IDE interfaces
 	 */
-	ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1);
+	if (pcibios_present()) {
+#ifdef CONFIG_BLK_DEV_RZ1000
+		ide_pci_init_proc_t init_rz1000;
+		ide_probe_pci (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, &init_rz1000, 0);
+#endif /* CONFIG_BLK_DEV_RZ1000 */
+#ifdef CONFIG_BLK_DEV_TRITON
+		/*
+		 * Apparently the BIOS32 services on Intel motherboards are
+		 * buggy and won't find the PCI_DEVICE_ID_INTEL_82371_1 for us.
+		 * So instead, we search for PCI_DEVICE_ID_INTEL_82371_0,
+		 * and then add 1.
+		 */
+		ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1);
+#endif /* CONFIG_BLK_DEV_TRITON */
+	}
+#endif /* CONFIG_PCI */
+#ifdef CONFIG_BLK_DEV_CMD640
+	{
+		extern void ide_probe_for_cmd640x (void);
+		ide_probe_for_cmd640x();
+	}
 #endif
 }
-#endif /* CONFIG_PCI */
 
 /*
  * This is gets invoked once during initialization, to set *everything* up
@@ -3023,22 +2977,9 @@
 
 	init_ide_data ();
 	/*
-	 * First, we determine what hardware is present
-	 */
-
-#ifdef CONFIG_PCI
-	/*
-	 * Find/initialize PCI IDE interfaces
+	 * Probe for special "known" interface chipsets
 	 */
-	if (pcibios_present())
-		ide_init_pci ();
-#endif /* CONFIG_PCI */
-#ifdef CONFIG_BLK_DEV_CMD640
-	{
-		extern void ide_probe_for_cmd640x (void);
-		ide_probe_for_cmd640x();
-	}
-#endif
+	probe_for_hwifs ();
 
 	/*
 	 * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports
diff -u --recursive --new-file v1.3.60/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v1.3.60/linux/drivers/block/ide.h	Tue Jan 23 21:15:39 1996
+++ linux/drivers/block/ide.h	Fri Feb  9 17:10:53 1996
@@ -34,24 +34,6 @@
 #ifndef FAKE_FDISK_FOR_EZDRIVE		/* 1 to help linux fdisk with EZDRIVE */
 #define FAKE_FDISK_FOR_EZDRIVE 	1	/* 0 to reduce kernel size */
 #endif
-#ifndef SUPPORT_RZ1000			/* 1 to support RZ1000 chipset */
-#define SUPPORT_RZ1000		1	/* 0 to reduce kernel size */
-#endif
-#ifndef SUPPORT_UMC8672			/* 1 to support UMC8672 chipset */
-#define SUPPORT_UMC8672		1	/* 0 to reduce kernel size */
-#endif
-#ifndef SUPPORT_HT6560B			/* 1 to support HT6560B chipset */
-#define SUPPORT_HT6560B		1	/* 0 to reduce kernel size */
-#endif
-#ifndef SUPPORT_QD6580			/* 1 to support QD6580 chipset */
-#define SUPPORT_QD6580		1	/* 0 to reduce kernel size */
-#endif
-#ifndef SUPPORT_DTC2278			/* 1 to support DTC2278 chipset */
-#define SUPPORT_DTC2278		1	/* 0 to reduce kernel size */
-#ifndef SET_DTC2278_MODE4
-#define SET_DTC2278_MODE4	0	/* 1 to init primary i/f for PIO mode4 */
-#endif
-#endif
 #ifndef FANCY_STATUS_DUMPS		/* 1 for human-readable drive errors */
 #define FANCY_STATUS_DUMPS	1	/* 0 to reduce kernel size */
 #endif
@@ -293,7 +275,8 @@
 		unsigned set_geometry	: 1;	/* respecify drive geometry */
 		unsigned recalibrate	: 1;	/* seek to cyl 0      */
 		unsigned set_multmode	: 1;	/* set multmode count */
-		unsigned reserved	: 5;	/* unused */
+		unsigned set_pio	: 1;	/* set pio mode */
+		unsigned reserved	: 4;	/* unused */
 		} b;
 	} special_t;
 
@@ -310,26 +293,26 @@
 
 typedef struct ide_drive_s {
 	special_t	special;	/* special action flags */
-#if FAKE_FDISK_FOR_EZDRIVE
-	unsigned ezdrive	: 1;	/* flag: partitioned with ezdrive */
-#endif /* FAKE_FDISK_FOR_EZDRIVE */
 	unsigned present	: 1;	/* drive is physically present */
 	unsigned noprobe 	: 1;	/* from:  hdx=noprobe */
 	unsigned keep_settings  : 1;	/* restore settings after drive reset */
 	unsigned busy		: 1;	/* currently doing revalidate_disk() */
-	unsigned vlb_32bit	: 1;	/* use 32bit in/out for data */
-	unsigned vlb_sync	: 1;	/* needed for some 32bit chip sets */
 	unsigned removeable	: 1;	/* 1 if need to do check_media_change */
 	unsigned using_dma	: 1;	/* disk is using dma for read/write */
+	unsigned forced_geom	: 1;	/* 1 if hdx=c,h,s was given at boot */
 	unsigned unmask		: 1;	/* flag: okay to unmask other irqs */
+	unsigned autotune	: 2;	/* 1=autotune, 2=noautotune, 0=default */
+#if FAKE_FDISK_FOR_EZDRIVE
+	unsigned remap_0_to_1	: 1;	/* flag: partitioned with ezdrive */
+#endif /* FAKE_FDISK_FOR_EZDRIVE */
 	ide_media_t	media;		/* disk, cdrom, tape */
 	select_t	select;		/* basic drive/head select reg value */
-	void		*hwif;		/* actually (ide_hwif_t *) */
 	byte		ctl;		/* "normal" value for IDE_CONTROL_REG */
 	byte		ready_stat;	/* min status value for drive ready */
 	byte		mult_count;	/* current multiple sector setting */
 	byte 		mult_req;	/* requested multiple sector setting */
-	byte		chipset;	/* interface chipset access method */
+	byte 		pio_req;	/* requested multiple sector setting */
+	byte		io_32bit;	/* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
 	byte		bad_wstat;	/* used for ignoring WRERR_STAT */
 	byte		sect0;		/* offset of first sector for DM6:DDO */
 	byte 		usage;		/* current "open()" count for drive */
@@ -339,27 +322,16 @@
 	byte		bios_sect;	/* BIOS/fdisk/LILO sectors per track */
 	unsigned short	bios_cyl;	/* BIOS/fdisk/LILO number of cyls */
 	unsigned short	cyl;		/* "real" number of cyls */
+	void		  *hwif;	/* actually (ide_hwif_t *) */
 	struct wait_queue *wqueue;	/* used to wait for drive in open() */
 	struct hd_driveid *id;		/* drive model identification info */
 	struct hd_struct  *part;	/* drive partition table */
 	char		name[4];	/* drive name, such as "hda" */
 #ifdef CONFIG_BLK_DEV_IDECD
-	struct cdrom_info cdrom_info;	/* from ide-cd.c */
+	struct cdrom_info cdrom_info;	/* for ide-cd.c */
 #endif /* CONFIG_BLK_DEV_IDECD */
-
-#ifdef CONFIG_BLK_DEV_IDETAPE		/* ide-tape specific data */
-
-/*
- *	Most of our global data which we need to save even as we leave the
- *	driver due to an interrupt or a timer event is stored here.
- *
- *	Additional global variables which provide the link between the
- *	character device interface to this structure are defined in
- *	ide-tape.c
- */
-
-	idetape_tape_t tape;
-
+#ifdef CONFIG_BLK_DEV_IDETAPE
+	idetape_tape_t	tape;		/* for ide-tape.c */
 #endif /* CONFIG_BLK_DEV_IDETAPE */
 
 	} ide_drive_t;
@@ -378,6 +350,35 @@
 typedef enum {ide_dma_read = 0, ide_dma_write = 1, ide_dma_abort = 2, ide_dma_check = 3} ide_dma_action_t;
 typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *);
 
+
+/*
+ * An ide_tuneproc_t() is used to set the speed of an IDE interface
+ * to a particular PIO mode.  The "byte" parameter is used
+ * to select the PIO mode by number (0,1,2,3,4,5), and a value of 255
+ * indicates that the interface driver should "auto-tune" the PIO mode
+ * according to the drive capabilities in drive->id;
+ *
+ * Not all interface types support tuning, and not all of those
+ * support all possible PIO settings.  They may silently ignore
+ * or round values as they see fit.
+ */
+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.
+ */
+typedef void (ide_selectproc_t) (ide_drive_t *);
+
+/*
+ * hwif_chipset_t is used to keep track of the specific hardware
+ * chipset used by each IDE interface, if known.
+ */
+typedef enum {	ide_unknown,	ide_generic,	ide_triton,
+		ide_cmd640,	ide_dtc2278,	ide_ali14xx,
+		ide_qd6580,	ide_umc8672,	ide_ht6560b }
+	hwif_chipset_t;
+
 typedef struct hwif_s {
 	struct hwif_s	*next;		/* for linked-list in ide_hwgroup_t */
 	void		*hwgroup;	/* actually (ide_hwgroup_t *) */
@@ -385,13 +386,18 @@
 	unsigned short	ctl_port;	/* usually io_base+0x206 */
 	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
+	ide_selectproc_t *selectproc;	/* tweaks hardware to select drive */
+#endif /* CONFIG_BLK_DEV_HT6560B */
 	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) */
 	byte		irq;		/* our irq number */
 	byte		major;		/* our major number */
-	byte		select;		/* pri/sec hwif select for ht6560b */
 	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] */
@@ -423,6 +429,19 @@
 	} ide_hwgroup_t;
 
 /*
+ * ide_hwifs[] is the master data structure used to keep track
+ * of just about everything in ide.c.  Whenever possible, routines
+ * should be using pointers to a drive (ide_drive_t *) or 
+ * pointers to a hwif (ide_hwif_t *), rather than indexing this
+ * structure directly (the allocation/layout may change!).
+ */
+#ifdef _IDE_C
+	ide_hwif_t	ide_hwifs[MAX_HWIFS];	/* master data repository */
+#else
+extern	ide_hwif_t	ide_hwifs[];
+#endif
+
+/*
  * One final include file, which references some of the data/defns from above
  */
 #define IDE_DRIVER	/* "parameter" for blk.h */
@@ -480,9 +499,22 @@
 int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout);
 
 /*
- * This is called from genhd.c to correct DiskManager/EZ-Drive geometries
+ * This routine is called from the partition-table code in genhd.c
+ * to "convert" a drive to a logical geometry with fewer than 1024 cyls.
+ *
+ * The second parameter, "xparm", determines exactly how the translation
+ * will be handled:
+ *		 0 = convert to CHS with fewer than 1024 cyls
+ *			using the same method as Ontrack DiskManager.
+ *		 1 = same as "0", plus offset everything by 63 sectors.
+ *		-1 = similar to "0", plus redirect sector 0 to sector 1.
+ *		>1 = convert to a CHS geometry with "xparm" heads.
+ *
+ * Returns 0 if the translation was not possible, if the device was not
+ * an IDE disk drive, or if a geometry was "forced" on the commandline.
+ * Returns 1 if the geometry translation was successful.
  */
-int ide_xlate_1024(kdev_t, int, const char *);
+int ide_xlate_1024 (kdev_t, int, const char *);
 
 /*
  * Start a reset operation for an IDE interface.
diff -u --recursive --new-file v1.3.60/linux/drivers/block/qd6580.c linux/drivers/block/qd6580.c
--- v1.3.60/linux/drivers/block/qd6580.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/qd6580.c	Fri Feb  9 07:31:22 1996
@@ -0,0 +1,67 @@
+/*
+ *  linux/drivers/block/qd6580.c       Version 0.01  Feb 06, 1996
+ *
+ *  Copyright (C) 1996  Linus Torvalds & author (see below)
+ */
+
+/*
+ * QDI QD6580 EIDE controller fast support by Colten Edwards.
+ * No net access, but (maybe) can be reached at pje120@cs.usask.ca
+ */
+
+#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"
+
+/*
+ * Register 0xb3 looks like:
+ *	0x4f is fast		mode3 ?
+ *	0x3f is medium		mode2 ?
+ *	0x2f is slower		mode1 ?
+ *	0x1f is slower yet	mode0 ?
+ *	0x0f ???		???
+ *
+ * Don't know whether this sets BOTH drives, or just the first drive.
+ * Don't know if there is a separate setting for the second drive.
+ *
+ * Feel free to patch this if you have one of these beasts
+ * and can work out the answers!
+ *
+ * I/O ports are 0xb0 0xb2 and 0xb3
+ */
+
+static void tune_qd6580 (ide_drive_t *drive, byte pio)
+{
+	unsigned long flags;
+
+	if (pio == 255)  {	/* auto-tune */
+		struct hd_driveid *id = drive->id;
+		pio = id->tPIO;
+		if ((id->field_valid & 0x02) && (id->eide_pio_modes & 0x03))
+			pio = 3;
+	}
+	pio++;	/* is this correct? */
+
+	save_flags(flags);
+	cli();
+	outb_p(0x8d,0xb0);
+	outb_p(0x0 ,0xb2);
+	outb_p((pio<<4)|0x0f,0xb3);
+	inb(0x3f6);
+	restore_flags(flags);
+}
+
+void init_qd6580 (void)
+{
+	ide_hwifs[0].chipset = ide_qd6580;
+	ide_hwifs[0].tuneproc = &tune_qd6580;
+}
diff -u --recursive --new-file v1.3.60/linux/drivers/block/rz1000.c linux/drivers/block/rz1000.c
--- v1.3.60/linux/drivers/block/rz1000.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/rz1000.c	Fri Feb  9 07:31:22 1996
@@ -0,0 +1,56 @@
+/*
+ *  linux/drivers/block/rz1000.c	Version 0.02  Feb 08, 1996
+ *
+ *  Copyright (C) 1995-1996  Linus Torvalds & author (see below)
+ */
+
+/*
+ *  Principal Author/Maintainer:  mlord@bnr.ca (Mark Lord)
+ *
+ *  This file provides support for disabling the buggy read-ahead
+ *  mode of the RZ1000 IDE chipset, commonly used on Intel motherboards.
+ */
+
+#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 <linux/bios32.h>
+#include <linux/pci.h>
+#include "ide.h"
+
+static void ide_pci_access_error (int rc)
+{
+	printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc));
+}
+
+void init_rz1000 (byte bus, byte fn)
+{
+	int rc;
+	unsigned short reg;
+
+	printk("ide: buggy RZ1000 interface: ");
+	if ((rc = pcibios_read_config_word (bus, fn, PCI_COMMAND, &reg))) {
+		ide_pci_access_error (rc);
+	} else if (!(reg & 1)) {
+		printk("not enabled\n");
+	} else {
+		if ((rc = pcibios_read_config_word(bus, fn, 0x40, &reg))
+		 || (rc =  pcibios_write_config_word(bus, fn, 0x40, reg & 0xdfff)))
+		{
+			extern int ide_disallow_unmask;
+			ide_disallow_unmask = 1;
+			ide_hwifs[0].serialized = 1;
+			ide_pci_access_error (rc);
+			printk("serialized, disabled unmasking\n");
+		} else
+			printk("disabled read-ahead\n");
+	}
+}
diff -u --recursive --new-file v1.3.60/linux/drivers/block/triton.c linux/drivers/block/triton.c
--- v1.3.60/linux/drivers/block/triton.c	Tue Jan 23 21:15:39 1996
+++ linux/drivers/block/triton.c	Fri Feb  9 07:31:23 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/triton.c	Version 1.05  Jan 11, 1996
+ *  linux/drivers/block/triton.c	Version 1.06  Feb 6, 1996
  *
  *  Copyright (c) 1995-1996  Mark Lord
  *  May be copied or modified under the terms of the GNU General Public License
@@ -319,7 +319,7 @@
 	if (check_region(base, 8)) {
 		printk(" -- ERROR, PORTS ALREADY IN USE");
 	} else {
-		request_region(base, 8, hwif->name);
+		request_region(base, 8, "triton DMA");
 		hwif->dma_base = base;
 		if (!dmatable) {
 			/*
@@ -364,7 +364,6 @@
 	int dma_enabled = 0;
 	unsigned short bmiba, pcicmd;
 	unsigned int timings;
-	extern ide_hwif_t ide_hwifs[];
 
 	printk("ide: Triton BM-IDE on PCI bus %d function %d\n", bus, fn);
 	/*
@@ -409,12 +408,14 @@
 			time = timings & 0xffff;
 			if ((timings & 0x8000) == 0)	/* interface enabled? */
 				continue;
+			hwif->chipset = ide_triton;
 			if (dma_enabled)
 				init_triton_dma(hwif, bmiba);
 		} else if (hwif->io_base == 0x170) {
 			time = timings >> 16;
 			if ((timings & 0x8000) == 0)	/* interface enabled? */
 				continue;
+			hwif->chipset = ide_triton;
 			if (dma_enabled)
 				init_triton_dma(hwif, bmiba + 8);
 		} else
diff -u --recursive --new-file v1.3.60/linux/drivers/block/umc8672.c linux/drivers/block/umc8672.c
--- v1.3.60/linux/drivers/block/umc8672.c	Mon Nov 27 12:48:29 1995
+++ linux/drivers/block/umc8672.c	Fri Feb  9 07:31:23 1996
@@ -1,7 +1,7 @@
 /*
- *  linux/drivers/block/umc8672.c	Version 0.01  Nov 16, 1995
+ *  linux/drivers/block/umc8672.c	Version 0.02  Feb 06, 1996
  *
- *  Copyright (C) 1995  Linus Torvalds & author (see below)
+ *  Copyright (C) 1995-1996  Linus Torvalds & author (see below)
  */
 
 /*
@@ -13,6 +13,8 @@
  *  Version 0.01	Initial version, hacked out of ide.c,
  *			and #include'd rather than compiled separately.
  *			This will get cleaned up in a subsequent release.
+ *
+ *  Version 0.02	now configs/compiles separate from ide.c  -ml
  */
 
 /*
@@ -32,77 +34,122 @@
  * the results from the DOS speed test programm supplied from UMC. 11 is the 
  * highest speed (about PIO mode 3)
  */
+#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"
 
 /*
  * The speeds will eventually become selectable using hdparm via ioctl's,
  * but for now they are coded here:
  */
-#define UMC_DRIVE0      11              /* DOS messured drive Speeds */
-#define UMC_DRIVE1      11              /* 0 - 11 allowed */
-#define UMC_DRIVE2      11              /* 11 = Highest Speed */
-#define UMC_DRIVE3      11              /* In case of crash reduce speed */
+#define UMC_DRIVE0      1              /* DOS measured drive speeds */
+#define UMC_DRIVE1      1              /* 0 to 11 allowed */
+#define UMC_DRIVE2      1              /* 11 = Fastest Speed */
+#define UMC_DRIVE3      1              /* In case of crash reduce speed */
+
+static byte current_speeds[4] = {UMC_DRIVE0, UMC_DRIVE1, UMC_DRIVE2, UMC_DRIVE3};
+static const byte pio_to_umc [5] = {0,3,6,10,11};	/* rough guesses */
+
+/*       0    1    2    3    4    5    6    7    8    9    10   11      */
+static const byte speedtab [3][12] = {
+	{0xf, 0xb, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 },
+	{0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 },
+	{0xff,0xcb,0xc0,0x58,0x36,0x33,0x23,0x22,0x21,0x11,0x10,0x0}};
 
-void out_umc (char port,char wert)
+static void out_umc (char port,char wert)
 {
 	outb_p (port,0x108);
 	outb_p (wert,0x109);
 }
 
-byte in_umc (char port)
+static byte in_umc (char port)
 {
 	outb_p (port,0x108);
 	return inb_p (0x109);
 }
 
-void init_umc8672(void)
+static void umc_set_speeds (byte speeds[])
 {
-	int i,tmp;
-	int speed [4];
-/*      0    1    2    3    4    5    6    7    8    9    10   11      */
-	char speedtab [3][12] = {
-	{0xf ,0xb ,0x2 ,0x2 ,0x2 ,0x1 ,0x1 ,0x1 ,0x1 ,0x1 ,0x1 ,0x1 },
-	{0x3 ,0x2 ,0x2 ,0x2 ,0x2 ,0x2 ,0x1 ,0x1 ,0x1 ,0x1 ,0x1 ,0x1 },
-	{0xff,0xcb,0xc0,0x58,0x36,0x33,0x23,0x22,0x21,0x11,0x10,0x0}};
+	int i, tmp;
+	unsigned long flags;
 
+	save_flags(flags);
 	cli ();
 	outb_p (0x5A,0x108); /* enable umc */
-	if (in_umc (0xd5) != 0xa0)
-	{
-		sti ();
-		printk ("UMC8672 not found\n");
-		return;  
-	}
-	speed[0] = UMC_DRIVE0;
-	speed[1] = UMC_DRIVE1;
-	speed[2] = UMC_DRIVE2;
-	speed[3] = UMC_DRIVE3;
-	for (i = 0;i < 4;i++)
-	{
-		if ((speed[i] < 0) || (speed[i] > 11))
-		{
-			sti ();
-			printk ("UMC 8672 drive speed out of range. Drive %d Speed %d\n",
-				i, speed[i]);
-			printk ("UMC support aborted\n");
-			return; 
-		}
-	}
-	out_umc (0xd7,(speedtab[0][speed[2]] | (speedtab[0][speed[3]]<<4)));
-	out_umc (0xd6,(speedtab[0][speed[0]] | (speedtab[0][speed[1]]<<4)));
+
+	out_umc (0xd7,(speedtab[0][speeds[2]] | (speedtab[0][speeds[3]]<<4)));
+	out_umc (0xd6,(speedtab[0][speeds[0]] | (speedtab[0][speeds[1]]<<4)));
 	tmp = 0;
 	for (i = 3; i >= 0; i--)
 	{
-		tmp = (tmp << 2) | speedtab[1][speed[i]];
+		tmp = (tmp << 2) | speedtab[1][speeds[i]];
 	}
 	out_umc (0xdc,tmp);
 	for (i = 0;i < 4; i++)
 	{
-		out_umc (0xd0+i,speedtab[2][speed[i]]);
-		out_umc (0xd8+i,speedtab[2][speed[i]]);
+		out_umc (0xd0+i,speedtab[2][speeds[i]]);
+		out_umc (0xd8+i,speedtab[2][speeds[i]]);
 	}
 	outb_p (0xa5,0x108); /* disable umc */
-	sti ();
-	printk ("Speeds for UMC8672 \n");
-	for (i = 0;i < 4;i++)
-		 printk ("Drive %d speed %d\n",i,speed[i]);
+	restore_flags(flags);
+
+	printk ("umc8672: drive speeds [0 to 11]: %d %d %d %d\n",
+		speeds[0], speeds[1], speeds[2], speeds[4]);
+}
+
+static void tune_umc (ide_drive_t *drive, byte pio)
+{
+	if (pio == 255)  {	/* auto-tune */
+		struct hd_driveid *id = drive->id;
+		pio = id->tPIO;
+		if (id->field_valid & 0x02) {
+			if (id->eide_pio_modes & 0x01)
+				pio = 3;
+			if (id->eide_pio_modes & 0x02)
+				pio = 4;
+		}
+	}
+	if (pio > 4)
+		pio = 4;
+	current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio];
+	umc_set_speeds (current_speeds);
+}
+
+void init_umc8672 (void)	/* called from ide.c */
+{
+	unsigned long flags;
+
+	if (check_region(0x108, 2)) {
+		printk("\numc8672: PORTS 0x108-0x109 ALREADY IN USE\n");
+		return;
+	}
+	save_flags(flags);
+	cli ();
+	outb_p (0x5A,0x108); /* enable umc */
+	if (in_umc (0xd5) != 0xa0)
+	{
+		sti ();
+		printk ("umc8672: not found\n");
+		return;  
+	}
+	outb_p (0xa5,0x108); /* disable umc */
+	restore_flags(flags);
+
+	umc_set_speeds (current_speeds);
+
+	request_region(0x108, 2, "umc8672");
+	ide_hwifs[0].chipset = ide_umc8672;
+	ide_hwifs[1].chipset = ide_umc8672;
+	ide_hwifs[0].tuneproc = &tune_umc;
+	ide_hwifs[1].tuneproc = &tune_umc;
+
 }
diff -u --recursive --new-file v1.3.60/linux/drivers/char/vesa_blank.c linux/drivers/char/vesa_blank.c
--- v1.3.60/linux/drivers/char/vesa_blank.c	Mon Nov 27 12:48:30 1995
+++ linux/drivers/char/vesa_blank.c	Thu Feb  8 04:43:10 1996
@@ -84,13 +84,17 @@
     the vertical synchronization pulse (mode 1) or horizontal
     synchronization pulse (mode 2).  Mode 1 should work with most
     monitors, but the VESA spec allows mode 2, so it's included for
-    completeness.
+    completeness. You may set this blanking interval in minutes by
+    echoing the escape sequence 'ESC[9;interval]' to the terminal.
+    By default this interval is set to 10 minutes.
 
     If you use one of these modes, you can also set a second interval
-    by echoing the escape sequence ESC[10;interval] to the terminal.
+    by echoing the escape sequence 'ESC[14;interval]' to the terminal.
     The monitor will be turned off completely (mode 3) after being in
-    suspend mode for the specified interval.  The interval defaults to
-    60 minutes. An interval of 0 disables this feature.
+    suspend mode for the specified interval. An interval of 0 disables
+    this feature which is the default.
+
+    Both intervals may be set within the range of 0..60 minutes.
 
 (2) Setting vesa_blanking_mode to 3.
     If your monitor locally has an Off_Mode timer then you should not
@@ -108,6 +112,8 @@
 before the monitor powers down. Do not set a too short period, if you love
 your hardware :-)) .
 
+By default vesa_blanking_mode is set to 0, thus not using any power saving
+features.
 */
 
 #define seq_port_reg	(0x3c4)		/* Sequencer register select port */
diff -u --recursive --new-file v1.3.60/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v1.3.60/linux/drivers/net/Config.in	Fri Jan 26 01:37:06 1996
+++ linux/drivers/net/Config.in	Fri Feb  9 16:59:04 1996
@@ -5,6 +5,7 @@
 tristate 'SLIP (serial line) support' CONFIG_SLIP
 if [ "$CONFIG_SLIP" != "n" ]; then
   bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED
+  bool ' Keepalive and linefill' CONFIG_SLIP_SMART
 fi
 tristate 'PPP (point-to-point) support' CONFIG_PPP
 if [ ! "$CONFIG_PPP" = "n" ]; then
diff -u --recursive --new-file v1.3.60/linux/drivers/net/loopback.c linux/drivers/net/loopback.c
--- v1.3.60/linux/drivers/net/loopback.c	Wed Feb  7 15:11:25 1996
+++ linux/drivers/net/loopback.c	Fri Feb  9 16:59:04 1996
@@ -47,6 +47,7 @@
 #include <linux/skbuff.h>
 #include <net/sock.h>
 
+#define LOOPBACK_MTU (PAGE_SIZE*7/8)
 
 static int loopback_xmit(struct sk_buff *skb, struct device *dev)
 {
@@ -99,6 +100,9 @@
 	skb->dev=dev;
 	save_flags(flags);
 	cli();
+#ifndef LOOPBACK_MUST_CHECKSUM
+	skb->ip_summed = CHECKSUM_UNNECESSARY;
+#endif
 	netif_rx(skb);
 	if(unlock)
 	  	skb_device_unlock(skb);
@@ -127,14 +131,8 @@
 int loopback_init(struct device *dev)
 {
 	int i;
-#if LINUS_EVER_SOLVES_THE_LARGE_ATOMIC_BUFFER_ISSUE
-	dev->mtu		= 7900;			/* MTU			*/
-#else
-/*
- *	If Alpha uses 8K pages then I guess 7K would be good for it.
- */
-	dev->mtu		= 2000;			/* Kept under 1 page    */
-#endif	
+
+	dev->mtu		= LOOPBACK_MTU;
 	dev->tbusy		= 0;
 	dev->hard_start_xmit	= loopback_xmit;
 	dev->open		= NULL;
diff -u --recursive --new-file v1.3.60/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v1.3.60/linux/drivers/net/slip.c	Wed Feb  7 15:11:26 1996
+++ linux/drivers/net/slip.c	Fri Feb  9 16:59:04 1996
@@ -46,7 +46,10 @@
  *					With MODULE-loading ``insmod'', user can
  *					issue parameter:   slip_maxdev=1024
  *					(Or how much he/she wants.. Default is 256)
- *
+ * *	Stanislav Voronyi	:	Slip line checking, with ideas taken
+ *					from multislip BSDI driver which was written
+ *					by Igor Chechik, RELCOM Corp. Only algorithms
+ * 					have been ported to Linux SLIP driver.
  */
 
 #define SL_CHECK_TRANSMIT
@@ -81,9 +84,9 @@
 #endif
 
 #ifdef MODULE
-#define SLIP_VERSION    "0.8.3-NET3.019-NEWTTY-MODULAR"
+#define SLIP_VERSION    "0.8.4-NET3.019-NEWTTY-MODULAR"
 #else
-#define	SLIP_VERSION	"0.8.3-NET3.019-NEWTTY"
+#define	SLIP_VERSION	"0.8.4-NET3.019-NEWTTY"
 #endif
 
 
@@ -103,7 +106,10 @@
 static int slip_esc6(unsigned char *p, unsigned char *d, int len);
 static void slip_unesc6(struct slip *sl, unsigned char c);
 #endif
-
+#ifdef CONFIG_SLIP_SMART
+static void sl_keepalive(unsigned long sls);
+static void sl_outfill(unsigned long sls);
+#endif
 
 /* Find a free SLIP channel, and link in this `tty' line. */
 static inline struct slip *
@@ -442,6 +448,8 @@
 #endif
 	sl->xleft = count - actual;
 	sl->xhead = sl->xbuff + actual;
+	/* VSV */
+	clear_bit(SLF_OUTWAIT, &sl->flags);	/* reset outfill flag */
 }
 
 /*
@@ -457,13 +465,13 @@
 	if (!sl || sl->magic != SLIP_MAGIC || !sl->dev->start) {
 		return;
 	}
-
 	if (sl->xleft <= 0)  {
 		/* Now serial buffer is almost free & we can start
 		 * transmission of another packet */
 		sl->tx_packets++;
 		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-		sl_unlock(sl);
+		if (test_bit(0, (void *) &sl->dev->tbusy)) /* add by VSV */
+			sl_unlock(sl);
 		mark_bh(NET_BH);
 		return;
 	}
@@ -620,7 +628,16 @@
 	sl->xbits    = 0;
 #endif
 	sl->flags   &= (1 << SLF_INUSE);      /* Clear ESCAPE & ERROR flags */
-
+#ifdef CONFIG_SLIP_SMART	
+	sl->keepalive=0;		/* no keepalive by default = VSV */
+	init_timer(&sl->keepalive_timer);	/* initialize timer_list struct */
+	sl->keepalive_timer.data=(unsigned long)sl;
+	sl->keepalive_timer.function=sl_keepalive;	
+	sl->outfill=0;			/* & outfill too */
+	init_timer(&sl->outfill_timer);
+	sl->outfill_timer.data=(unsigned long)sl;
+	sl->outfill_timer.function=sl_outfill;
+#endif
 	/* Needed because address '0' is special */
 	if (dev->pa_addr == 0)  {
 		dev->pa_addr=ntohl(0xC0A80001);
@@ -873,6 +890,10 @@
 
 	switch(s) {
 	 case END:
+		/* drop keeptest bit = VSV */
+		if (test_bit(SLF_KEEPTEST, &sl->flags))
+			clear_bit(SLF_KEEPTEST, &sl->flags);
+
 		if (!clear_bit(SLF_ERROR, &sl->flags) && (sl->rcount > 2))  {
 			sl_bump(sl);
 		}
@@ -954,6 +975,10 @@
 	unsigned char c;
 
 	if (s == 0x70) {
+		/* drop keeptest bit = VSV */
+		if (test_bit(SLF_KEEPTEST, &sl->flags))
+			clear_bit(SLF_KEEPTEST, &sl->flags);
+
 		if (!clear_bit(SLF_ERROR, &sl->flags) && (sl->rcount > 2))  {
 			sl_bump(sl);
 		}
@@ -1086,6 +1111,60 @@
 		return -EINVAL;
 #endif
 
+#ifdef CONFIG_SLIP_SMART
+	/* VSV changes start here */
+        case SIOCSKEEPALIVE:
+                if (sl->keepalive)
+                        del_timer (&sl->keepalive_timer);
+		err = verify_area(VERIFY_READ, arg, sizeof(int));
+		if (err)  {
+			return -err;
+		}
+		tmp = get_user((int *)arg);
+                if (tmp > 255) /* max for unchar */
+			return -EINVAL; 
+		if ((sl->keepalive = (unchar) tmp) != 0) {
+			sl->keepalive_timer.expires=jiffies+sl->keepalive*HZ;
+			add_timer(&sl->keepalive_timer);
+			set_bit(SLF_KEEPTEST, &sl->flags);
+                }
+		return 0;
+
+        case SIOCGKEEPALIVE:
+		err = verify_area(VERIFY_WRITE, arg, sizeof(int));
+		if (err)  {
+			return -err;
+		}
+		put_user(sl->keepalive, (int *)arg);
+		return 0;
+
+        case SIOCSOUTFILL:
+                if (sl->outfill)
+                         (void)del_timer (&sl->outfill_timer);
+		err = verify_area(VERIFY_READ, arg, sizeof(int));
+		if (err)  {
+			return -err;
+		}
+		tmp = get_user((int *)arg);
+                if (tmp > 255) /* max for unchar */
+			return -EINVAL; 
+                if ((sl->outfill = (unchar) tmp) != 0){
+			sl->outfill_timer.expires=jiffies+sl->outfill*HZ;
+			add_timer(&sl->outfill_timer);
+			set_bit(SLF_OUTWAIT, &sl->flags);
+		}
+                return 0;
+
+        case SIOCGOUTFILL:
+		err = verify_area(VERIFY_WRITE, arg, sizeof(int));
+		if (err)  {
+			return -err;
+		}
+		put_user(sl->outfill, (int *)arg);
+		return 0;
+	/* VSV changes end */
+#endif	
+
 	/* Allow stty to read, but not set, the serial port */
 	case TCGETS:
 	case TCGETA:
@@ -1108,8 +1187,7 @@
 #ifdef MODULE
 static int slip_init_ctrl_dev(void)
 #else	/* !MODULE */
-int
-slip_init_ctrl_dev(struct device *dummy)
+int slip_init_ctrl_dev(struct device *dummy)
 #endif	/* !MODULE */
 {
 	int status;
@@ -1120,19 +1198,23 @@
 #ifdef CONFIG_SLIP_MODE_SLIP6
 	       " (6 bit encapsulation enabled)"
 #endif
-	       "\n",
+	       ".\n",
 	       SLIP_VERSION, slip_maxdev );
 #if defined(SL_INCLUDE_CSLIP) && !defined(MODULE)
-	printk("CSLIP: code copyright 1989 Regents of the University of California\n");
+	printk("CSLIP: code copyright 1989 Regents of the University of California.\n");
 #endif
 #ifdef CONFIG_AX25
-	printk("AX25: KISS encapsulation enabled\n");
+	printk("AX25: KISS encapsulation enabled.\n");
 #endif
+#ifdef CONFIG_SLIP_SMART
+	printk("SLIP linefill/keepalive option.\n");
+#endif	
 
 	slip_ctrls = (slip_ctrl_t **) kmalloc(sizeof(void*)*slip_maxdev, GFP_KERNEL);
-	if (slip_ctrls == NULL) {
-	  printk("SLIP: Can't allocate slip_ctrls[] array!  Uaargh! (-> No SLIP available)\n");
-	  return -ENOMEM;
+	if (slip_ctrls == NULL) 
+	{
+		printk("SLIP: Can't allocate slip_ctrls[] array!  Uaargh! (-> No SLIP available)\n");
+		return -ENOMEM;
 	}
 	
 	/* Clear the pointer array, we allocate devices when we need them */
@@ -1153,7 +1235,7 @@
 	sl_ldisc.receive_room = slip_receive_room;
 	sl_ldisc.write_wakeup = slip_write_wakeup;
 	if ((status = tty_register_ldisc(N_SLIP, &sl_ldisc)) != 0)  {
-	  printk("SLIP: can't register line discipline (err = %d)\n", status);
+		printk("SLIP: can't register line discipline (err = %d)\n", status);
 	}
 
 
@@ -1242,18 +1324,95 @@
 {
 	int i;
 
-	if (slip_ctrls != NULL) {
-	  for (i = 0; i < slip_maxdev; i++)  {
-	    if (slip_ctrls[i] != NULL) {
-	      unregister_netdev(&(slip_ctrls[i]->dev));
-	      kfree(slip_ctrls[i]);
-	    }
-	  }
-	  kfree(slip_ctrls);
-	  slip_ctrls = NULL;
+	if (slip_ctrls != NULL) 
+	{
+		for (i = 0; i < slip_maxdev; i++)  
+		{
+			if (slip_ctrls[i] != NULL) 
+			{
+				unregister_netdev(&(slip_ctrls[i]->dev));
+				kfree(slip_ctrls[i]);
+			}
+		}
+		kfree(slip_ctrls);
+		slip_ctrls = NULL;
 	}
-	if ((i = tty_register_ldisc(N_SLIP, NULL)))  {
-	  printk("SLIP: can't unregister line discipline (err = %d)\n", i);
+	if ((i = tty_register_ldisc(N_SLIP, NULL)))  
+	{
+		printk("SLIP: can't unregister line discipline (err = %d)\n", i);
 	}
 }
 #endif /* MODULE */
+
+#ifdef CONFIG_SLIP_SMART
+/*
+ * This is start of the code for multislip style line checking
+ * added by Stanislav Voronyi. All changes before marked VSV
+ */
+ 
+static void sl_outfill(unsigned long sls)
+{
+	struct slip *sl=(struct slip *)sls;
+
+	if(sls==NULL)
+		return;
+
+	if(sl->outfill)
+	{
+		if( test_bit(SLF_OUTWAIT, &sl->flags) )
+		{
+			/* no packets was transmited, do outfill */
+#ifdef CONFIG_SLIP_MODE_SLIP6
+			unsigned char s = (sl->mode & SL_MODE_SLIP6)?0x70:END;
+#else
+			unsigned char s = END;
+#endif
+			/* put END into tty queue. Is it right ??? */
+			if (!test_bit(0, (void *) &sl->dev->tbusy))
+			{ 
+				/* if device busy no outfill */
+				sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+				sl->tty->driver.write(sl->tty, 0, &s, 1);
+			}
+		}
+		else
+			set_bit(SLF_OUTWAIT, &sl->flags);
+		(void)del_timer(&sl->outfill_timer);
+		sl->outfill_timer.expires=jiffies+sl->outfill*HZ;
+		add_timer(&sl->outfill_timer);
+	}
+	else
+		del_timer(&sl->outfill_timer);
+}
+
+static void sl_keepalive(unsigned long sls)
+{
+	struct slip *sl=(struct slip *)sls;
+
+	if(sls==NULL)
+		return;
+
+	if( sl->keepalive)
+	{
+		if(test_bit(SLF_KEEPTEST, &sl->flags))
+		{
+			/* keepalive still high :(, we must hangup */
+			(void)del_timer(&sl->keepalive_timer);
+			if( sl->outfill ) /* outfill timer must be deleted too */
+				(void)del_timer(&sl->outfill_timer);
+			printk("%s: no packets received during keepalive timeout, hangup.\n", sl->dev->name);
+			tty_hangup(sl->tty); /* this must hangup tty & close slip */
+			/* I think we need not something else */
+			return;
+		}
+		else
+			set_bit(SLF_KEEPTEST, &sl->flags);
+		(void)del_timer(&sl->keepalive_timer);
+		 sl->keepalive_timer.expires=jiffies+sl->keepalive*HZ;
+		add_timer(&sl->keepalive_timer);
+	}
+	else
+		(void)del_timer(&sl->keepalive_timer);
+}
+
+#endif
diff -u --recursive --new-file v1.3.60/linux/drivers/net/slip.h linux/drivers/net/slip.h
--- v1.3.60/linux/drivers/net/slip.h	Fri Aug 18 08:44:57 1995
+++ linux/drivers/net/slip.h	Fri Feb  9 16:59:04 1996
@@ -12,6 +12,8 @@
  *		Alan Cox	:	Added SL_SLIP_LOTS
  *	Dmitry Gorodchanin	:	A lot of changes in the 'struct slip'
  *	Dmitry Gorodchanin	:	Added CSLIP statistics.
+ *	Stanislav Voronyi	:	Make line checking as created by
+ *					Igor Chechik, RELCOM Corp.
  *
  * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
  */
@@ -87,6 +89,8 @@
 #define SLF_INUSE	0		/* Channel in use               */
 #define SLF_ESCAPE	1               /* ESC received                 */
 #define SLF_ERROR	2               /* Parity, etc. error           */
+#define SLF_KEEPTEST	4		/* Keepalive test flag		*/
+#define SLF_OUTWAIT	8		/* is outpacket was flag	*/
 
   unsigned char		mode;		/* SLIP mode			*/
 #define SL_MODE_SLIP	0
@@ -95,6 +99,12 @@
 #define SL_MODE_CSLIP6	(SL_MODE_SLIP6|SL_MODE_CSLIP)
 #define SL_MODE_AX25	4
 #define SL_MODE_ADAPTIVE 8
+#ifdef CONFIG_SLIP_SMART  
+  unsigned char		outfill;	/* # of sec betwen outfill packet */
+  unsigned char		keepalive;	/* keepalive seconds		*/
+  struct timer_list	outfill_timer;
+  struct timer_list	keepalive_timer;
+#endif  
 };
 
 
diff -u --recursive --new-file v1.3.60/linux/drivers/scsi/NCR5380.c linux/drivers/scsi/NCR5380.c
--- v1.3.60/linux/drivers/scsi/NCR5380.c	Wed Feb  7 15:11:27 1996
+++ linux/drivers/scsi/NCR5380.c	Fri Feb  9 07:50:17 1996
@@ -68,6 +68,8 @@
  *      the high level code.
  */
 
+#include "g_NCR5380.h"
+
 #if (NDEBUG & NDEBUG_LISTS)
 #define LIST(x,y) {printk("LINE:%d   Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); }
 #define REMOVE(w,x,y,z) {printk("LINE:%d   Removing: %p->%p  %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); }
diff -u --recursive --new-file v1.3.60/linux/drivers/scsi/t128.c linux/drivers/scsi/t128.c
--- v1.3.60/linux/drivers/scsi/t128.c	Sun Dec 17 11:43:20 1995
+++ linux/drivers/scsi/t128.c	Fri Feb  9 07:50:21 1996
@@ -109,6 +109,7 @@
 #include <asm/system.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
+#include <asm/io.h>
 #include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
diff -u --recursive --new-file v1.3.60/linux/drivers/sound/configure.c linux/drivers/sound/configure.c
--- v1.3.60/linux/drivers/sound/configure.c	Tue Jan 23 21:15:45 1996
+++ linux/drivers/sound/configure.c	Thu Feb  8 09:12:31 1996
@@ -678,7 +678,7 @@
 
 
   ask_int_choice (B (OPT_GUS), "GUS_BASE",
-		  "I/O base for Gravis UltraSound (GUS)",
+		  "I/O base for GUS",
 		  FMT_HEX,
 		  0x220,
 		  "210, 220, 230, 240, 250 or 260");
diff -u --recursive --new-file v1.3.60/linux/fs/Makefile linux/fs/Makefile
--- v1.3.60/linux/fs/Makefile	Wed Feb  7 15:11:29 1996
+++ linux/fs/Makefile	Thu Feb  8 05:41:50 1996
@@ -52,7 +52,7 @@
 ifeq ($(CONFIG_FAT_FS),y)
 SUB_DIRS += fat
 else
-  ifeq ($(CONFIG_MSDOS_FS),m)
+  ifeq ($(CONFIG_FAT_FS),m)
   MOD_SUB_DIRS += fat
   endif
 endif
@@ -146,6 +146,14 @@
 else
   ifeq ($(CONFIG_BINFMT_ELF),m)
   M_OBJS += binfmt_elf.o
+  endif
+endif
+
+ifeq ($(CONFIG_BINFMT_AOUT),y)
+BINFMTS += binfmt_aout.o
+else
+  ifeq ($(CONFIG_BINFMT_AOUT),m)
+  M_OBJS += binfmt_aout.o
   endif
 endif
 
diff -u --recursive --new-file v1.3.60/linux/fs/binfmt_aout.c linux/fs/binfmt_aout.c
--- v1.3.60/linux/fs/binfmt_aout.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/binfmt_aout.c	Thu Feb  8 06:22:11 1996
@@ -0,0 +1,450 @@
+/*
+ *  linux/fs/binfmt_aout.c
+ *
+ *  Copyright (C) 1991, 1992, 1996  Linus Torvalds
+ */
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/a.out.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/malloc.h>
+#include <linux/binfmts.h>
+#include <linux/personality.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/pgtable.h>
+
+static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs);
+static int load_aout_library(int fd);
+static int aout_core_dump(long signr, struct pt_regs * regs);
+
+extern void dump_thread(struct pt_regs *, struct user *);
+
+static struct linux_binfmt aout_format = {
+#ifndef MODULE
+	NULL, NULL, load_aout_binary, load_aout_library, aout_core_dump
+#else
+	NULL, &mod_use_count_, load_aout_binary, load_aout_library, aout_core_dump
+#endif
+};
+
+static void set_brk(unsigned long start, unsigned long end)
+{
+	start = PAGE_ALIGN(start);
+	end = PAGE_ALIGN(end);
+	if (end <= start)
+		return;
+	do_mmap(NULL, start, end - start,
+		PROT_READ | PROT_WRITE | PROT_EXEC,
+		MAP_FIXED | MAP_PRIVATE, 0);
+}
+
+/*
+ * These are the only things you should do on a core-file: use only these
+ * macros to write out all the necessary info.
+ */
+#define DUMP_WRITE(addr,nr) \
+while (file.f_op->write(inode,&file,(char *)(addr),(nr)) != (nr)) goto close_coredump
+
+#define DUMP_SEEK(offset) \
+if (file.f_op->lseek) { \
+	if (file.f_op->lseek(inode,&file,(offset),0) != (offset)) \
+ 		goto close_coredump; \
+} else file.f_pos = (offset)		
+
+/*
+ * Routine writes a core dump image in the current directory.
+ * Currently only a stub-function.
+ *
+ * Note that setuid/setgid files won't make a core-dump if the uid/gid
+ * changed due to the set[u|g]id. It's enforced by the "current->dumpable"
+ * field, which also makes sure the core-dumps won't be recursive if the
+ * dumping of the process results in another error..
+ */
+
+static inline int
+do_aout_core_dump(long signr, struct pt_regs * regs)
+{
+	struct inode * inode = NULL;
+	struct file file;
+	unsigned short fs;
+	int has_dumped = 0;
+	char corefile[6+sizeof(current->comm)];
+	unsigned long dump_start, dump_size;
+	struct user dump;
+#ifdef __alpha__
+#       define START_DATA(u)	(u.start_data)
+#else
+#       define START_DATA(u)	(u.u_tsize << PAGE_SHIFT)
+#endif
+
+	if (!current->dumpable)
+		return 0;
+	current->dumpable = 0;
+
+/* See if we have enough room to write the upage.  */
+	if (current->rlim[RLIMIT_CORE].rlim_cur < PAGE_SIZE)
+		return 0;
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	memcpy(corefile,"core.",5);
+#if 0
+	memcpy(corefile+5,current->comm,sizeof(current->comm));
+#else
+	corefile[4] = '\0';
+#endif
+	if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) {
+		inode = NULL;
+		goto end_coredump;
+	}
+	if (!S_ISREG(inode->i_mode))
+		goto end_coredump;
+	if (!inode->i_op || !inode->i_op->default_file_ops)
+		goto end_coredump;
+	if (get_write_access(inode))
+		goto end_coredump;
+	file.f_mode = 3;
+	file.f_flags = 0;
+	file.f_count = 1;
+	file.f_inode = inode;
+	file.f_pos = 0;
+	file.f_reada = 0;
+	file.f_op = inode->i_op->default_file_ops;
+	if (file.f_op->open)
+		if (file.f_op->open(inode,&file))
+			goto done_coredump;
+	if (!file.f_op->write)
+		goto close_coredump;
+	has_dumped = 1;
+       	strncpy(dump.u_comm, current->comm, sizeof(current->comm));
+	dump.u_ar0 = (void *)(((unsigned long)(&dump.regs)) - ((unsigned long)(&dump)));
+	dump.signal = signr;
+	dump_thread(regs, &dump);
+
+/* If the size of the dump file exceeds the rlimit, then see what would happen
+   if we wrote the stack, but not the data area.  */
+	if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE >
+	    current->rlim[RLIMIT_CORE].rlim_cur)
+		dump.u_dsize = 0;
+
+/* Make sure we have enough room to write the stack and data areas. */
+	if ((dump.u_ssize+1) * PAGE_SIZE >
+	    current->rlim[RLIMIT_CORE].rlim_cur)
+		dump.u_ssize = 0;
+
+/* make sure we actually have a data and stack area to dump */
+	set_fs(USER_DS);
+	if (verify_area(VERIFY_READ, (void *) START_DATA(dump), dump.u_dsize << PAGE_SHIFT))
+		dump.u_dsize = 0;
+	if (verify_area(VERIFY_READ, (void *) dump.start_stack, dump.u_ssize << PAGE_SHIFT))
+		dump.u_ssize = 0;
+
+	set_fs(KERNEL_DS);
+/* struct user */
+	DUMP_WRITE(&dump,sizeof(dump));
+/* Now dump all of the user data.  Include malloced stuff as well */
+	DUMP_SEEK(PAGE_SIZE);
+/* now we start writing out the user space info */
+	set_fs(USER_DS);
+/* Dump the data area */
+	if (dump.u_dsize != 0) {
+		dump_start = START_DATA(dump);
+		dump_size = dump.u_dsize << PAGE_SHIFT;
+		DUMP_WRITE(dump_start,dump_size);
+	}
+/* Now prepare to dump the stack area */
+	if (dump.u_ssize != 0) {
+		dump_start = dump.start_stack;
+		dump_size = dump.u_ssize << PAGE_SHIFT;
+		DUMP_WRITE(dump_start,dump_size);
+	}
+/* Finally dump the task struct.  Not be used by gdb, but could be useful */
+	set_fs(KERNEL_DS);
+	DUMP_WRITE(current,sizeof(*current));
+close_coredump:
+	if (file.f_op->release)
+		file.f_op->release(inode,&file);
+done_coredump:
+	put_write_access(inode);
+end_coredump:
+	set_fs(fs);
+	iput(inode);
+	return has_dumped;
+}
+
+static int
+aout_core_dump(long signr, struct pt_regs * regs)
+{
+	int retval;
+
+	MOD_INC_USE_COUNT;
+	retval = do_aout_core_dump(signr, regs);
+	MOD_DEC_USE_COUNT;
+	return retval;
+}
+
+/*
+ * These are the functions used to load a.out style executables and shared
+ * libraries.  There is no binary dependent code anywhere else.
+ */
+
+static inline int
+do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+	struct exec ex;
+	struct file * file;
+	int fd;
+	unsigned long error;
+	unsigned long p = bprm->p;
+	unsigned long fd_offset;
+	unsigned long rlim;
+
+	ex = *((struct exec *) bprm->buf);		/* exec-header */
+	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && 
+	     N_MAGIC(ex) != QMAGIC) ||
+	    N_TRSIZE(ex) || N_DRSIZE(ex) ||
+	    bprm->inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
+		return -ENOEXEC;
+	}
+
+	current->personality = PER_LINUX;
+	fd_offset = N_TXTOFF(ex);
+
+#ifdef __i386__
+	if (N_MAGIC(ex) == ZMAGIC && fd_offset != BLOCK_SIZE) {
+		printk(KERN_NOTICE "N_TXTOFF != BLOCK_SIZE. See a.out.h.\n");
+		return -ENOEXEC;
+	}
+
+	if (N_MAGIC(ex) == ZMAGIC && ex.a_text &&
+	    (fd_offset < bprm->inode->i_sb->s_blocksize)) {
+		printk(KERN_NOTICE "N_TXTOFF < BLOCK_SIZE. Please convert binary.\n");
+		return -ENOEXEC;
+	}
+#endif
+
+	/* Check initial limits. This avoids letting people circumvent
+	 * size limits imposed on them by creating programs with large
+	 * arrays in the data or bss.
+	 */
+	rlim = current->rlim[RLIMIT_DATA].rlim_cur;
+	if (rlim >= RLIM_INFINITY)
+		rlim = ~0;
+	if (ex.a_data + ex.a_bss > rlim)
+		return -ENOMEM;
+
+	/* OK, This is the point of no return */
+	flush_old_exec(bprm);
+
+	current->mm->end_code = ex.a_text +
+		(current->mm->start_code = N_TXTADDR(ex));
+	current->mm->end_data = ex.a_data +
+		(current->mm->start_data = N_DATADDR(ex));
+	current->mm->brk = ex.a_bss +
+		(current->mm->start_brk = N_BSSADDR(ex));
+
+	current->mm->rss = 0;
+	current->mm->mmap = NULL;
+	current->suid = current->euid = current->fsuid = bprm->e_uid;
+	current->sgid = current->egid = current->fsgid = bprm->e_gid;
+	if (N_MAGIC(ex) == OMAGIC) {
+#ifdef __alpha__
+		do_mmap(NULL, N_TXTADDR(ex) & PAGE_MASK,
+			ex.a_text+ex.a_data + PAGE_SIZE - 1,
+			PROT_READ|PROT_WRITE|PROT_EXEC,
+			MAP_FIXED|MAP_PRIVATE, 0);
+		read_exec(bprm->inode, fd_offset, (char *) N_TXTADDR(ex),
+			  ex.a_text+ex.a_data, 0);
+#else
+		do_mmap(NULL, 0, ex.a_text+ex.a_data,
+			PROT_READ|PROT_WRITE|PROT_EXEC,
+			MAP_FIXED|MAP_PRIVATE, 0);
+		read_exec(bprm->inode, 32, (char *) 0, ex.a_text+ex.a_data, 0);
+#endif
+	} else {
+		if (ex.a_text & 0xfff || ex.a_data & 0xfff)
+			printk(KERN_NOTICE "executable not page aligned\n");
+		
+		fd = open_inode(bprm->inode, O_RDONLY);
+		
+		if (fd < 0)
+			return fd;
+		file = current->files->fd[fd];
+		if (!file->f_op || !file->f_op->mmap) {
+			sys_close(fd);
+			do_mmap(NULL, 0, ex.a_text+ex.a_data,
+				PROT_READ|PROT_WRITE|PROT_EXEC,
+				MAP_FIXED|MAP_PRIVATE, 0);
+			read_exec(bprm->inode, fd_offset,
+				  (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0);
+			goto beyond_if;
+		}
+
+		error = do_mmap(file, N_TXTADDR(ex), ex.a_text,
+			PROT_READ | PROT_EXEC,
+			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
+			fd_offset);
+
+		if (error != N_TXTADDR(ex)) {
+			sys_close(fd);
+			send_sig(SIGKILL, current, 0);
+			return error;
+		}
+		
+ 		error = do_mmap(file, N_DATADDR(ex), ex.a_data,
+				PROT_READ | PROT_WRITE | PROT_EXEC,
+				MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
+				fd_offset + ex.a_text);
+		sys_close(fd);
+		if (error != N_DATADDR(ex)) {
+			send_sig(SIGKILL, current, 0);
+			return error;
+		}
+	}
+beyond_if:
+	if (current->exec_domain && current->exec_domain->use_count)
+		(*current->exec_domain->use_count)--;
+	if (current->binfmt && current->binfmt->use_count)
+		(*current->binfmt->use_count)--;
+	current->exec_domain = lookup_exec_domain(current->personality);
+	current->binfmt = &aout_format;
+	if (current->exec_domain && current->exec_domain->use_count)
+		(*current->exec_domain->use_count)++;
+	if (current->binfmt && current->binfmt->use_count)
+		(*current->binfmt->use_count)++;
+
+	set_brk(current->mm->start_brk, current->mm->brk);
+
+	fd_offset = setup_arg_pages(ex.a_text,bprm->page) - MAX_ARG_PAGES*PAGE_SIZE;
+	p += fd_offset;
+	if (bprm->loader)
+		bprm->loader += fd_offset;
+	bprm->exec += fd_offset;
+	
+	p = (unsigned long)create_tables((char *)p, bprm,
+					current->personality != PER_LINUX);
+	current->mm->start_stack = p;
+#ifdef __alpha__
+	regs->gp = ex.a_gpvalue;
+#endif
+	start_thread(regs, ex.a_entry, p);
+	if (current->flags & PF_PTRACED)
+		send_sig(SIGTRAP, current, 0);
+	return 0;
+}
+
+static int
+load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+	int retval;
+
+	MOD_INC_USE_COUNT;
+	retval = do_load_aout_binary(bprm, regs);
+	MOD_DEC_USE_COUNT;
+	return retval;
+}
+
+static inline int
+do_load_aout_library(int fd)
+{
+        struct file * file;
+	struct exec ex;
+	struct  inode * inode;
+	unsigned int len;
+	unsigned int bss;
+	unsigned int start_addr;
+	unsigned long error;
+	
+	file = current->files->fd[fd];
+	inode = file->f_inode;
+	
+	if (!file || !file->f_op)
+		return -EACCES;
+
+	/* Seek into the file */
+	if (file->f_op->lseek) {
+		if ((error = file->f_op->lseek(inode, file, 0, 0)) != 0)
+			return -ENOEXEC;
+	} else
+		file->f_pos = 0;
+
+	set_fs(KERNEL_DS);
+	error = file->f_op->read(inode, file, (char *) &ex, sizeof(ex));
+	set_fs(USER_DS);
+	if (error != sizeof(ex))
+		return -ENOEXEC;
+
+	/* We come in here for the regular a.out style of shared libraries */
+	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
+	    N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
+	    inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
+		return -ENOEXEC;
+	}
+	if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) && 
+	    (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
+		printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
+		return -ENOEXEC;
+	}
+	
+	if (N_FLAGS(ex)) return -ENOEXEC;
+
+	/* For  QMAGIC, the starting address is 0x20 into the page.  We mask
+	   this off to get the starting address for the page */
+
+	start_addr =  ex.a_entry & 0xfffff000;
+
+	/* Now use mmap to map the library into memory. */
+	error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
+			PROT_READ | PROT_WRITE | PROT_EXEC,
+			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
+			N_TXTOFF(ex));
+	if (error != start_addr)
+		return error;
+	len = PAGE_ALIGN(ex.a_text + ex.a_data);
+	bss = ex.a_text + ex.a_data + ex.a_bss;
+	if (bss > len)
+		do_mmap(NULL, start_addr + len, bss-len,
+			PROT_READ|PROT_WRITE|PROT_EXEC,
+			MAP_PRIVATE|MAP_FIXED, 0);
+	return 0;
+}
+
+static int
+load_aout_library(int fd)
+{
+	int retval;
+
+	MOD_INC_USE_COUNT;
+	retval = do_load_aout_library(fd);
+	MOD_DEC_USE_COUNT;
+	return retval;
+}
+
+
+int init_aout_binfmt(void) {
+	return register_binfmt(&aout_format);
+}
+
+#ifdef MODULE
+int init_module(void) {
+	return init_aout_binfmt();
+}
+
+void cleanup_module( void) {
+	unregister_binfmt(&aout_format);
+}
+#endif
+
diff -u --recursive --new-file v1.3.60/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- v1.3.60/linux/fs/binfmt_elf.c	Mon Nov 27 12:48:31 1995
+++ linux/fs/binfmt_elf.c	Thu Feb  8 06:02:01 1996
@@ -33,11 +33,6 @@
 
 #include <linux/config.h>
 
-#include <linux/unistd.h>
-typedef int (*sysfun_p)(int, ...);
-extern sysfun_p sys_call_table[];
-#define SYS(name)	(sys_call_table[__NR_##name])
-
 #define DLINFO_ITEMS 12
 
 #include <linux/elf.h>
@@ -47,13 +42,7 @@
 static int elf_core_dump(long signr, struct pt_regs * regs);
 extern int dump_fpu (elf_fpregset_t *);
 
-/*
- * Please do not change the default core dump format to ELF when most people
- * do not have a gdb capable of interpreting ELF core files.  Once a gdb has
- * been released that understands ELF, *THEN* switch the core dump format.
- */
-
-struct linux_binfmt elf_format = {
+static struct linux_binfmt elf_format = {
 #ifndef MODULE
 	NULL, NULL, load_elf_binary, load_elf_library, elf_core_dump
 #else
@@ -61,6 +50,18 @@
 #endif
 };
 
+static void set_brk(unsigned long start, unsigned long end)
+{
+	start = PAGE_ALIGN(start);
+	end = PAGE_ALIGN(end);
+	if (end <= start) 
+		return;
+	do_mmap(NULL, start, end - start,
+		PROT_READ | PROT_WRITE | PROT_EXEC,
+		MAP_FIXED | MAP_PRIVATE, 0);
+}
+
+
 /* We need to explicitly zero any fractional pages
    after the data section (i.e. bss).  This would
    contain the junk from the file that should not
@@ -268,7 +269,7 @@
 	
 	/* Now use mmap to map the library into memory. */
 
-	SYS(close)(elf_exec_fileno);
+	sys_close(elf_exec_fileno);
 	if(error < 0 && error > -1024) {
 		kfree(elf_phdata);
 		return 0xffffffff;
@@ -615,7 +616,7 @@
 	
 	kfree(elf_phdata);
 	
-	if(interpreter_type != INTERPRETER_AOUT) SYS(close)(elf_exec_fileno);
+	if(interpreter_type != INTERPRETER_AOUT) sys_close(elf_exec_fileno);
 	current->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX);
 
 	if (current->exec_domain && current->exec_domain->use_count)
@@ -655,10 +656,9 @@
 	current->mm->end_data = end_data;
 	current->mm->start_stack = bprm->p;
 
-	/* Calling sys_brk effectively mmaps the pages that we need for the bss and break
+	/* Calling set_brk effectively mmaps the pages that we need for the bss and break
 	   sections */
-	current->mm->brk = (elf_bss + 0xfff) & 0xfffff000;
-	SYS(brk)((elf_brk + 0xfff) & 0xfffff000);
+	set_brk(elf_bss, elf_brk);
 
 	padzero(elf_bss);
 
@@ -1221,6 +1221,10 @@
 	return has_dumped;
 }
 
+int init_elf_binfmt(void) {
+	return register_binfmt(&elf_format);
+}
+
 #ifdef MODULE
 
 int init_module(void) {
@@ -1228,7 +1232,7 @@
 	 * N.B. We *rely* on the table being the right size with the
 	 * right number of free slots...
 	 */
-	return register_binfmt(&elf_format);
+	return init_elf_binfmt();
 }
 
 
diff -u --recursive --new-file v1.3.60/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.60/linux/fs/buffer.c	Wed Feb  7 15:11:29 1996
+++ linux/fs/buffer.c	Fri Feb  9 07:20:49 1996
@@ -35,9 +35,10 @@
 #include <asm/segment.h>
 #include <asm/io.h>
 
-#define NR_SIZES 4
-static char buffersize_index[9] = {-1,  0,  1, -1,  2, -1, -1, -1, 3};
-static short int bufferindex_size[NR_SIZES] = {512, 1024, 2048, 4096};
+#define NR_SIZES 5
+static char buffersize_index[17] =
+{-1,  0,  1, -1,  2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4};
+static short int bufferindex_size[NR_SIZES] = {512, 1024, 2048, 4096, 8192};
 
 #define BUFSIZE_INDEX(X) ((int) buffersize_index[(X)>>9])
 #define MAX_BUF_PER_PAGE (PAGE_SIZE / 512)
@@ -99,7 +100,7 @@
 				   trim back the buffers */
 	} b_un;
 	unsigned int data[N_PARAM];
-} bdf_prm = {{25, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}};
+} bdf_prm = {{60, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}};
 
 /* The lav constant is set for 1 minute, as long as the update process runs
    every 5 seconds.  If you change the frequency of update, the time
@@ -502,9 +503,12 @@
 	if (!blksize_size[MAJOR(dev)])
 		return;
 
+	if (size > PAGE_SIZE)
+		size = 0;
+
 	switch(size) {
 		default: panic("Invalid blocksize passed to set_blocksize");
-		case 512: case 1024: case 2048: case 4096:;
+	        case 512: case 1024: case 2048: case 4096: case 8192: ;
 	}
 
 	if (blksize_size[MAJOR(dev)][MINOR(dev)] == 0 && size == BLOCK_SIZE) {
diff -u --recursive --new-file v1.3.60/linux/fs/exec.c linux/fs/exec.c
--- v1.3.60/linux/fs/exec.c	Wed Feb  7 15:11:30 1996
+++ linux/fs/exec.c	Thu Feb  8 05:41:50 1996
@@ -51,28 +51,27 @@
 asmlinkage int sys_exit(int exit_code);
 asmlinkage int sys_brk(unsigned long);
 
-static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs);
-static int load_aout_library(int fd);
-
-extern void dump_thread(struct pt_regs *, struct user *);
-
 /*
  * Here are the actual binaries that will be accepted:
  * add more with "register_binfmt()" if using modules...
+ *
+ * These are defined again for the 'real' modules if you are using a
+ * module definition for these routines.
  */
-extern struct linux_binfmt elf_format;
 
-static struct linux_binfmt aout_format = {
-#ifndef CONFIG_BINFMT_ELF
- 	NULL, NULL, load_aout_binary, load_aout_library, aout_core_dump
-#else
- 	&elf_format, NULL, load_aout_binary, load_aout_library, aout_core_dump
+static struct linux_binfmt *formats = (struct linux_binfmt *) NULL;
+
+void binfmt_setup(void)
+{
+#ifdef CONFIG_BINFMT_ELF
+	init_elf_binfmt();
 #endif
-};
 
-static struct linux_binfmt *formats = &aout_format;
+#ifdef CONFIG_BINFMT_AOUT
+	init_aout_binfmt();
+#endif
+}
 
-#ifdef CONFIG_MODULES
 int register_binfmt(struct linux_binfmt * fmt)
 {
 	struct linux_binfmt ** tmp = &formats;
@@ -91,6 +90,7 @@
 	return 0;	
 }
 
+#ifdef CONFIG_MODULES
 int unregister_binfmt(struct linux_binfmt * fmt)
 {
 	struct linux_binfmt ** tmp = &formats;
@@ -147,137 +147,6 @@
 }
 
 /*
- * These are the only things you should do on a core-file: use only these
- * macros to write out all the necessary info.
- */
-#define DUMP_WRITE(addr,nr) \
-while (file.f_op->write(inode,&file,(char *)(addr),(nr)) != (nr)) goto close_coredump
-
-#define DUMP_SEEK(offset) \
-if (file.f_op->lseek) { \
-	if (file.f_op->lseek(inode,&file,(offset),0) != (offset)) \
- 		goto close_coredump; \
-} else file.f_pos = (offset)		
-
-/*
- * Routine writes a core dump image in the current directory.
- * Currently only a stub-function.
- *
- * Note that setuid/setgid files won't make a core-dump if the uid/gid
- * changed due to the set[u|g]id. It's enforced by the "current->dumpable"
- * field, which also makes sure the core-dumps won't be recursive if the
- * dumping of the process results in another error..
- */
-int aout_core_dump(long signr, struct pt_regs * regs)
-{
-	struct inode * inode = NULL;
-	struct file file;
-	unsigned short fs;
-	int has_dumped = 0;
-	char corefile[6+sizeof(current->comm)];
-	unsigned long dump_start, dump_size;
-	struct user dump;
-#ifdef __alpha__
-#       define START_DATA(u)	(u.start_data)
-#else
-#       define START_DATA(u)	(u.u_tsize << PAGE_SHIFT)
-#endif
-
-	if (!current->dumpable)
-		return 0;
-	current->dumpable = 0;
-
-/* See if we have enough room to write the upage.  */
-	if (current->rlim[RLIMIT_CORE].rlim_cur < PAGE_SIZE)
-		return 0;
-	fs = get_fs();
-	set_fs(KERNEL_DS);
-	memcpy(corefile,"core.",5);
-#if 0
-	memcpy(corefile+5,current->comm,sizeof(current->comm));
-#else
-	corefile[4] = '\0';
-#endif
-	if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) {
-		inode = NULL;
-		goto end_coredump;
-	}
-	if (!S_ISREG(inode->i_mode))
-		goto end_coredump;
-	if (!inode->i_op || !inode->i_op->default_file_ops)
-		goto end_coredump;
-	if (get_write_access(inode))
-		goto end_coredump;
-	file.f_mode = 3;
-	file.f_flags = 0;
-	file.f_count = 1;
-	file.f_inode = inode;
-	file.f_pos = 0;
-	file.f_reada = 0;
-	file.f_op = inode->i_op->default_file_ops;
-	if (file.f_op->open)
-		if (file.f_op->open(inode,&file))
-			goto done_coredump;
-	if (!file.f_op->write)
-		goto close_coredump;
-	has_dumped = 1;
-       	strncpy(dump.u_comm, current->comm, sizeof(current->comm));
-	dump.u_ar0 = (void *)(((unsigned long)(&dump.regs)) - ((unsigned long)(&dump)));
-	dump.signal = signr;
-	dump_thread(regs, &dump);
-
-/* If the size of the dump file exceeds the rlimit, then see what would happen
-   if we wrote the stack, but not the data area.  */
-	if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE >
-	    current->rlim[RLIMIT_CORE].rlim_cur)
-		dump.u_dsize = 0;
-
-/* Make sure we have enough room to write the stack and data areas. */
-	if ((dump.u_ssize+1) * PAGE_SIZE >
-	    current->rlim[RLIMIT_CORE].rlim_cur)
-		dump.u_ssize = 0;
-
-/* make sure we actually have a data and stack area to dump */
-	set_fs(USER_DS);
-	if (verify_area(VERIFY_READ, (void *) START_DATA(dump), dump.u_dsize << PAGE_SHIFT))
-		dump.u_dsize = 0;
-	if (verify_area(VERIFY_READ, (void *) dump.start_stack, dump.u_ssize << PAGE_SHIFT))
-		dump.u_ssize = 0;
-
-	set_fs(KERNEL_DS);
-/* struct user */
-	DUMP_WRITE(&dump,sizeof(dump));
-/* Now dump all of the user data.  Include malloced stuff as well */
-	DUMP_SEEK(PAGE_SIZE);
-/* now we start writing out the user space info */
-	set_fs(USER_DS);
-/* Dump the data area */
-	if (dump.u_dsize != 0) {
-		dump_start = START_DATA(dump);
-		dump_size = dump.u_dsize << PAGE_SHIFT;
-		DUMP_WRITE(dump_start,dump_size);
-	}
-/* Now prepare to dump the stack area */
-	if (dump.u_ssize != 0) {
-		dump_start = dump.start_stack;
-		dump_size = dump.u_ssize << PAGE_SHIFT;
-		DUMP_WRITE(dump_start,dump_size);
-	}
-/* Finally dump the task struct.  Not be used by gdb, but could be useful */
-	set_fs(KERNEL_DS);
-	DUMP_WRITE(current,sizeof(*current));
-close_coredump:
-	if (file.f_op->release)
-		file.f_op->release(inode,&file);
-done_coredump:
-	put_write_access(inode);
-end_coredump:
-	set_fs(fs);
-	iput(inode);
-	return has_dumped;
-}
-
-/*
  * Note that a shared library must be both readable and executable due to
  * security reasons.
  *
@@ -821,229 +690,4 @@
 	for (i=0 ; i<MAX_ARG_PAGES ; i++)
 		free_page(bprm.page[i]);
 	return(retval);
-}
-
-static void set_brk(unsigned long start, unsigned long end)
-{
-	start = PAGE_ALIGN(start);
-	end = PAGE_ALIGN(end);
-	if (end <= start)
-		return;
-	do_mmap(NULL, start, end - start,
-		PROT_READ | PROT_WRITE | PROT_EXEC,
-		MAP_FIXED | MAP_PRIVATE, 0);
-}
-
-/*
- * These are the functions used to load a.out style executables and shared
- * libraries.  There is no binary dependent code anywhere else.
- */
-
-static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
-{
-	struct exec ex;
-	struct file * file;
-	int fd;
-	unsigned long error;
-	unsigned long p = bprm->p;
-	unsigned long fd_offset;
-	unsigned long rlim;
-
-	ex = *((struct exec *) bprm->buf);		/* exec-header */
-	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && 
-	     N_MAGIC(ex) != QMAGIC) ||
-	    N_TRSIZE(ex) || N_DRSIZE(ex) ||
-	    bprm->inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
-		return -ENOEXEC;
-	}
-
-	current->personality = PER_LINUX;
-	fd_offset = N_TXTOFF(ex);
-
-#ifdef __i386__
-	if (N_MAGIC(ex) == ZMAGIC && fd_offset != BLOCK_SIZE) {
-		printk(KERN_NOTICE "N_TXTOFF != BLOCK_SIZE. See a.out.h.\n");
-		return -ENOEXEC;
-	}
-
-	if (N_MAGIC(ex) == ZMAGIC && ex.a_text &&
-	    (fd_offset < bprm->inode->i_sb->s_blocksize)) {
-		printk(KERN_NOTICE "N_TXTOFF < BLOCK_SIZE. Please convert binary.\n");
-		return -ENOEXEC;
-	}
-#endif
-
-	/* Check initial limits. This avoids letting people circumvent
-	 * size limits imposed on them by creating programs with large
-	 * arrays in the data or bss.
-	 */
-	rlim = current->rlim[RLIMIT_DATA].rlim_cur;
-	if (rlim >= RLIM_INFINITY)
-		rlim = ~0;
-	if (ex.a_data + ex.a_bss > rlim)
-		return -ENOMEM;
-
-	/* OK, This is the point of no return */
-	flush_old_exec(bprm);
-
-	current->mm->end_code = ex.a_text +
-		(current->mm->start_code = N_TXTADDR(ex));
-	current->mm->end_data = ex.a_data +
-		(current->mm->start_data = N_DATADDR(ex));
-	current->mm->brk = ex.a_bss +
-		(current->mm->start_brk = N_BSSADDR(ex));
-
-	current->mm->rss = 0;
-	current->mm->mmap = NULL;
-	current->suid = current->euid = current->fsuid = bprm->e_uid;
-	current->sgid = current->egid = current->fsgid = bprm->e_gid;
-	if (N_MAGIC(ex) == OMAGIC) {
-#ifdef __alpha__
-		do_mmap(NULL, N_TXTADDR(ex) & PAGE_MASK,
-			ex.a_text+ex.a_data + PAGE_SIZE - 1,
-			PROT_READ|PROT_WRITE|PROT_EXEC,
-			MAP_FIXED|MAP_PRIVATE, 0);
-		read_exec(bprm->inode, fd_offset, (char *) N_TXTADDR(ex),
-			  ex.a_text+ex.a_data, 0);
-#else
-		do_mmap(NULL, 0, ex.a_text+ex.a_data,
-			PROT_READ|PROT_WRITE|PROT_EXEC,
-			MAP_FIXED|MAP_PRIVATE, 0);
-		read_exec(bprm->inode, 32, (char *) 0, ex.a_text+ex.a_data, 0);
-#endif
-	} else {
-		if (ex.a_text & 0xfff || ex.a_data & 0xfff)
-			printk(KERN_NOTICE "executable not page aligned\n");
-		
-		fd = open_inode(bprm->inode, O_RDONLY);
-		
-		if (fd < 0)
-			return fd;
-		file = current->files->fd[fd];
-		if (!file->f_op || !file->f_op->mmap) {
-			sys_close(fd);
-			do_mmap(NULL, 0, ex.a_text+ex.a_data,
-				PROT_READ|PROT_WRITE|PROT_EXEC,
-				MAP_FIXED|MAP_PRIVATE, 0);
-			read_exec(bprm->inode, fd_offset,
-				  (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0);
-			goto beyond_if;
-		}
-
-		error = do_mmap(file, N_TXTADDR(ex), ex.a_text,
-			PROT_READ | PROT_EXEC,
-			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
-			fd_offset);
-
-		if (error != N_TXTADDR(ex)) {
-			sys_close(fd);
-			send_sig(SIGKILL, current, 0);
-			return error;
-		}
-		
- 		error = do_mmap(file, N_DATADDR(ex), ex.a_data,
-				PROT_READ | PROT_WRITE | PROT_EXEC,
-				MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
-				fd_offset + ex.a_text);
-		sys_close(fd);
-		if (error != N_DATADDR(ex)) {
-			send_sig(SIGKILL, current, 0);
-			return error;
-		}
-	}
-beyond_if:
-	if (current->exec_domain && current->exec_domain->use_count)
-		(*current->exec_domain->use_count)--;
-	if (current->binfmt && current->binfmt->use_count)
-		(*current->binfmt->use_count)--;
-	current->exec_domain = lookup_exec_domain(current->personality);
-	current->binfmt = &aout_format;
-	if (current->exec_domain && current->exec_domain->use_count)
-		(*current->exec_domain->use_count)++;
-	if (current->binfmt && current->binfmt->use_count)
-		(*current->binfmt->use_count)++;
-
-	set_brk(current->mm->start_brk, current->mm->brk);
-
-	fd_offset = setup_arg_pages(ex.a_text,bprm->page) - MAX_ARG_PAGES*PAGE_SIZE;
-	p += fd_offset;
-	if (bprm->loader)
-		bprm->loader += fd_offset;
-	bprm->exec += fd_offset;
-	
-	p = (unsigned long)create_tables((char *)p, bprm,
-					current->personality != PER_LINUX);
-	current->mm->start_stack = p;
-#ifdef __alpha__
-	regs->gp = ex.a_gpvalue;
-#endif
-	start_thread(regs, ex.a_entry, p);
-	if (current->flags & PF_PTRACED)
-		send_sig(SIGTRAP, current, 0);
-	return 0;
-}
-
-
-static int load_aout_library(int fd)
-{
-        struct file * file;
-	struct exec ex;
-	struct  inode * inode;
-	unsigned int len;
-	unsigned int bss;
-	unsigned int start_addr;
-	unsigned long error;
-	
-	file = current->files->fd[fd];
-	inode = file->f_inode;
-	
-	if (!file || !file->f_op)
-		return -EACCES;
-
-	/* Seek into the file */
-	if (file->f_op->lseek) {
-		if ((error = file->f_op->lseek(inode, file, 0, 0)) != 0)
-			return -ENOEXEC;
-	} else
-		file->f_pos = 0;
-
-	set_fs(KERNEL_DS);
-	error = file->f_op->read(inode, file, (char *) &ex, sizeof(ex));
-	set_fs(USER_DS);
-	if (error != sizeof(ex))
-		return -ENOEXEC;
-
-	/* We come in here for the regular a.out style of shared libraries */
-	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
-	    N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
-	    inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
-		return -ENOEXEC;
-	}
-	if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) && 
-	    (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
-		printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
-		return -ENOEXEC;
-	}
-	
-	if (N_FLAGS(ex)) return -ENOEXEC;
-
-	/* For  QMAGIC, the starting address is 0x20 into the page.  We mask
-	   this off to get the starting address for the page */
-
-	start_addr =  ex.a_entry & 0xfffff000;
-
-	/* Now use mmap to map the library into memory. */
-	error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
-			PROT_READ | PROT_WRITE | PROT_EXEC,
-			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
-			N_TXTOFF(ex));
-	if (error != start_addr)
-		return error;
-	len = PAGE_ALIGN(ex.a_text + ex.a_data);
-	bss = ex.a_text + ex.a_data + ex.a_bss;
-	if (bss > len)
-		do_mmap(NULL, start_addr + len, bss-len,
-			PROT_READ|PROT_WRITE|PROT_EXEC,
-			MAP_PRIVATE|MAP_FIXED, 0);
-	return 0;
 }
diff -u --recursive --new-file v1.3.60/linux/fs/fat/buffer.c linux/fs/fat/buffer.c
--- v1.3.60/linux/fs/fat/buffer.c	Wed Feb  7 15:11:30 1996
+++ linux/fs/fat/buffer.c	Fri Feb  9 07:47:16 1996
@@ -1,5 +1,5 @@
 /*
- * linux/fs/msdos/buffer.c
+ * linux/fs/fat/buffer.c
  *
  *
  */
diff -u --recursive --new-file v1.3.60/linux/fs/fat/cache.c linux/fs/fat/cache.c
--- v1.3.60/linux/fs/fat/cache.c	Wed Feb  7 15:11:30 1996
+++ linux/fs/fat/cache.c	Fri Feb  9 07:47:16 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/fs/msdos/cache.c
+ *  linux/fs/fat/cache.c
  *
  *  Written 1992,1993 by Werner Almesberger
  */
diff -u --recursive --new-file v1.3.60/linux/fs/fat/dir.c linux/fs/fat/dir.c
--- v1.3.60/linux/fs/fat/dir.c	Wed Feb  7 15:11:30 1996
+++ linux/fs/fat/dir.c	Fri Feb  9 07:47:16 1996
@@ -1,6 +1,7 @@
 /*
- *  linux/fs/msdos/dir.c
- *  MS-DOS directory handling functions
+ *  linux/fs/fat/dir.c
+ *
+ *  directory handling functions for fat-based filesystems
  *
  *  Written 1992,1993 by Werner Almesberger
  *
diff -u --recursive --new-file v1.3.60/linux/fs/fat/fatfs_syms.c linux/fs/fat/fatfs_syms.c
--- v1.3.60/linux/fs/fat/fatfs_syms.c	Wed Feb  7 15:11:30 1996
+++ linux/fs/fat/fatfs_syms.c	Fri Feb  9 07:50:27 1996
@@ -1,9 +1,17 @@
+/*
+ * linux/fs/fat/fatfs_syms.c
+ *
+ * Exported kernel symbols for the low-level FAT-based fs support.
+ *
+ */
 #include <linux/module.h>
 
 #include <linux/msdos_fs.h>
 
 #include "msbuffer.h"
 #include "tables.h"
+
+extern struct file_operations fat_dir_operations;
 
 static struct symbol_table fat_syms = {
 #include <linux/symtab_begin.h>
diff -u --recursive --new-file v1.3.60/linux/fs/fat/file.c linux/fs/fat/file.c
--- v1.3.60/linux/fs/fat/file.c	Wed Feb  7 15:11:30 1996
+++ linux/fs/fat/file.c	Fri Feb  9 07:47:16 1996
@@ -1,9 +1,9 @@
 /*
- *  linux/fs/msdos/file.c
+ *  linux/fs/fat/file.c
  *
  *  Written 1992,1993 by Werner Almesberger
  *
- *  MS-DOS regular file handling primitives
+ *  regular file handling primitives for fat-based filesystems
  */
 
 #include <linux/sched.h>
diff -u --recursive --new-file v1.3.60/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v1.3.60/linux/fs/fat/inode.c	Wed Feb  7 15:11:31 1996
+++ linux/fs/fat/inode.c	Fri Feb  9 07:47:16 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/fs/msdos/inode.c
+ *  linux/fs/fat/inode.c
  *
  *  Written 1992,1993 by Werner Almesberger
  *  VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
diff -u --recursive --new-file v1.3.60/linux/fs/fat/misc.c linux/fs/fat/misc.c
--- v1.3.60/linux/fs/fat/misc.c	Wed Feb  7 15:11:31 1996
+++ linux/fs/fat/misc.c	Fri Feb  9 07:47:16 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/fs/msdos/misc.c
+ *  linux/fs/fat/misc.c
  *
  *  Written 1992,1993 by Werner Almesberger
  */
diff -u --recursive --new-file v1.3.60/linux/fs/fat/mmap.c linux/fs/fat/mmap.c
--- v1.3.60/linux/fs/fat/mmap.c	Wed Feb  7 15:11:31 1996
+++ linux/fs/fat/mmap.c	Fri Feb  9 07:47:16 1996
@@ -1,10 +1,10 @@
 /*
- *	fs/msdos/mmap.c
+ *	linux/fs/fat/mmap.c
  *
  *	Written by Jacques Gelinas (jacques@solucorp.qc.ca)
  *	Inspired by fs/nfs/mmap.c (Jon Tombs 15 Aug 1993)
  *
- *	msdos mmap handling
+ *	mmap handling for fat-based filesystems
  */
 
 #include <linux/stat.h>
diff -u --recursive --new-file v1.3.60/linux/fs/fat/tables.c linux/fs/fat/tables.c
--- v1.3.60/linux/fs/fat/tables.c	Wed Feb  7 15:11:31 1996
+++ linux/fs/fat/tables.c	Fri Feb  9 07:47:16 1996
@@ -1,8 +1,10 @@
 /*
- * linux/fs/msdos/tables.c
+ * linux/fs/fat/tables.c
  *
  * ASCII / Unicode translation tables for VFAT filename handling.
  * By Gordon Chaffee.
+ *
+ * Note: This file is used by all fat-based filesystems.
  */
 
 #include <linux/kernel.h>
diff -u --recursive --new-file v1.3.60/linux/fs/filesystems.c linux/fs/filesystems.c
--- v1.3.60/linux/fs/filesystems.c	Wed Feb  7 15:11:31 1996
+++ linux/fs/filesystems.c	Fri Feb  9 06:48:50 1996
@@ -25,6 +25,7 @@
 #include <linux/major.h>
 
 extern void device_setup(void);
+extern void binfmt_setup(void);
 
 #ifdef CONFIG_ROOT_NFS
 extern int nfs_root_init(char *nfsname, char *nfsaddrs);
@@ -43,6 +44,8 @@
 
 	device_setup();
 
+	binfmt_setup();
+
 #ifdef CONFIG_EXT_FS
 	init_ext_fs();
 #endif
@@ -59,6 +62,10 @@
 	init_minix_fs();
 #endif
 
+#ifdef CONFIG_UMSDOS_FS
+	init_umsdos_fs();
+#endif
+
 #ifdef CONFIG_FAT_FS
 	init_fat_fs();
 #endif
@@ -69,10 +76,6 @@
 
 #ifdef CONFIG_VFAT_FS
 	init_vfat_fs();
-#endif
-
-#ifdef CONFIG_UMSDOS_FS
-	init_umsdos_fs();
 #endif
 
 #ifdef CONFIG_PROC_FS
diff -u --recursive --new-file v1.3.60/linux/fs/locks.c linux/fs/locks.c
--- v1.3.60/linux/fs/locks.c	Sun Jun 11 19:00:25 1995
+++ linux/fs/locks.c	Fri Feb  9 17:01:55 1996
@@ -55,6 +55,10 @@
  *  above, mandatory locks requires lots of changes elsewhere and I am
  *  reluctant to start something so drastic for so little gain.
  *  Andy Walker (andy@keo.kvaerner.no), June 09, 1995
+ * 
+ *  Removed some race conditions in flock_lock_file(), marked other possible
+ *  races. Just grep for FIXME to see them. 
+ *  Dmitry Gorodchanin (begemot@bgm.rosprint.net), Feb 09, 1996.
  */
 
 #include <asm/segment.h>
@@ -66,6 +70,7 @@
 #include <linux/stat.h>
 #include <linux/fcntl.h>
 
+
 #define OFFSET_MAX	((off_t)0x7fffffff)	/* FIXME: move elsewhere? */
 
 static int flock_make_lock(struct file *filp, struct file_lock *fl,
@@ -88,10 +93,60 @@
 static struct file_lock *locks_alloc_lock(struct file_lock *fl);
 static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl);
 static void locks_delete_lock(struct file_lock **fl, unsigned int wait);
-static void locks_insert_block(struct file_lock **block, struct file_lock *fl);
 
 static struct file_lock *file_lock_table = NULL;
 
+/* Free lock not inserted in any queue */
+static inline void locks_free_lock(struct file_lock **fl)
+{
+	kfree(*fl);
+	*fl = NULL;		       /* Just in case */
+}
+
+/* Add lock fl to the blocked list pointed to by block.
+ * We search to the end of the existing list and insert the the new
+ * struct. This ensures processes will be woken up in the order they
+ * blocked.
+ * NOTE: nowhere does the documentation insist that processes be woken
+ * up in this order, but it seems like the reasonable thing to do.
+ * If the blocked list gets long then this search could get expensive,
+ * in which case we could consider waking the processes up in reverse
+ * order, or making the blocked list a doubly linked circular list.
+ * 
+ * This functions are called only from one place (flock_lock_file)
+ * so they are inlined now. -- Dmitry Gorodchanin 02/09/96.
+ */
+
+static inline void locks_insert_block(struct file_lock **block, 
+				      struct file_lock *fl)
+{
+	struct file_lock *bfl;
+
+	while ((bfl = *block) != NULL) {
+		block = &bfl->fl_block;
+	}
+
+	*block = fl;
+	fl->fl_block = NULL;
+	
+	return;
+}
+
+static inline void locks_delete_block(struct file_lock **block,
+				      struct file_lock *fl)
+{
+	struct file_lock *bfl;
+	
+	while ((bfl = *block) != NULL) {
+		if (bfl == fl) {
+			*block = fl->fl_block;
+			fl->fl_block = NULL;
+			return;
+		}
+		block = &bfl->fl_block;
+	}
+}
+
 /* flock() system call entry point. Apply a FLOCK style locks to
  * an open file descriptor.
  */
@@ -378,6 +433,11 @@
  * The detection scheme is recursive... we may need a test to make it exit if the
  * function gets stuck due to bad lock data. 4.4 BSD uses a maximum depth of 50
  * for this.
+ * 
+ * FIXME: 
+ * IMHO this function is dangerous, deep recursion may result in kernel stack
+ * corruption. Perhaps we need to limit depth here. 
+ *		Dmitry Gorodchanin 09/02/96
  */
 static int posix_locks_deadlock(struct task_struct *my_task,
 				struct task_struct *blocked_task)
@@ -447,19 +507,33 @@
 		
 		if (wait) {
 			if (current->signal & ~current->blocked) {
-				locks_delete_lock(&new_fl, 0);
+				/* Note: new_fl is not in any queue at this
+				 * point. So we must use locks_free_lock()
+				 * instead of locks_delete_lock()
+				 * 	Dmitry Gorodchanin 09/02/96.
+				 */
+				locks_free_lock(&new_fl);
 				return (-ERESTARTSYS);
 			}
 			locks_insert_block(&fl->fl_block, new_fl);
 			interruptible_sleep_on(&new_fl->fl_wait);
 			wake_up(&new_fl->fl_wait);
 			if (current->signal & ~current->blocked) {
-				locks_delete_lock(&new_fl, 0);
+				/* If we are here, than we were awaken
+				 * by signal, so new_fl is still in 
+				 * block queue of fl. We need remove 
+				 * new_fl and then free it. 
+				 * 	Dmitry Gorodchanin 09/02/96.
+				 */
+
+				locks_delete_block(&fl->fl_block, new_fl);
+				locks_free_lock(&new_fl);
 				return (-ERESTARTSYS);
 			}
 			goto repeat;
 		}
-		locks_delete_lock(&new_fl, 0);
+		
+		locks_free_lock(&new_fl);
 		return (-EAGAIN);
 	}
 	locks_insert_lock(&filp->f_inode->i_flock, new_fl);
@@ -516,8 +590,10 @@
 	/* First skip FLOCK locks and locks owned by other processes.
 	 */
 	while ((fl = *before) && ((fl->fl_flags == F_FLOCK) ||
-				  (caller->fl_owner != fl->fl_owner)))
+				  (caller->fl_owner != fl->fl_owner))) {
 		before = &fl->fl_next;
+	}
+	
 
 	/* Process locks with this owner.
 	 */
@@ -596,6 +672,18 @@
 		before = &(*before)->fl_next;
 	}
 
+	/* FIXME:
+	 * Note: We may sleep in locks_alloc_lock(), so
+	 * the 'before' pointer may be not valid any more.
+	 * This can cause random kernel memory corruption.
+	 * It seems the right way is to alloc two locks
+	 * at the begining of this func, and then free them
+	 * if they were not needed.
+	 * Another way is to change GFP_KERNEL to GFP_ATOMIC
+	 * in locks_alloc_lock() for this case.
+	 * 
+	 * Dmitry Gorodchanin 09/02/96.
+	 */ 
 	if (!added) {
 		if (caller->fl_type == F_UNLCK)
 			return (0);
@@ -691,9 +779,10 @@
 
 	if (fl->fl_prevlink != NULL)
 		fl->fl_prevlink->fl_nextlink = fl->fl_nextlink;
-	else
+	else {
 		file_lock_table = fl->fl_nextlink;
-
+	}
+	
 	while ((bfl = fl->fl_block) != NULL) {
 		fl->fl_block = bfl->fl_block;
 		bfl->fl_block = NULL;
@@ -707,27 +796,3 @@
 
 	return;
 }
-
-/* Add lock fl to the blocked list pointed to by block.
- * We search to the end of the existing list and insert the the new
- * struct. This ensures processes will be woken up in the order they
- * blocked.
- * NOTE: nowhere does the documentation insist that processes be woken
- * up in this order, but it seems like the reasonable thing to do.
- * If the blocked list gets long then this search could get expensive,
- * in which case we could consider waking the processes up in reverse
- * order, or making the blocked list a doubly linked circular list.
- */
-static void locks_insert_block(struct file_lock **block, struct file_lock *fl)
-{
-	struct file_lock *bfl;
-
-	while ((bfl = *block) != NULL)
-		block = &bfl->fl_block;
-
-	*block = fl;
-	fl->fl_block = NULL;
-
-	return;
-}
-
diff -u --recursive --new-file v1.3.60/linux/fs/msdos/msdosfs_syms.c linux/fs/msdos/msdosfs_syms.c
--- v1.3.60/linux/fs/msdos/msdosfs_syms.c	Wed Feb  7 15:11:33 1996
+++ linux/fs/msdos/msdosfs_syms.c	Fri Feb  9 07:47:16 1996
@@ -1,3 +1,10 @@
+/*
+ * linux/fs/msdos/msdosfs_syms.c
+ *
+ * Exported kernel symbols for the MS-DOS filesystem.
+ * These symbols are used by umsdos.
+ */
+
 #include <linux/module.h>
 
 #include <linux/msdos_fs.h>
diff -u --recursive --new-file v1.3.60/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
--- v1.3.60/linux/fs/msdos/namei.c	Wed Feb  7 15:11:34 1996
+++ linux/fs/msdos/namei.c	Fri Feb  9 07:47:16 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/fs/msdos/msdos.c
+ *  linux/fs/msdos/namei.c
  *
  *  Written 1992,1993 by Werner Almesberger
  *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
diff -u --recursive --new-file v1.3.60/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.3.60/linux/fs/proc/array.c	Wed Feb  7 15:11:34 1996
+++ linux/fs/proc/array.c	Fri Feb  9 15:24:03 1996
@@ -134,24 +134,24 @@
  */
 static int read_profile(struct inode *inode, struct file *file, char *buf, int count)
 {
-    unsigned long p = file->f_pos;
+	unsigned long p = file->f_pos;
 	int read;
 	char * pnt;
 	unsigned long sample_step = 1 << prof_shift;
 
 	if (count < 0)
-	    return -EINVAL;
+		return -EINVAL;
 	if (p >= (prof_len+1)*sizeof(unsigned long))
-	    return 0;
+		return 0;
 	if (count > (prof_len+1)*sizeof(unsigned long) - p)
-	    count = (prof_len+1)*sizeof(unsigned long) - p;
-    read = 0;
+		count = (prof_len+1)*sizeof(unsigned long) - p;
+	read = 0;
 
-    while (p < sizeof(unsigned long) && count > 0) {
-        put_user(*((char *)(&sample_step)+p),buf);
+	while (p < sizeof(unsigned long) && count > 0) {
+		put_user(*((char *)(&sample_step)+p),buf);
 		buf++; p++; count--; read++;
-    }
-    pnt = (char *)prof_buffer + p - sizeof(unsigned long);
+	}
+	pnt = (char *)prof_buffer + p - sizeof(unsigned long);
 	memcpy_tofs(buf,(void *)pnt,count);
 	read += count;
 	file->f_pos += read;
diff -u --recursive --new-file v1.3.60/linux/fs/umsdos/inode.c linux/fs/umsdos/inode.c
--- v1.3.60/linux/fs/umsdos/inode.c	Wed Feb  7 15:11:35 1996
+++ linux/fs/umsdos/inode.c	Fri Feb  9 06:48:50 1996
@@ -50,7 +50,7 @@
 
 void UMSDOS_put_super(struct super_block *sb)
 {
-	fat_put_super(sb);
+	msdos_put_super(sb);
 	MOD_DEC_USE_COUNT;
 }
 
@@ -400,7 +400,7 @@
 	*/
 	struct super_block *sb;
 	MOD_INC_USE_COUNT;
-	sb = fat_read_super(s,data,silent);
+	sb = msdos_read_super(s,data,silent);
 	printk ("UMSDOS Beta 0.6 (compatibility level %d.%d, fast msdos)\n"
 		,UMSDOS_VERSION,UMSDOS_RELEASE);
 	if (sb != NULL){
diff -u --recursive --new-file v1.3.60/linux/fs/vfat/namei.c linux/fs/vfat/namei.c
--- v1.3.60/linux/fs/vfat/namei.c	Wed Feb  7 15:11:35 1996
+++ linux/fs/vfat/namei.c	Fri Feb  9 07:47:16 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/fs/msdos/vfat.c
+ *  linux/fs/vfat/namei.c
  *
  *  Written 1992,1993 by Werner Almesberger
  *
diff -u --recursive --new-file v1.3.60/linux/include/asm-i386/smp_lock.h linux/include/asm-i386/smp_lock.h
--- v1.3.60/linux/include/asm-i386/smp_lock.h	Wed Feb  7 15:11:37 1996
+++ linux/include/asm-i386/smp_lock.h	Fri Feb  9 16:59:04 1996
@@ -24,7 +24,9 @@
 			break;
 		do 
 		{
+#ifdef __SMP_PROF__		
 			smp_spins[smp_processor_id()]++;
+#endif			
 			/*
 			 *	Doing test_bit here doesn't lock the bus 
 			 */
diff -u --recursive --new-file v1.3.60/linux/include/linux/binfmts.h linux/include/linux/binfmts.h
--- v1.3.60/linux/include/linux/binfmts.h	Sun Sep  3 12:27:02 1995
+++ linux/include/linux/binfmts.h	Thu Feb  8 05:41:50 1996
@@ -44,7 +44,9 @@
 	char * addr, unsigned long count, int to_kmem);
 
 extern int open_inode(struct inode * inode, int mode);
-extern int aout_core_dump(long signr, struct pt_regs * regs);
+
+extern int init_elf_binfmt(void);
+extern int init_aout_binfmt(void);
 
 extern void flush_old_exec(struct linux_binprm * bprm);
 extern unsigned long setup_arg_pages(unsigned long text_size,unsigned long * page);
diff -u --recursive --new-file v1.3.60/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.3.60/linux/include/linux/fs.h	Wed Feb  7 15:11:37 1996
+++ linux/include/linux/fs.h	Fri Feb  9 17:09:30 1996
@@ -505,7 +505,6 @@
 extern int fs_may_remount_ro(kdev_t dev);
 
 extern struct file *first_file;
-extern int nr_files;
 extern struct super_block super_blocks[NR_SUPER];
 
 extern void refile_buffer(struct buffer_head * buf);
diff -u --recursive --new-file v1.3.60/linux/include/linux/hdreg.h linux/include/linux/hdreg.h
--- v1.3.60/linux/include/linux/hdreg.h	Fri Sep 15 11:13:02 1995
+++ linux/include/linux/hdreg.h	Fri Feb  9 17:09:30 1996
@@ -88,18 +88,19 @@
 #define HDIO_GET_MULTCOUNT	0x0304	/* get current IDE blockmode setting */
 #define HDIO_GET_IDENTITY 	0x0307	/* get IDE identification info */
 #define HDIO_GET_KEEPSETTINGS 	0x0308	/* get keep-settings-on-reset flag */
-#define HDIO_GET_CHIPSET	0x0309	/* get current interface type setting */
+#define HDIO_GET_32BIT 		0x0309	/* get current io_32bit setting */
 #define HDIO_GET_NOWERR		0x030a	/* get ignore-write-error flag */
 #define HDIO_GET_DMA		0x030b	/* get use-dma flag */
 #define HDIO_DRIVE_CMD		0x031f	/* execute a special drive command */
 
 /* hd/ide ctl's that pass (arg) non-ptr values are numbered 0x032n/0x033n */
-#define HDIO_SET_MULTCOUNT	0x0321	/* set IDE blockmode */
+#define HDIO_SET_MULTCOUNT	0x0321	/* change IDE blockmode */
 #define HDIO_SET_UNMASKINTR	0x0322	/* permit other irqs during I/O */
 #define HDIO_SET_KEEPSETTINGS	0x0323	/* keep ioctl settings on reset */
-#define HDIO_SET_CHIPSET	0x0324	/* optimise driver for interface type */
-#define HDIO_SET_NOWERR		0x0325	/* set ignore-write-error flag */
-#define HDIO_SET_DMA		0x0326	/* set use-dma flag */
+#define HDIO_SET_32BIT		0x0324	/* change io_32bit flags */
+#define HDIO_SET_NOWERR		0x0325	/* change ignore-write-error flag */
+#define HDIO_SET_DMA		0x0326	/* change use-dma flag */
+#define HDIO_SET_PIO_MODE	0x0327	/* reconfig interface to new speed */
 
 /* structure returned by HDIO_GET_IDENTITY, as per ANSI ATA2 rev.2f spec */
 struct hd_driveid {
diff -u --recursive --new-file v1.3.60/linux/include/linux/if_slip.h linux/include/linux/if_slip.h
--- v1.3.60/linux/include/linux/if_slip.h	Sat May  7 14:12:56 1994
+++ linux/include/linux/if_slip.h	Fri Feb  9 16:59:04 1996
@@ -15,5 +15,14 @@
 #define		SL_OPT_SIXBIT		2
 #define		SL_OPT_ADAPTIVE		8
 
+/*
+ *	VSV = ioctl for keepalive & outfill in SLIP driver 
+ */
+ 
+#define SIOCSKEEPALIVE	(SIOCDEVPRIVATE)		/* Set keepalive timeout in sec */
+#define SIOCGKEEPALIVE	(SICODEVPRIVATE+1)		/* Get keepalive timeout */
+#define SIOCSOUTFILL	(SIOCDEVPRIVATE+2)		/* Set outfill timeout */
+#define	SIOCGOUTFILL	(SIOCDEVPRIVATE+3)		/* Get outfill timeout */
+
 
 #endif
diff -u --recursive --new-file v1.3.60/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v1.3.60/linux/include/linux/interrupt.h	Tue Jan  2 16:46:29 1996
+++ linux/include/linux/interrupt.h	Fri Feb  9 12:03:08 1996
@@ -53,8 +53,6 @@
 
 extern inline void end_bh_atomic(void)
 {
-	if (intr_count == 1 && (bh_active & bh_mask))
-		do_bottom_half();
 	intr_count--;
 }
 
diff -u --recursive --new-file v1.3.60/linux/include/linux/ip_fw.h linux/include/linux/ip_fw.h
--- v1.3.60/linux/include/linux/ip_fw.h	Wed Feb  7 15:11:37 1996
+++ linux/include/linux/ip_fw.h	Fri Feb  9 17:18:41 1996
@@ -16,6 +16,10 @@
  *
  * Fixes:
  *	Pauline Middelink	:	Added masquerading.
+ *	Jos Vos			:	Separate input  and output firewall
+ *					chains, new "insert" and "append"
+ *					commands to replace "add" commands,
+ *					add ICMP header to struct ip_fwpkt.
  *
  *	All the real work was done by .....
  */
@@ -59,23 +63,7 @@
 #define IP_FW_MAX_PORTS	10      		/* A reasonable maximum */
 	unsigned short fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */
 	unsigned long  fw_pcnt,fw_bcnt;		/* Packet and byte counters */
-	unsigned short fw_priority;		/* Revised packet priority */
-};
-
-struct ip_fw_old
-{
-	struct ip_fw  *fw_next;			/* Next firewall on chain */
-	struct in_addr fw_src, fw_dst;		/* Source and destination IP addr */
-	struct in_addr fw_smsk, fw_dmsk;	/* Mask for src and dest IP addr */
-	struct in_addr fw_via;			/* IP address of interface "via" */
-	unsigned short fw_flg;			/* Flags word */
-	unsigned short fw_nsp, fw_ndp;          /* N'of src ports and # of dst ports */
-						/* in ports array (dst ports follow */
-    						/* src ports; max of 10 ports in all; */
-    						/* count of 0 means match all ports) */
-#define IP_FW_MAX_PORTS	10      		/* A reasonable maximum */
-	unsigned short fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */
-	unsigned long  fw_pcnt,fw_bcnt;		/* Packet and byte counters */
+	unsigned char fw_tosand, fw_tosxor;	/* Revised packet priority */
 };
 
 /*
@@ -105,9 +93,8 @@
 #define IP_FW_F_ICMPRPL 0x100	/* Send back icmp unreachable packet  */
 #define IP_FW_F_MASQ	0x200	/* Masquerading			      */
 #define IP_FW_F_TCPACK	0x400	/* For tcp-packets match if ACK is set*/
-#define IP_FW_F_APPEND	0x800	/* Dont try to guess placement        */
 
-#define IP_FW_F_MASK	0xFFF	/* All possible flag bits mask        */
+#define IP_FW_F_MASK	0x7FF	/* All possible flag bits mask        */
 
 /*    
  *	New IP firewall options for [gs]etsockopt at the RAW IP level.
@@ -115,25 +102,54 @@
  *	a raw socket for this. Instead we check rights in the calls.
  */     
 
-#define IP_FW_BASE_CTL   64
+#define IP_FW_BASE_CTL  	64	/* base for firewall socket options */
 
-#define IP_FW_ADD_BLK    (IP_FW_BASE_CTL)
-#define IP_FW_ADD_FWD    (IP_FW_BASE_CTL+1)   
-#define IP_FW_CHK_BLK    (IP_FW_BASE_CTL+2)
-#define IP_FW_CHK_FWD    (IP_FW_BASE_CTL+3)
-#define IP_FW_DEL_BLK    (IP_FW_BASE_CTL+4)
-#define IP_FW_DEL_FWD    (IP_FW_BASE_CTL+5)
-#define IP_FW_FLUSH_BLK  (IP_FW_BASE_CTL+6)
-#define IP_FW_FLUSH_FWD  (IP_FW_BASE_CTL+7)
-#define IP_FW_ZERO_BLK   (IP_FW_BASE_CTL+8)
-#define IP_FW_ZERO_FWD   (IP_FW_BASE_CTL+9)
-#define IP_FW_POLICY_BLK (IP_FW_BASE_CTL+10)
-#define IP_FW_POLICY_FWD (IP_FW_BASE_CTL+11)
-
-#define IP_ACCT_ADD      (IP_FW_BASE_CTL+16)
-#define IP_ACCT_DEL      (IP_FW_BASE_CTL+17)
-#define IP_ACCT_FLUSH    (IP_FW_BASE_CTL+18)
-#define IP_ACCT_ZERO     (IP_FW_BASE_CTL+19)
+#define IP_FW_COMMAND		0x00FF	/* mask for command without chain */
+#define IP_FW_TYPE		0x0300	/* mask for type (chain) */
+#define IP_FW_SHIFT		8	/* shift count for type (chain) */
+
+#define IP_FW_FWD		0
+#define IP_FW_IN		1
+#define IP_FW_OUT		2
+#define IP_FW_ACCT		3
+
+#define IP_FW_INSERT		(IP_FW_BASE_CTL)
+#define IP_FW_APPEND		(IP_FW_BASE_CTL+1)
+#define IP_FW_DELETE		(IP_FW_BASE_CTL+2)
+#define IP_FW_FLUSH		(IP_FW_BASE_CTL+3)
+#define IP_FW_ZERO		(IP_FW_BASE_CTL+4)
+#define IP_FW_POLICY		(IP_FW_BASE_CTL+5)
+#define IP_FW_CHECK		(IP_FW_BASE_CTL+6)
+
+#define IP_FW_INSERT_FWD	(IP_FW_INSERT | (IP_FW_FWD << IP_FW_SHIFT))
+#define IP_FW_APPEND_FWD	(IP_FW_APPEND | (IP_FW_FWD << IP_FW_SHIFT))
+#define IP_FW_DELETE_FWD	(IP_FW_DELETE | (IP_FW_FWD << IP_FW_SHIFT))
+#define IP_FW_FLUSH_FWD		(IP_FW_FLUSH  | (IP_FW_FWD << IP_FW_SHIFT))
+#define IP_FW_ZERO_FWD		(IP_FW_ZERO   | (IP_FW_FWD << IP_FW_SHIFT))
+#define IP_FW_POLICY_FWD	(IP_FW_POLICY | (IP_FW_FWD << IP_FW_SHIFT))
+#define IP_FW_CHECK_FWD		(IP_FW_CHECK  | (IP_FW_FWD << IP_FW_SHIFT))
+
+#define IP_FW_INSERT_IN		(IP_FW_INSERT | (IP_FW_IN << IP_FW_SHIFT))
+#define IP_FW_APPEND_IN		(IP_FW_APPEND | (IP_FW_IN << IP_FW_SHIFT))
+#define IP_FW_DELETE_IN		(IP_FW_DELETE | (IP_FW_IN << IP_FW_SHIFT))
+#define IP_FW_FLUSH_IN		(IP_FW_FLUSH  | (IP_FW_IN << IP_FW_SHIFT))
+#define IP_FW_ZERO_IN		(IP_FW_ZERO   | (IP_FW_IN << IP_FW_SHIFT))
+#define IP_FW_POLICY_IN		(IP_FW_POLICY | (IP_FW_IN << IP_FW_SHIFT))
+#define IP_FW_CHECK_IN		(IP_FW_CHECK  | (IP_FW_IN << IP_FW_SHIFT))
+
+#define IP_FW_INSERT_OUT	(IP_FW_INSERT | (IP_FW_OUT << IP_FW_SHIFT))
+#define IP_FW_APPEND_OUT	(IP_FW_APPEND | (IP_FW_OUT << IP_FW_SHIFT))
+#define IP_FW_DELETE_OUT	(IP_FW_DELETE | (IP_FW_OUT << IP_FW_SHIFT))
+#define IP_FW_FLUSH_OUT		(IP_FW_FLUSH  | (IP_FW_OUT << IP_FW_SHIFT))
+#define IP_FW_ZERO_OUT		(IP_FW_ZERO   | (IP_FW_OUT << IP_FW_SHIFT))
+#define IP_FW_POLICY_OUT	(IP_FW_POLICY | (IP_FW_OUT << IP_FW_SHIFT))
+#define IP_FW_CHECK_OUT		(IP_FW_CHECK  | (IP_FW_OUT << IP_FW_SHIFT))
+
+#define IP_ACCT_INSERT		(IP_FW_INSERT | (IP_FW_ACCT << IP_FW_SHIFT))
+#define IP_ACCT_APPEND		(IP_FW_APPEND | (IP_FW_ACCT << IP_FW_SHIFT))
+#define IP_ACCT_DELETE		(IP_FW_DELETE | (IP_FW_ACCT << IP_FW_SHIFT))
+#define IP_ACCT_FLUSH		(IP_FW_FLUSH  | (IP_FW_ACCT << IP_FW_SHIFT))
+#define IP_ACCT_ZERO		(IP_FW_ZERO   | (IP_FW_ACCT << IP_FW_SHIFT))
 
 struct ip_fwpkt
 {
@@ -141,6 +157,7 @@
 	union {
 		struct tcphdr fwp_tcph;		/* TCP header or */
 		struct udphdr fwp_udph;		/* UDP header */
+		struct icmphdr fwp_icmph;	/* ICMP header */
 	} fwp_protoh;
 	struct in_addr fwp_via;			/* interface address */
 };
@@ -171,9 +188,11 @@
 extern int ip_fw_demasquerade(struct sk_buff *);
 #endif
 #ifdef CONFIG_IP_FIREWALL
-extern struct ip_fw *ip_fw_blk_chain;
+extern struct ip_fw *ip_fw_in_chain;
+extern struct ip_fw *ip_fw_out_chain;
 extern struct ip_fw *ip_fw_fwd_chain;
-extern int ip_fw_blk_policy;
+extern int ip_fw_in_policy;
+extern int ip_fw_out_policy;
 extern int ip_fw_fwd_policy;
 extern int ip_fw_ctl(int, void *, int);
 #endif
diff -u --recursive --new-file v1.3.60/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h
--- v1.3.60/linux/include/linux/msdos_fs.h	Wed Feb  7 15:11:37 1996
+++ linux/include/linux/msdos_fs.h	Fri Feb  9 17:14:48 1996
@@ -184,6 +184,7 @@
 extern void fat_put_super(struct super_block *sb);
 extern void fat_read_inode(struct inode *inode, struct inode_operations *dir_ops);
 extern struct super_block *fat_read_super(struct super_block *s, void *data, int silent);
+extern void msdos_put_super(struct super_block *sb);
 extern void fat_statfs(struct super_block *sb,struct statfs *buf, int);
 extern void fat_write_inode(struct inode *inode);
 
diff -u --recursive --new-file v1.3.60/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v1.3.60/linux/include/linux/proc_fs.h	Wed Feb  7 15:11:38 1996
+++ linux/include/linux/proc_fs.h	Fri Feb  9 17:09:40 1996
@@ -75,7 +75,8 @@
 	PROC_NET_IPMR_VIF,
 	PROC_NET_IPMR_MFC,
 	PROC_NET_IPFWFWD,
-	PROC_NET_IPFWBLK,
+	PROC_NET_IPFWIN,
+	PROC_NET_IPFWOUT,
 	PROC_NET_IPACCT,
 	PROC_NET_IPMSQHST,
 	PROC_NET_WAVELAN,
diff -u --recursive --new-file v1.3.60/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v1.3.60/linux/include/linux/skbuff.h	Wed Feb  7 15:11:38 1996
+++ linux/include/linux/skbuff.h	Fri Feb  9 17:09:40 1996
@@ -27,6 +27,9 @@
 #define FREE_READ	1
 #define FREE_WRITE	0
 
+#define CHECKSUM_NONE 0
+#define CHECKSUM_HW 1
+#define CHECKSUM_UNNECESSARY 2
 
 struct sk_buff_head 
 {
diff -u --recursive --new-file v1.3.60/linux/include/linux/smp.h linux/include/linux/smp.h
--- v1.3.60/linux/include/linux/smp.h	Wed Feb  7 15:11:38 1996
+++ linux/include/linux/smp.h	Fri Feb  9 16:59:05 1996
@@ -24,7 +24,7 @@
 extern volatile unsigned long smp_idle_count[1+NR_CPUS];/* count idle ticks */
 extern volatile unsigned long smp_idle_map;		/* map with idle cpus */
 #else
-exern volatile unsigned long smp_spins;
+extern volatile unsigned long smp_spins;
 #endif
 
 
diff -u --recursive --new-file v1.3.60/linux/include/linux/sockios.h linux/include/linux/sockios.h
--- v1.3.60/linux/include/linux/sockios.h	Fri Nov 17 08:42:28 1995
+++ linux/include/linux/sockios.h	Fri Feb  9 16:59:05 1996
@@ -71,6 +71,7 @@
 #define SIOCGIFMAP	0x8970		/* Get device parameters	*/
 #define SIOCSIFMAP	0x8971		/* Set device parameters	*/
 
+
 /* Device private ioctl calls */
 
 /*
diff -u --recursive --new-file v1.3.60/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.3.60/linux/kernel/ksyms.c	Wed Feb  7 15:11:40 1996
+++ linux/kernel/ksyms.c	Fri Feb  9 16:59:05 1996
@@ -113,6 +113,8 @@
 extern int dump_fpu(elf_fpregset_t *);
 #endif
 
+extern void dump_thread(struct pt_regs *, struct user *);
+
 struct symbol_table symbol_table = {
 #include <linux/symtab_begin.h>
 #ifdef MODVERSIONS
@@ -182,6 +184,7 @@
 	X(namei),
 	X(lnamei),
 	X(open_namei),
+	X(sys_close),
 	X(close_fp),
 	X(check_disk_change),
 	X(invalidate_buffers),
@@ -203,7 +206,6 @@
 	X(unlock_buffer),
 	X(dcache_lookup),
 	X(dcache_add),
-	X(aout_core_dump),
 	X(add_blkdev_randomness),
 	X(generic_file_read),
 	X(generic_readpage),
@@ -344,6 +346,12 @@
 	/* Socket layer registration */
 	X(sock_register),
 	X(sock_unregister),
+	/* Socket layer support routines */
+	X(skb_recv_datagram),
+	X(skb_free_datagram),
+	X(skb_copy_datagram),
+	X(skb_copy_datagram_iovec),
+	X(datagram_select),
 #ifdef CONFIG_FIREWALL
 	/* Firewall registration */
 	X(register_firewall),
@@ -468,6 +476,10 @@
 #ifdef CONFIG_BINFMT_ELF
 	X(dump_fpu),
 #endif
+	/* bimfm_aout */
+	X(dump_thread),
+	X(get_write_access),
+	X(put_write_access),
 
 	/********************************************************
 	 * Do not add anything below this line,
diff -u --recursive --new-file v1.3.60/linux/kernel/sched.c linux/kernel/sched.c
--- v1.3.60/linux/kernel/sched.c	Wed Feb  7 15:11:40 1996
+++ linux/kernel/sched.c	Fri Feb  9 17:21:43 1996
@@ -261,6 +261,11 @@
 		printk("Aiee: scheduling in interrupt\n");
 		return;
 	}
+	if (bh_active & bh_mask) {
+		intr_count = 1;
+		do_bottom_half();
+		intr_count = 0;
+	}
 	run_task_queue(&tq_scheduler);
 
 	need_resched = 0;
@@ -466,7 +471,7 @@
  * and the sorting routine counts on this..
  */
 static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL };
-#define SLOW_BUT_DEBUGGING_TIMERS 1
+#define SLOW_BUT_DEBUGGING_TIMERS 0
 
 void add_timer(struct timer_list * timer)
 {
@@ -516,18 +521,18 @@
 			__builtin_return_address(0));
 	restore_flags(flags);
 	return 0;
-#else	
+#else
+	struct timer_list * next;
+	int ret = 0;
 	save_flags(flags);
 	cli();
-	if (timer->next) {
-		timer->next->prev = timer->prev;
-		timer->prev->next = timer->next;
+	if ((next = timer->next) != NULL) {
+		(next->prev = timer->prev)->next = next;
 		timer->next = timer->prev = NULL;
-		restore_flags(flags);
-		return 1;
+		ret = 1;
 	}
 	restore_flags(flags);
-	return 0;
+	return ret;
 #endif
 }
 
diff -u --recursive --new-file v1.3.60/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v1.3.60/linux/net/appletalk/ddp.c	Wed Feb  7 15:11:40 1996
+++ linux/net/appletalk/ddp.c	Fri Feb  9 16:59:05 1996
@@ -1033,7 +1033,8 @@
 		return err;
 	put_user(sizeof(int),optlen);
 	err=verify_area(VERIFY_WRITE,optval,sizeof(int));
-	if (err) return err;
+	if (err) 
+		return err;
 	put_user(val,optval);
 	return(0);
 }
diff -u --recursive --new-file v1.3.60/linux/net/core/dev.c linux/net/core/dev.c
--- v1.3.60/linux/net/core/dev.c	Wed Feb  7 15:11:41 1996
+++ linux/net/core/dev.c	Thu Feb  8 05:05:29 1996
@@ -1343,7 +1343,7 @@
 	 *	SLHC if present needs attaching so other people see it
 	 *	even if not opened.
 	 */
-#if defined(CONFIG_SLIP_COMPRESSED) || defined(CONFIG_PPP)
+#if (defined(CONFIG_SLIP_COMPRESSED) || defined(CONFIG_PPP)) && defined(CONFIG_SLHC_BUILTIN)
 	slhc_install();
 #endif	
 
diff -u --recursive --new-file v1.3.60/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v1.3.60/linux/net/ipv4/igmp.c	Wed Feb  7 15:11:42 1996
+++ linux/net/ipv4/igmp.c	Thu Feb  8 04:56:28 1996
@@ -51,6 +51,8 @@
  *					which caused a "del_timer() called 
  *					from %p with timer not initialized\n"
  *					message (960131).
+ *		Christian Daudt :	removed del_timer from 
+ *					igmp_timer_expire function (960205).
  */
 
 
@@ -259,7 +261,8 @@
 {
 	struct ip_mc_list *im=(struct ip_mc_list *)data;
 	struct ip_router_info *r;
-	igmp_stop_timer(im);
+
+	im->tm_running=0;
 	r=igmp_get_mrouter_info(im->interface);
 	if(r==NULL)
 		return;
diff -u --recursive --new-file v1.3.60/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v1.3.60/linux/net/ipv4/ip_forward.c	Wed Feb  7 15:11:42 1996
+++ linux/net/ipv4/ip_forward.c	Thu Feb  8 12:51:25 1996
@@ -10,6 +10,8 @@
  * Fixes:
  *		Many		:	Split from ip.c , see ip_input.c for history.
  *		Dave Gregorich	:	NULL ip_rt_put fix for multicast routing.
+ *		Jos Vos		:	Add call_out_firewall before sending,
+ *					use output device for accounting.
  */
 
 #include <linux/config.h>
@@ -340,8 +342,20 @@
 #ifdef CONFIG_IP_MROUTE
 			}				
 #endif			
-			ip_statistics.IpForwDatagrams++;
 		}
+#ifdef CONFIG_FIREWALL
+		if((fw_res = call_out_firewall(PF_INET, skb2, iph)) < FW_ACCEPT)
+		{
+			/* FW_ACCEPT and FW_MASQUERADE are treated equal:
+			   masquerading is only supported via forward rules */
+			if (fw_res == FW_REJECT)
+				icmp_send(skb2, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
+			if (skb != skb2)
+				kfree_skb(skb2,FREE_WRITE);
+			return -1;
+		}
+#endif
+		ip_statistics.IpForwDatagrams++;
 
 		if (opt->optlen) 
 		{
@@ -413,7 +427,7 @@
 			 *	Count mapping we shortcut
 			 */
 			 
-			ip_fw_chk(iph,dev,ip_acct_chain,IP_FW_F_ACCEPT,1);
+			ip_fw_chk(iph,dev2,ip_acct_chain,IP_FW_F_ACCEPT,1);
 #endif			
 			
 			/*
diff -u --recursive --new-file v1.3.60/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v1.3.60/linux/net/ipv4/ip_fw.c	Wed Feb  7 15:11:42 1996
+++ linux/net/ipv4/ip_fw.c	Thu Feb  8 12:51:25 1996
@@ -26,6 +26,11 @@
  *		Alan Cox 25/8/95, based on information from bugtraq.
  *	ICMP type printk, IP_FW_F_APPEND
  *		Bernd Eckenfels 1996-01-31
+ *	Split blocking chain into input and output chains, add new "insert" and
+ *	"append" commands to replace semi-intelligent "add" command, let "delete".
+ *	only delete the first matching entry, use 0xFFFF (0xFF) as ports (ICMP
+ *	types) when counting packets being 2nd and further fragments.
+ *		Jos Vos <jos@xos.nl> 8/2/1996.
  *
  * Masquerading functionality
  *
@@ -99,7 +104,7 @@
  *	Implement IP packet firewall
  */
 
-#ifdef CONFIG_IPFIREWALL_DEBUG 
+#ifdef CONFIG_IP_FIREWALL_DEBUG 
 #define dprintf1(a)		printk(a)
 #define dprintf2(a1,a2)		printk(a1,a2)
 #define dprintf3(a1,a2,a3)	printk(a1,a2,a3)
@@ -116,25 +121,30 @@
 					      (ntohl(a)>>8)&0xFF,\
 					      (ntohl(a))&0xFF);
 
-#ifdef IPFIREWALL_DEBUG
+#ifdef CONFIG_IP_FIREWALL_DEBUG
 #define dprint_ip(a)	print_ip(a)
 #else
 #define dprint_ip(a)	
 #endif
 
-#ifdef CONFIG_IP_FIREWALL
+#if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL)
+
 struct ip_fw *ip_fw_fwd_chain;
-struct ip_fw *ip_fw_blk_chain;
-int ip_fw_blk_policy=IP_FW_F_ACCEPT;
-int ip_fw_fwd_policy=IP_FW_F_ACCEPT;
-#endif
-#ifdef CONFIG_IP_ACCT
+struct ip_fw *ip_fw_in_chain;
+struct ip_fw *ip_fw_out_chain;
 struct ip_fw *ip_acct_chain;
-#endif
 
-#define IP_INFO_BLK	0
-#define IP_INFO_FWD	1
-#define IP_INFO_ACCT	2
+static struct ip_fw **chains[] =
+	{&ip_fw_fwd_chain, &ip_fw_in_chain, &ip_fw_out_chain, &ip_acct_chain};
+
+int ip_fw_fwd_policy=IP_FW_F_ACCEPT;
+int ip_fw_in_policy=IP_FW_F_ACCEPT;
+int ip_fw_out_policy=IP_FW_F_ACCEPT;
+
+static int *policies[] =
+	{&ip_fw_fwd_policy, &ip_fw_in_policy, &ip_fw_out_policy};
+
+#endif
 
 #ifdef CONFIG_IP_MASQUERADE
 /*
@@ -195,11 +205,12 @@
 	struct udphdr		*udp=(struct udphdr *)((unsigned long *)ip+ip->ihl);
 	struct icmphdr		*icmp=(struct icmphdr *)((unsigned long *)ip+ip->ihl);
 	__u32			src, dst;
-	__u16			src_port=0, dst_port=0, icmp_type=0;
+	__u16			src_port=0xFFFF, dst_port=0xFFFF, icmp_type=0xFF;
 	unsigned short		f_prt=0, prt;
 	char			notcpsyn=1, notcpack=1, match;
 	unsigned short		offset;
-	int			answer, priority;
+	int			answer;
+	unsigned char		tosand, tosxor;
 
 	/*
 	 *	If the chain is empty follow policy. The BSD one
@@ -237,7 +248,7 @@
 		return FW_BLOCK;
 		
 	if (offset!=0 && (opt != 1) && (ip->protocol == IPPROTO_TCP ||
-			ip->protocol == IPPROTO_UDP))
+			ip->protocol == IPPROTO_UDP || ip->protocol == IPPROTO_ICMP))
 		return FW_ACCEPT;
 		
 	/*
@@ -270,7 +281,7 @@
 	{
 		case IPPROTO_TCP:
 			dprintf1("TCP ");
-			/* ports stay 0 if it is not the first fragment */
+			/* ports stay 0xFFFF if it is not the first fragment */
 			if (!offset) {
 				src_port=ntohs(tcp->source);
 				dst_port=ntohs(tcp->dest);
@@ -285,7 +296,7 @@
 			break;
 		case IPPROTO_UDP:
 			dprintf1("UDP ");
-			/* ports stay 0 if it is not the first fragment */
+			/* ports stay 0xFFFF if it is not the first fragment */
 			if (!offset) {
 				src_port=ntohs(udp->source);
 				dst_port=ntohs(udp->dest);
@@ -293,7 +304,9 @@
 			prt=IP_FW_F_UDP;
 			break;
 		case IPPROTO_ICMP:
-			icmp_type=(__u16)(icmp->type);
+			/* icmp_type stays 255 if it is not the first fragment */
+			if (!offset)
+				icmp_type=(__u16)(icmp->type);
 			dprintf2("ICMP:%d ",icmp_type);
 			prt=IP_FW_F_ICMP;
 			break;
@@ -306,11 +319,11 @@
 	dprint_ip(ip->saddr);
 	
 	if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP)
-		/* This will print 0 when it is not the first fragment! */
+		/* This will print 65535 when it is not the first fragment! */
 		dprintf2(":%d ", src_port);
 	dprint_ip(ip->daddr);
 	if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP)
-		/* This will print 0 when it is not the first fragment! */
+		/* This will print 65535 when it is not the first fragment! */
 		dprintf2(":%d ",dst_port);
 	dprintf1("\n");
 #endif	
@@ -372,28 +385,17 @@
 		if (f_prt!=IP_FW_F_ALL) 
 		{
 			/*
-			 * This is actually buggy as if you set SYN flag 
+			 * This is actually buggy as if you set ACK/SYN flags
 			 * on UDP or ICMP firewall it will never work,but 
 			 * actually it is a concern of software which sets
 			 * firewall entries.
 			 */
 			 
-			 if((f->fw_flg&IP_FW_F_TCPSYN) && notcpsyn)
+			if((f->fw_flg&IP_FW_F_TCPSYN) && notcpsyn)
 			 	continue;
 
-			/*
-			 * When a bidirectional rule is used we only check
-			 * for ack bits on reverse matches. This way it's
-			 * easy to set up rules which only allow connections
-			 * initiated from "normal" match adresses.
-			 */
-
 			if((f->fw_flg&IP_FW_F_TCPACK) && notcpack)
-				if(f->fw_flg&IP_FW_F_BIDIR) {
-					if(match & 0x02)
-						continue;
-				} else
-					continue;
+			 	continue;
 
 			/*
 			 *	Specific firewall - packet's protocol
@@ -422,15 +424,18 @@
 #ifdef CONFIG_IP_FIREWALL_VERBOSE
 		/*
 		 * VERY ugly piece of code which actually
-		 * makes kernel printf for denied packets...
+		 * makes kernel printf for matching packets...
 		 */
 
 		if (f->fw_flg & IP_FW_F_PRN)
 		{
 			if(opt != 1) {
-				if(f->fw_flg&IP_FW_F_ACCEPT)
-					printk("Accept ");
-				else if(f->fw_flg&IP_FW_F_ICMPRPL)
+				if(f->fw_flg&IP_FW_F_ACCEPT) {
+					if(f->fw_flg&IP_FW_F_MASQ)
+						printk("Masquerade ");
+					else
+						printk("Accept ");
+				} else if(f->fw_flg&IP_FW_F_ICMPRPL)
 					printk("Reject ");
 				else
 					printk("Deny ");
@@ -443,7 +448,7 @@
 				case IPPROTO_UDP:
 					printk("UDP ");
 				case IPPROTO_ICMP:
-					printk("ICMP:%d ",icmp_type);
+					printk("ICMP:%d ", icmp_type);
 					break;
 				default:
 					printk("p=%d ",ip->protocol);
@@ -478,10 +483,12 @@
 	if(f!=NULL) 
 	{
 		policy=f->fw_flg;
-		priority=f->fw_priority;
+		tosand=f->fw_tosand;
+		tosxor=f->fw_tosxor;
 	}
 	else
-		priority=0xFF00;
+		tosand=0xFF;
+		tosxor=0x00;
 
 	if(opt != 1) 
 	{
@@ -492,9 +499,9 @@
 				answer = FW_REJECT;
 	}
 
-	if (answer == 0) { /* Adjust priority and recompute checksum */
+	if (policy&IP_FW_F_ACCEPT) { /* Adjust priority and recompute checksum */
 		__u8 old_tos = ip->tos;
-		ip->tos = (old_tos & (priority>>8)) ^ priority;
+		ip->tos = (old_tos & tosand) ^ tosxor;
 		if (ip->tos != old_tos)
 		 	ip_send_check(ip);
 	}
@@ -1039,18 +1046,10 @@
 
 /* Volatiles to keep some of the compiler versions amused */
 
-static int add_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl,int len)
+static int insert_in_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl,int len)
 {
 	struct ip_fw *ftmp;
-	struct ip_fw *chtmp=NULL;
-	struct ip_fw *volatile chtmp_prev=NULL;
 	unsigned long flags;
-	unsigned long m_src_mask,m_dst_mask;
-	unsigned long n_sa,n_da,o_sa,o_da,o_sm,o_dm,n_sm,n_dm;
-	unsigned short n_sr,n_dr,o_sr,o_dr; 
-	unsigned short oldkind,newkind;
-	int addb4=0;
-	int n_o,n_n;
 
 	save_flags(flags);
 
@@ -1064,164 +1063,50 @@
 	}
 
 	memcpy(ftmp, frwl, len);
-	if (len == sizeof (struct ip_fw_old))
-		ftmp->fw_priority = 0xFF00; /* and_mask, xor_mask */
-
-	ftmp->fw_priority = (ftmp->fw_priority & 0xFFFC) | 0x0300;
+	ftmp->fw_tosand |= 0x03;
+	ftmp->fw_tosxor &= 0xFC;
 	ftmp->fw_pcnt=0L;
 	ftmp->fw_bcnt=0L;
 
-	ftmp->fw_next = NULL;
-
 	cli();
 
-	if (*chainptr==NULL)
-	{
-		*chainptr=ftmp;
-	}
-	else
-	{
-		chtmp_prev=NULL;
-		for (chtmp=*chainptr;chtmp!=NULL;chtmp=chtmp->fw_next) 
-		{
-			if (ftmp->fw_flg & IP_FW_F_APPEND) {
-				chtmp_prev=chtmp;
-				continue;
-			}
-			addb4=0;
-			newkind=ftmp->fw_flg & IP_FW_F_KIND;
-			oldkind=chtmp->fw_flg & IP_FW_F_KIND;
-	
-			if (newkind!=IP_FW_F_ALL 
-				&&  oldkind!=IP_FW_F_ALL
-				&&  oldkind!=newkind) 
-			{
-				chtmp_prev=chtmp;
-				continue;
-			}
+	ftmp->fw_next = *chainptr;
+       	*chainptr=ftmp;
+	restore_flags(flags);
+	return(0);
+}
 
-			/*
-			 *	Very very *UGLY* code...
-			 *	Sorry,but i had to do this....
-			 */
+static int append_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl,int len)
+{
+	struct ip_fw *ftmp;
+	struct ip_fw *chtmp=NULL;
+	struct ip_fw *volatile chtmp_prev=NULL;
+	unsigned long flags;
 
-			n_sa=ntohl(ftmp->fw_src.s_addr);
-			n_da=ntohl(ftmp->fw_dst.s_addr);
-			n_sm=ntohl(ftmp->fw_smsk.s_addr);
-			n_dm=ntohl(ftmp->fw_dmsk.s_addr);
-
-			o_sa=ntohl(chtmp->fw_src.s_addr);
-			o_da=ntohl(chtmp->fw_dst.s_addr);
-			o_sm=ntohl(chtmp->fw_smsk.s_addr);
-			o_dm=ntohl(chtmp->fw_dmsk.s_addr);
+	save_flags(flags);
 
-			m_src_mask = o_sm & n_sm;
-			m_dst_mask = o_dm & n_dm;
+	ftmp = kmalloc( sizeof(struct ip_fw), GFP_ATOMIC );
+	if ( ftmp == NULL ) 
+	{
+#ifdef DEBUG_CONFIG_IP_FIREWALL
+		printk("ip_fw_ctl:  malloc said no\n");
+#endif
+		return( ENOMEM );
+	}
 
-			if ((o_sa & m_src_mask) == (n_sa & m_src_mask)) 
-			{
-				if (n_sm > o_sm) 
-					addb4++;
-				if (n_sm < o_sm) 
-					addb4--;
-			}
+	memcpy(ftmp, frwl, len);
+	ftmp->fw_tosand |= 0x03;
+	ftmp->fw_tosxor &= 0xFC;
+	ftmp->fw_pcnt=0L;
+	ftmp->fw_bcnt=0L;
 
-			if ((o_da & m_dst_mask) == (n_da & m_dst_mask)) 
-			{
-				if (n_dm > o_dm)
-					addb4++;
-				if (n_dm < o_dm)
-					addb4--;
-			}
+	ftmp->fw_next = NULL;
 
-			if (((o_da & o_dm) == (n_da & n_dm))
-               			&&((o_sa & o_sm) == (n_sa & n_sm)))
-			{
-				if (newkind!=IP_FW_F_ALL &&
-					oldkind==IP_FW_F_ALL)
-					addb4++;
-				if (newkind==oldkind && (oldkind==IP_FW_F_TCP
-					||  oldkind==IP_FW_F_UDP)) 
-				{
+	cli();
 
-					/*
-					 * 	Here the main idea is to check the size
-					 * 	of port range which the frwl covers
-					 * 	We actually don't check their values but
-					 *	just the wideness of range they have
-					 *	so that less wide ranges or single ports
-					 *	go first and wide ranges go later. No ports
-					 *	at all treated as a range of maximum number
-					 *	of ports.
-					 */
-
-					if (ftmp->fw_flg & IP_FW_F_SRNG) 
-						n_sr=ftmp->fw_pts[1]-ftmp->fw_pts[0];
-					else 
-						n_sr=(ftmp->fw_nsp)?
-							ftmp->fw_nsp : 0xFFFF;
-						
-					if (chtmp->fw_flg & IP_FW_F_SRNG) 
-						o_sr=chtmp->fw_pts[1]-chtmp->fw_pts[0];
-					else 
-						o_sr=(chtmp->fw_nsp)?chtmp->fw_nsp : 0xFFFF;
-
-					if (n_sr<o_sr)
-						addb4++;
-					if (n_sr>o_sr)
-						addb4--;
-					
-					n_n=ftmp->fw_nsp;
-					n_o=chtmp->fw_nsp;
-	
-					/*
-					 * Actually this cannot happen as the frwl control
-					 * procedure checks for number of ports in source and
-					 * destination range but we will try to be more safe.
-					 */
-					 
-					if ((n_n>(IP_FW_MAX_PORTS-2)) ||
-						(n_o>(IP_FW_MAX_PORTS-2)))
-						goto skip_check;
-
-					if (ftmp->fw_flg & IP_FW_F_DRNG) 
-					       n_dr=ftmp->fw_pts[n_n+1]-ftmp->fw_pts[n_n];
-					else 
-					       n_dr=(ftmp->fw_ndp)? ftmp->fw_ndp : 0xFFFF;
-
-					if (chtmp->fw_flg & IP_FW_F_DRNG) 
-						o_dr=chtmp->fw_pts[n_o+1]-chtmp->fw_pts[n_o];
-					else 
-						o_dr=(chtmp->fw_ndp)? chtmp->fw_ndp : 0xFFFF;
-					if (n_dr<o_dr)
-						addb4++;
-					if (n_dr>o_dr)
-						addb4--;
-skip_check:
-				}
-				/* finally look at the interface address */
-				if ((addb4 == 0) && ftmp->fw_via.s_addr &&
-						!(chtmp->fw_via.s_addr))
-					addb4++;
-			}
-			if (addb4>0) 
-			{
-				if (chtmp_prev) 
-				{
-					chtmp_prev->fw_next=ftmp; 
-					ftmp->fw_next=chtmp;
-				} 
-				else 
-				{
-					*chainptr=ftmp;
-					ftmp->fw_next=chtmp;
-				}
-				restore_flags(flags);
-				return 0;
-			}
-			chtmp_prev=chtmp;
-		}
-	}
+	chtmp_prev=NULL;
+	for (chtmp=*chainptr;chtmp!=NULL;chtmp=chtmp->fw_next) 
+		chtmp_prev=chtmp;
 	
 	if (chtmp_prev)
 		chtmp_prev->fw_next=ftmp;
@@ -1255,7 +1140,7 @@
 	ltmp=NULL;
 	was_found=0;
 
-	while( ftmp != NULL )
+	while( !was_found && ftmp != NULL )
 	{
 		matches=1;
 	     if (ftmp->fw_src.s_addr!=frwl->fw_src.s_addr 
@@ -1310,7 +1195,7 @@
 struct ip_fw *check_ipfw_struct(struct ip_fw *frwl, int len)
 {
 
-	if ( len != sizeof(struct ip_fw) && len != sizeof(struct ip_fw_old))
+	if ( len != sizeof(struct ip_fw) )
 	{
 #ifdef DEBUG_CONFIG_IP_FIREWALL
 		printk("ip_fw_ctl: len=%d, want %d\n",len, sizeof(struct ip_fw));
@@ -1382,9 +1267,8 @@
 		zero_fw_chain(ip_acct_chain);
 		return(0);
 	}
-	if ( stage == IP_ACCT_ADD
-	  || stage == IP_ACCT_DEL
-	   )
+	if ( stage == IP_ACCT_INSERT || stage == IP_ACCT_APPEND ||
+	  				stage == IP_ACCT_DELETE )
 	{
 		struct ip_fw *frwl;
 
@@ -1393,9 +1277,11 @@
 
 		switch (stage) 
 		{
-			case IP_ACCT_ADD:
-				return( add_to_chain(&ip_acct_chain,frwl,len));
-		    	case IP_ACCT_DEL:
+			case IP_ACCT_INSERT:
+				return( insert_in_chain(&ip_acct_chain,frwl,len));
+			case IP_ACCT_APPEND:
+				return( append_to_chain(&ip_acct_chain,frwl,len));
+		    	case IP_ACCT_DELETE:
 				return( del_from_chain(&ip_acct_chain,frwl));
 			default:
 				/*
@@ -1417,44 +1303,32 @@
 #ifdef CONFIG_IP_FIREWALL
 int ip_fw_ctl(int stage, void *m, int len)
 {
-	int ret;
+	int ret, cmd, fwtype;
 
-	if ( stage == IP_FW_FLUSH_BLK )
-	{
-		free_fw_chain(&ip_fw_blk_chain);
-		return(0);
-	}  
-
-	if ( stage == IP_FW_FLUSH_FWD )
-	{
-		free_fw_chain(&ip_fw_fwd_chain);
-		return(0);
-	}  
+	cmd = stage & IP_FW_COMMAND;
+	fwtype = (stage & IP_FW_TYPE) >> IP_FW_SHIFT;
 
-	if ( stage == IP_FW_ZERO_BLK )
+	if ( cmd == IP_FW_FLUSH )
 	{
-		zero_fw_chain(ip_fw_blk_chain);
+		free_fw_chain(chains[fwtype]);
 		return(0);
 	}  
 
-	if ( stage == IP_FW_ZERO_FWD )
+	if ( cmd == IP_FW_ZERO )
 	{
-		zero_fw_chain(ip_fw_fwd_chain);
+		zero_fw_chain(*chains[fwtype]);
 		return(0);
 	}  
 
-	if ( stage == IP_FW_POLICY_BLK || stage == IP_FW_POLICY_FWD )
+	if ( cmd == IP_FW_POLICY )
 	{
 		int *tmp_policy_ptr;
 		tmp_policy_ptr=(int *)m;
-		if ( stage == IP_FW_POLICY_BLK )
-			ip_fw_blk_policy=*tmp_policy_ptr;
-		else
-			ip_fw_fwd_policy=*tmp_policy_ptr;
+		*policies[fwtype] = *tmp_policy_ptr;
 		return 0;
 	}
 
-	if ( stage == IP_FW_CHK_BLK || stage == IP_FW_CHK_FWD )
+	if ( cmd == IP_FW_CHECK )
 	{
 		struct device viadev;
 		struct ip_fwpkt *ipfwp;
@@ -1483,16 +1357,14 @@
 
 		viadev.pa_addr = ipfwp->fwp_via.s_addr;
 
-		if ((ret = ip_fw_chk(ip, &viadev,
-			stage == IP_FW_CHK_BLK ?
-	                ip_fw_blk_chain : ip_fw_fwd_chain,
-			stage == IP_FW_CHK_BLK ?
-	                ip_fw_blk_policy : ip_fw_fwd_policy, 2 )) > 0
-		   )
+		if ((ret = ip_fw_chk(ip, &viadev, *chains[fwtype],
+				*policies[fwtype], 2)) == FW_ACCEPT)
 			return(0);
-	    	else if (ret == -1)	
+	    	else if (ret == FW_MASQUERADE)	
+			return(ECONNRESET);
+	    	else if (ret == FW_REJECT)	
 			return(ECONNREFUSED);
-		else
+		else /* ret == FW_BLOCK */
 			return(ETIMEDOUT);
 	}
 
@@ -1501,25 +1373,24 @@
  *	to blocking/forwarding chains or deleting 'em
  */
 
-	if ( stage == IP_FW_ADD_BLK || stage == IP_FW_ADD_FWD
-		|| stage == IP_FW_DEL_BLK || stage == IP_FW_DEL_FWD
-		)
+	if ( cmd == IP_FW_INSERT || cmd == IP_FW_APPEND || cmd == IP_FW_DELETE )
 	{
 		struct ip_fw *frwl;
+		int fwtype;
+
 		frwl=check_ipfw_struct(m,len);
 		if (frwl==NULL)
 			return (EINVAL);
+		fwtype = (stage & IP_FW_TYPE) >> IP_FW_SHIFT;
 		
-		switch (stage) 
+		switch (cmd) 
 		{
-			case IP_FW_ADD_BLK:
-				return(add_to_chain(&ip_fw_blk_chain,frwl,len));
-			case IP_FW_ADD_FWD:
-				return(add_to_chain(&ip_fw_fwd_chain,frwl,len));
-			case IP_FW_DEL_BLK:
-				return(del_from_chain(&ip_fw_blk_chain,frwl));
-			case IP_FW_DEL_FWD: 
-				return(del_from_chain(&ip_fw_fwd_chain,frwl));
+			case IP_FW_INSERT:
+				return(insert_in_chain(chains[fwtype],frwl,len));
+			case IP_FW_APPEND:
+				return(append_to_chain(chains[fwtype],frwl,len));
+			case IP_FW_DELETE:
+				return(del_from_chain(chains[fwtype],frwl));
 			default:
 			/*
 	 		 *	Should be panic but... (Why are BSD people panic obsessed ??)
@@ -1552,19 +1423,24 @@
 	switch(stage)
 	{
 #ifdef CONFIG_IP_FIREWALL
-		case IP_INFO_BLK:
-			i = ip_fw_blk_chain;
-			len=sprintf(buffer, "IP firewall block rules, default %d\n",
-				ip_fw_blk_policy);
+		case IP_FW_IN:
+			i = ip_fw_in_chain;
+			len=sprintf(buffer, "IP firewall input rules, default %d\n",
+				ip_fw_in_policy);
+			break;
+		case IP_FW_OUT:
+			i = ip_fw_out_chain;
+			len=sprintf(buffer, "IP firewall output rules, default %d\n",
+				ip_fw_out_policy);
 			break;
-		case IP_INFO_FWD:
+		case IP_FW_FWD:
 			i = ip_fw_fwd_chain;
 			len=sprintf(buffer, "IP firewall forward rules, default %d\n",
 				ip_fw_fwd_policy);
 			break;
 #endif
 #ifdef CONFIG_IP_ACCT
-		case IP_INFO_ACCT:
+		case IP_FW_ACCT:
 			i = ip_acct_chain;
 			len=sprintf(buffer,"IP accounting rules\n");
 			break;
@@ -1589,7 +1465,7 @@
 			i->fw_nsp,i->fw_ndp, i->fw_pcnt,i->fw_bcnt);
 		for (p = 0; p < IP_FW_MAX_PORTS; p++)
 			len+=sprintf(buffer+len, " %u", i->fw_pts[p]);
-		len+=sprintf(buffer+len, " M%04X", i->fw_priority);
+		len+=sprintf(buffer+len, " A%02X X%02X", i->fw_tosand, i->fw_tosxor);
 		buffer[len++]='\n';
 		buffer[len]='\0';
 		pos=begin+len;
@@ -1622,7 +1498,7 @@
 static int ip_acct_procinfo(char *buffer, char **start, off_t offset,
 			    int length, int reset)
 {
-	return ip_chain_procinfo(IP_INFO_ACCT, buffer,start, offset,length,
+	return ip_chain_procinfo(IP_FW_ACCT, buffer,start, offset,length,
 				 reset);
 }
 
@@ -1630,17 +1506,24 @@
 
 #ifdef CONFIG_IP_FIREWALL
 
-static int ip_fw_blk_procinfo(char *buffer, char **start, off_t offset,
+static int ip_fw_in_procinfo(char *buffer, char **start, off_t offset,
+			      int length, int reset)
+{
+	return ip_chain_procinfo(IP_FW_IN, buffer,start,offset,length,
+				 reset);
+}
+
+static int ip_fw_out_procinfo(char *buffer, char **start, off_t offset,
 			      int length, int reset)
 {
-	return ip_chain_procinfo(IP_INFO_BLK, buffer,start,offset,length,
+	return ip_chain_procinfo(IP_FW_OUT, buffer,start,offset,length,
 				 reset);
 }
 
 static int ip_fw_fwd_procinfo(char *buffer, char **start, off_t offset,
 			      int length, int reset)
 {
-	return ip_chain_procinfo(IP_INFO_FWD, buffer,start,offset,length,
+	return ip_chain_procinfo(IP_FW_FWD, buffer,start,offset,length,
 				 reset);
 }
 #endif
@@ -1655,7 +1538,7 @@
 	unsigned long flags;
 	int len=0;
 	
-	len=sprintf(buffer,"Prc FromIP   FPrt ToIP     TPrt Masq Init-seq Delta PDelta Expires\n"); 
+	len=sprintf(buffer,"Prc FromIP   FPrt ToIP     TPrt Masq Init-seq  Delta PDelta Expires\n"); 
 	save_flags(flags);
 	cli();
 	
@@ -1665,7 +1548,7 @@
 		int timer_active = del_timer(&ms->timer);
 		if (!timer_active)
 			ms->timer.expires = jiffies;
-		len+=sprintf(buffer+len,"%s %08lX:%04X %08lX:%04X %04X %08X %5d %5d %lu\n",
+		len+=sprintf(buffer+len,"%s %08lX:%04X %08lX:%04X %04X %08X %6d %6d %lu\n",
 			strProt[ms->protocol==IPPROTO_TCP],
 			ntohl(ms->src),ntohs(ms->sport),
 			ntohl(ms->dst),ntohs(ms->dport),
@@ -1701,7 +1584,12 @@
  
 int ipfw_input_check(struct firewall_ops *this, int pf, struct sk_buff *skb, void *phdr)
 {
-	return ip_fw_chk(phdr, skb->dev, ip_fw_blk_chain, ip_fw_blk_policy, 0);
+	return ip_fw_chk(phdr, skb->dev, ip_fw_in_chain, ip_fw_in_policy, 0);
+}
+
+int ipfw_output_check(struct firewall_ops *this, int pf, struct sk_buff *skb, void *phdr)
+{
+	return ip_fw_chk(phdr, skb->dev, ip_fw_out_chain, ip_fw_out_policy, 0);
 }
 
 int ipfw_forward_check(struct firewall_ops *this, int pf, struct sk_buff *skb, void *phdr)
@@ -1714,7 +1602,7 @@
 	NULL,
 	ipfw_forward_check,
 	ipfw_input_check,
-	ipfw_input_check,
+	ipfw_output_check,
 	PF_INET,
 	0	/* We don't even allow a fall through so we are last */
 };
@@ -1737,10 +1625,16 @@
 		panic("Unable to register IP firewall.\n");
 		
 	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_IPFWBLK, 8, "ip_block",
+		PROC_NET_IPFWIN, 8, "ip_input",
+		S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0,
+		0, &proc_net_inode_operations,
+		ip_fw_in_procinfo
+	});
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_IPFWOUT, 9, "ip_output",
 		S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0,
 		0, &proc_net_inode_operations,
-		ip_fw_blk_procinfo
+		ip_fw_out_procinfo
 	});
 	proc_net_register(&(struct proc_dir_entry) {
 		PROC_NET_IPFWFWD, 10, "ip_forward",
diff -u --recursive --new-file v1.3.60/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v1.3.60/linux/net/ipv4/ip_input.c	Wed Feb  7 15:11:42 1996
+++ linux/net/ipv4/ip_input.c	Thu Feb  8 12:51:25 1996
@@ -95,6 +95,7 @@
  *		Alan Cox	:	Outgoing firewall on build_xmit
  *		A.N.Kuznetsov	:	IP_OPTIONS support throughout the kernel
  *		Alan Cox	:	Multicast routing hooks
+ *		Jos Vos		:	Do accounting *before* call_in_firewall
  *
  *  
  *
@@ -286,11 +287,20 @@
 #endif
 
 	/*
-	 *	See if the firewall wants to dispose of the packet. 
+	 *	Account for the packet (even if the packet is
+	 *	not accepted by the firewall!).
 	 */
 
-#ifdef	CONFIG_FIREWALL
+#ifdef CONFIG_IP_ACCT
+	ip_fw_chk(iph,dev,ip_acct_chain,IP_FW_F_ACCEPT,1);
+#endif	
+
+	/*
+	 *	See if the firewall wants to dispose of the packet. 
+	 */
 	
+#ifdef	CONFIG_FIREWALL
+
 	if ((err=call_in_firewall(PF_INET, skb, iph))<FW_ACCEPT)
 	{
 		if(err==FW_REJECT)
@@ -421,14 +431,6 @@
 			return(0);
 		}
 #endif
-
-		/*
-		 *	Account for the packet
-		 */
- 
-#ifdef CONFIG_IP_ACCT
-		ip_fw_chk(iph,dev,ip_acct_chain,IP_FW_F_ACCEPT,1);
-#endif	
 
 		/*
 		 *	Reassemble IP fragments.
diff -u --recursive --new-file v1.3.60/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v1.3.60/linux/net/ipv4/ip_output.c	Wed Feb  7 15:11:42 1996
+++ linux/net/ipv4/ip_output.c	Thu Feb  8 12:51:25 1996
@@ -715,7 +715,7 @@
 		}
 #endif
 #ifdef CONFIG_IP_ACCT
-		ip_fw_chk((void *)skb->data,dev,ip_acct_chain, IP_FW_F_ACCEPT,1);
+		ip_fw_chk(iph,dev,ip_acct_chain, IP_FW_F_ACCEPT,1);
 #endif		
 		if(dev->flags&IFF_UP)
 			dev_queue_xmit(skb,dev,sk->priority);
diff -u --recursive --new-file v1.3.60/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v1.3.60/linux/net/ipv4/ip_sockglue.c	Sun Jan 14 16:30:16 1996
+++ linux/net/ipv4/ip_sockglue.c	Thu Feb  8 12:51:25 1996
@@ -366,17 +366,26 @@
 		}
 #endif			
 #ifdef CONFIG_IP_FIREWALL
-		case IP_FW_ADD_BLK:
-		case IP_FW_DEL_BLK:
-		case IP_FW_ADD_FWD:
-		case IP_FW_DEL_FWD:
-		case IP_FW_CHK_BLK:
-		case IP_FW_CHK_FWD:
-		case IP_FW_FLUSH_BLK:
+		case IP_FW_INSERT_IN:
+		case IP_FW_INSERT_OUT:
+		case IP_FW_INSERT_FWD:
+		case IP_FW_APPEND_IN:
+		case IP_FW_APPEND_OUT:
+		case IP_FW_APPEND_FWD:
+		case IP_FW_DELETE_IN:
+		case IP_FW_DELETE_OUT:
+		case IP_FW_DELETE_FWD:
+		case IP_FW_CHECK_IN:
+		case IP_FW_CHECK_OUT:
+		case IP_FW_CHECK_FWD:
+		case IP_FW_FLUSH_IN:
+		case IP_FW_FLUSH_OUT:
 		case IP_FW_FLUSH_FWD:
-		case IP_FW_ZERO_BLK:
+		case IP_FW_ZERO_IN:
+		case IP_FW_ZERO_OUT:
 		case IP_FW_ZERO_FWD:
-		case IP_FW_POLICY_BLK:
+		case IP_FW_POLICY_IN:
+		case IP_FW_POLICY_OUT:
 		case IP_FW_POLICY_FWD:
 			if(!suser())
 				return -EPERM;
@@ -391,8 +400,9 @@
 			
 #endif
 #ifdef CONFIG_IP_ACCT
-		case IP_ACCT_DEL:
-		case IP_ACCT_ADD:
+		case IP_ACCT_INSERT:
+		case IP_ACCT_APPEND:
+		case IP_ACCT_DELETE:
 		case IP_ACCT_FLUSH:
 		case IP_ACCT_ZERO:
 			if(!suser())
diff -u --recursive --new-file v1.3.60/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v1.3.60/linux/net/ipv4/tcp.c	Wed Feb  7 15:11:43 1996
+++ linux/net/ipv4/tcp.c	Fri Feb  9 16:59:05 1996
@@ -1988,7 +1988,7 @@
 		 */
 		 
 			send_tmp = NULL;
-			if (copy < sk->mss && !(flags & MSG_OOB)) 
+			if (copy < sk->mss && !(flags & MSG_OOB) && sk->packets_out) 
 			{
 				/*
 				 *	We will release the socket in case we sleep here. 
@@ -2105,7 +2105,7 @@
 			skb->free = 0;
 			sk->write_seq += copy;
 		
-			if (send_tmp != NULL && sk->packets_out) 
+			if (send_tmp != NULL) 
 			{
 				tcp_enqueue_partial(send_tmp, sk);
 				continue;
@@ -2230,32 +2230,33 @@
 static void cleanup_rbuf(struct sock *sk)
 {
 	unsigned long flags;
-	unsigned long left;
 	struct sk_buff *skb;
 	unsigned long rspace;
 
-	if(sk->debug)
-	  	printk("cleaning rbuf for sk=%p\n", sk);
-  
 	save_flags(flags);
 	cli();
-  
-	left = sock_rspace(sk);
- 
+
+	/*
+	 * See if we have anything to free up?
+	 */
+
+	skb = skb_peek(&sk->receive_queue);
+	if (!skb || !skb->used || skb->users) {
+		restore_flags(flags);
+		return;
+	}
+
 	/*
 	 *	We have to loop through all the buffer headers,
 	 *	and try to free up all the space we can.
 	 */
 
-	while((skb=skb_peek(&sk->receive_queue)) != NULL) 
-	{
-		if (!skb->used || skb->users) 
-			break;
+	do {
 		skb_unlink(skb);
 		skb->sk = sk;
 		kfree_skb(skb, FREE_READ);
-	}
-
+		skb = skb_peek(&sk->receive_queue);
+	} while (skb && skb->used && !skb->users);
 	restore_flags(flags);
 
 	/*
@@ -2265,22 +2266,21 @@
 	 *	TCP_WINDOW_DIFF.
 	 */
 
+	rspace=sock_rspace(sk);
 	if(sk->debug)
-		printk("sk->rspace = %lu, was %lu\n", sock_rspace(sk),
-  					    left);
-	if ((rspace=sock_rspace(sk)) != left) 
-	{
-		/*
-		 * This area has caused the most trouble.  The current strategy
-		 * is to simply do nothing if the other end has room to send at
-		 * least 3 full packets, because the ack from those will auto-
-		 * matically update the window.  If the other end doesn't think
-		 * we have much space left, but we have room for at least 1 more
-		 * complete packet than it thinks we do, we will send an ack
-		 * immediately.  Otherwise we will wait up to .5 seconds in case
-		 * the user reads some more.
-		 */
-		sk->ack_backlog++;
+		printk("sk->rspace = %lu\n", rspace);
+	/*
+	 * This area has caused the most trouble.  The current strategy
+	 * is to simply do nothing if the other end has room to send at
+	 * least 3 full packets, because the ack from those will auto-
+	 * matically update the window.  If the other end doesn't think
+	 * we have much space left, but we have room for at least 1 more
+	 * complete packet than it thinks we do, we will send an ack
+	 * immediately.  Otherwise we will wait up to .5 seconds in case
+	 * the user reads some more.
+	 */
+	sk->ack_backlog++;
+
 	/*
 	 * It's unclear whether to use sk->mtu or sk->mss here.  They differ only
 	 * if the other end is offering a window smaller than the agreed on MSS
@@ -2290,22 +2290,21 @@
 	 * only on the send side, so I'm putting mtu here.
 	 */
 
-		if (rspace > (sk->window - sk->bytes_rcv + sk->mtu)) 
+	if (rspace > (sk->window - sk->bytes_rcv + sk->mtu)) 
+	{
+		/* Send an ack right now. */
+		tcp_read_wakeup(sk);
+	} 
+	else 
+	{
+		/* Force it to send an ack soon. */
+		int was_active = del_timer(&sk->retransmit_timer);
+		if (!was_active || jiffies+TCP_ACK_TIME < sk->timer.expires) 
 		{
-			/* Send an ack right now. */
-			tcp_read_wakeup(sk);
+			reset_xmit_timer(sk, TIME_WRITE, TCP_ACK_TIME);
 		} 
-		else 
-		{
-			/* Force it to send an ack soon. */
-			int was_active = del_timer(&sk->retransmit_timer);
-			if (!was_active || jiffies+TCP_ACK_TIME < sk->timer.expires) 
-			{
-				reset_xmit_timer(sk, TIME_WRITE, TCP_ACK_TIME);
-			} 
-			else
-				add_timer(&sk->retransmit_timer);
-		}
+		else
+			add_timer(&sk->retransmit_timer);
 	}
 } 
 
@@ -4180,7 +4179,7 @@
  *	room, then we will just have to discard the packet.
  */
 
-extern __inline__ int tcp_data(struct sk_buff *skb, struct sock *sk, 
+extern /* __inline__ */ int tcp_data(struct sk_buff *skb, struct sock *sk, 
 	 unsigned long saddr, unsigned short len)
 {
 	struct sk_buff *skb1, *skb2;
@@ -4798,44 +4797,14 @@
 	return(0);
 }
 
-
 /*
- *	This functions checks to see if the tcp header is actually acceptable. 
+ * React to a out-of-window TCP sequence number in an incoming packet
  */
- 
-extern __inline__ int tcp_sequence(struct sock *sk, struct tcphdr *th, short len,
+static void bad_tcp_sequence(struct sock *sk, struct tcphdr *th, short len,
 	     struct options *opt, unsigned long saddr, struct device *dev)
 {
-	u32 next_seq;
-
-	next_seq = len - 4*th->doff;
-	if (th->fin)
-		next_seq++;
-	/* if we have a zero window, we can't have any data in the packet.. */
-	if (next_seq && !sk->window)
-		goto ignore_it;
-	next_seq += ntohl(th->seq);
-
-	/*
-	 * This isn't quite right.  sk->acked_seq could be more recent
-	 * than sk->window.  This is however close enough.  We will accept
-	 * slightly more packets than we should, but it should not cause
-	 * problems unless someone is trying to forge packets.
-	 */
-
-	/* have we already seen all of this packet? */
-	if (!after(next_seq+1, sk->acked_seq))
-		goto ignore_it;
-	/* or does it start beyond the window? */
-	if (!before(ntohl(th->seq), sk->acked_seq + sk->window + 1))
-		goto ignore_it;
-
-	/* ok, at least part of this packet would seem interesting.. */
-	return 1;
-
-ignore_it:
 	if (th->rst)
-		return 0;
+		return;
 
 	/*
 	 *	Send a reset if we get something not ours and we are
@@ -4847,12 +4816,24 @@
 	if (sk->state==TCP_SYN_SENT || sk->state==TCP_SYN_RECV) 
 	{
 		tcp_reset(sk->saddr,sk->daddr,th,sk->prot,NULL,dev, sk->ip_tos,sk->ip_ttl);
-		return 1;
+		return;
 	}
 
 	/* Try to resync things. */
 	tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr);
-	return 0;
+	return;
+}
+
+/*
+ *	This functions checks to see if the tcp header is actually acceptable. 
+ */
+ 
+extern __inline__ int tcp_sequence(struct sock *sk, u32 seq, u32 end_seq)
+{
+	/* does the packet contain any unseen data AND */
+	/* does the packet start before the window? */
+	return	after(end_seq+1, sk->acked_seq) &&
+		before(seq, sk->acked_seq + sk->window + 1);
 }
 
 /*
@@ -4888,6 +4869,29 @@
 }
 
 /*
+ *	Find the socket, using the last hit cache if applicable.
+ */
+static inline struct sock * get_tcp_sock(u32 saddr, u16 sport, u32 daddr, u16 dport)
+{
+	struct sock * sk;
+
+	sk = (struct sock *) th_cache_sk;
+	if (saddr != th_cache_saddr || daddr != th_cache_daddr ||
+	    sport != th_cache_sport || dport != th_cache_dport) {
+		sk = get_sock(&tcp_prot, dport, saddr, sport, daddr);
+		if (sk) {
+			th_cache_saddr=saddr;
+			th_cache_daddr=daddr;
+  			th_cache_dport=dport;
+			th_cache_sport=sport;
+			th_cache_sk=sk;
+		}
+	}
+	return sk;
+}
+
+
+/*
  *	A TCP packet has arrived.
  *		skb->h.raw is the TCP header.
  */
@@ -4900,52 +4904,21 @@
 	struct sock *sk;
 	int syn_ok=0;
 
-	tcp_statistics.TcpInSegs++;
-	if(skb->pkt_type!=PACKET_HOST)
-	{
-	  	kfree_skb(skb,FREE_READ);
-	  	return(0);
-	}
-  
-	th = skb->h.th;
-
 	/*
-	 *	Find the socket, using the last hit cache if applicable.
+	 * "redo" is 1 if we have already seen this skb but couldn't
+	 * use it at that time (the socket was locked).  In that case
+	 * we have already done a lot of the work (looked up the socket
+	 * etc).
 	 */
-
-	if(!redo && saddr==th_cache_saddr && daddr==th_cache_daddr && th->dest==th_cache_dport && th->source==th_cache_sport)
-	{
-		sk=(struct sock *)th_cache_sk;
-		/*
-		 *	We think this is causing the bug so
-		 */
-		 if(sk!=get_sock(&tcp_prot,th->dest, saddr, th->source, daddr))
-		 	printk("Cache mismatch on TCP.\n");
-	}
-	else
-	{
-		sk = get_sock(&tcp_prot, th->dest, saddr, th->source, daddr);
-  		th_cache_saddr=saddr;
-  		th_cache_daddr=daddr;
-  		th_cache_dport=th->dest;
-  		th_cache_sport=th->source;
-  		th_cache_sk=sk;
-	}		
-
-	/*
-	 *	If this socket has got a reset it's to all intents and purposes 
-  	 *	really dead. Count closed sockets as dead.
-  	 *
-  	 *	Note: BSD appears to have a bug here. A 'closed' TCP in BSD
-  	 *	simply drops data. This seems incorrect as a 'closed' TCP doesn't
-  	 *	exist so should cause resets as if the port was unreachable.
-  	 */
-  	 
-	if (sk!=NULL && (sk->zapped || sk->state==TCP_CLOSE))
-		sk=NULL;
-
-	if (!redo) 
-	{
+	th = skb->h.th;
+	sk = skb->sk;
+	if (!redo) {
+		tcp_statistics.TcpInSegs++;
+		if (skb->pkt_type!=PACKET_HOST)
+		{
+		  	kfree_skb(skb,FREE_READ);
+		  	return(0);
+		}
 		/*
 		 *	Pull up the IP header.
 		 */
@@ -4954,8 +4927,9 @@
 		 *	Try to use the device checksum if provided.
 		 */
 		if (
-			(skb->ip_summed && tcp_check(th, len, saddr, daddr, skb->csum ))||
-		    	(!skb->ip_summed && tcp_check(th, len, saddr, daddr, csum_partial((char *)th, len, 0)))
+			((skb->ip_summed == CHECKSUM_HW) && tcp_check(th, len, saddr, daddr, skb->csum ))||
+		    	((skb->ip_summed == CHECKSUM_NONE) && tcp_check(th, len, saddr, daddr, csum_partial((char *)th, len, 0)))
+		    /* skip if CHECKSUM_UNNECESSARY */
 		    )
 		{
 			skb->sk = NULL;
@@ -4966,26 +4940,14 @@
 			 */
 			return(0);
 		}
-
+		sk = get_tcp_sock(saddr, th->source, daddr, th->dest);
+		if (!sk)
+			goto no_tcp_socket;
+		skb->sk = sk;
 		skb->seq = ntohl(th->seq);
 		skb->end_seq = skb->seq + th->syn + th->fin + len - th->doff*4;
 		skb->ack_seq = ntohl(th->ack_seq);
 
-		/* See if we know about the socket. */
-		if (sk == NULL) 
-		{
-			/*
-			 *	No such TCB. If th->rst is 0 send a reset (checked in tcp_reset)
-			 */
-			tcp_reset(daddr, saddr, th, &tcp_prot, opt,dev,skb->ip_hdr->tos,255);
-			skb->sk = NULL;
-			/*
-			 *	Discard frame
-			 */
-			kfree_skb(skb, FREE_READ);
-			return(0);
-		}
-
 		skb->acked = 0;
 		skb->used = 0;
 		skb->free = 0;
@@ -5003,17 +4965,18 @@
 		sk->inuse = 1;
 		sti();
 	}
-	else
-	{
-		if (sk==NULL) 
-		{
-			tcp_reset(daddr, saddr, th, &tcp_prot, opt,dev,skb->ip_hdr->tos,255);
-			skb->sk = NULL;
-			kfree_skb(skb, FREE_READ);
-			return(0);
-		}
-	}
 
+	/*
+	 *	If this socket has got a reset it's to all intents and purposes 
+	 *	really dead. Count closed sockets as dead.
+	 *
+	 *	Note: BSD appears to have a bug here. A 'closed' TCP in BSD
+	 *	simply drops data. This seems incorrect as a 'closed' TCP doesn't
+	 *	exist so should cause resets as if the port was unreachable.
+	 */
+
+	if (sk->zapped || sk->state==TCP_CLOSE)
+		goto no_tcp_socket;
 
 	if (!sk->prot) 
 	{
@@ -5223,8 +5186,9 @@
 	 *	I have time to test it hard and look at what gcc outputs 
 	 */
 	
-	if(!tcp_sequence(sk,th,len,opt,saddr,dev))
+	if (!tcp_sequence(sk, skb->seq, skb->end_seq))
 	{
+		bad_tcp_sequence(sk, th, len, opt, saddr, dev);
 		kfree_skb(skb, FREE_READ);
 		release_sock(sk);
 		return 0;
@@ -5343,6 +5307,18 @@
 	 */	
 	
 	release_sock(sk);
+	return 0;
+
+no_tcp_socket:
+	/*
+	 *	No such TCB. If th->rst is 0 send a reset (checked in tcp_reset)
+	 */
+	tcp_reset(daddr, saddr, th, &tcp_prot, opt,dev,skb->ip_hdr->tos,255);
+	skb->sk = NULL;
+	/*
+	 *	Discard frame
+	 */
+	kfree_skb(skb, FREE_READ);
 	return 0;
 }
 
diff -u --recursive --new-file v1.3.60/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v1.3.60/linux/net/ipv4/udp.c	Wed Feb  7 15:11:43 1996
+++ linux/net/ipv4/udp.c	Fri Feb  9 16:59:06 1996
@@ -633,8 +633,9 @@
 	/* (That's the Right Place to do it, IMHO.) -- MS */
 
 	if (uh->check && (
-		( skb->ip_summed && udp_check(uh, len, saddr, daddr, skb->csum ) ) ||
-		( !skb->ip_summed && udp_check(uh, len, saddr, daddr,csum_partial((char*)uh, len, 0)))
+		( (skb->ip_summed == CHECKSUM_HW) && udp_check(uh, len, saddr, daddr, skb->csum ) ) ||
+		( (skb->ip_summed == CHECKSUM_NONE) && udp_check(uh, len, saddr, daddr,csum_partial((char*)uh, len, 0)))
+			  /* skip if CHECKSUM_UNNECESSARY */
 		         )
 	   )
 	{
diff -u --recursive --new-file v1.3.60/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v1.3.60/linux/net/ipx/af_ipx.c	Wed Feb  7 15:11:44 1996
+++ linux/net/ipx/af_ipx.c	Fri Feb  9 16:59:06 1996
@@ -2162,7 +2162,7 @@
 	ipx_recvmsg
 };
 
-/* Called by ddi.c on kernel start up */
+/* Called by protocol.c on kernel start up */
 
 static struct packet_type ipx_8023_packet_type = 
 
diff -u --recursive --new-file v1.3.60/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v1.3.60/linux/net/unix/af_unix.c	Wed Feb  7 15:11:44 1996
+++ linux/net/unix/af_unix.c	Thu Feb  8 05:02:02 1996
@@ -771,6 +771,7 @@
 				sk->protinfo.af_unix.other=NULL;
 				sock->state=SS_UNCONNECTED;
 				sti();
+				kfree_skb(skb, FREE_WRITE);
 				if(!sent)
 					return -ECONNRESET;
 				else
@@ -783,8 +784,8 @@
 			other=unix_find_other(sunaddr->sun_path, &err);
 			if(other==NULL)
 			{
-				kfree_skb(skb, FREE_WRITE);
 				sti();
+				kfree_skb(skb, FREE_WRITE);
 				if(sent)
 					return sent;
 				else
diff -u --recursive --new-file v1.3.60/linux/scripts/Configure linux/scripts/Configure
--- v1.3.60/linux/scripts/Configure	Wed Feb  7 15:11:44 1996
+++ linux/scripts/Configure	Thu Feb  8 05:05:29 1996
@@ -211,7 +211,7 @@
 	if [ "$3" != "m" ]; then
 		tristate "$1" "$2"
 	else
-           if [ "$MODULES" = "y" ]; then
+           if [ "$CONFIG_MODULES" = "y" ]; then
 	        case "$def" in
         	 "y" | "m") defprompt="M/n/?"
 		      def="m"
diff -u --recursive --new-file v1.3.60/linux/scripts/lxdialog/Makefile linux/scripts/lxdialog/Makefile
--- v1.3.60/linux/scripts/lxdialog/Makefile	Wed Feb  7 15:11:46 1996
+++ linux/scripts/lxdialog/Makefile	Fri Feb  9 16:59:06 1996
@@ -12,8 +12,13 @@
         CFLAGS += -I/usr/include/ncurses
         CURSES =  -DCURSES_LOC="<ncurses.h>"
 else
+ifeq (/usr/include/ncurses/curses.h, $(wildcard /usr/include/ncurses/curses.h))
+        CFLAGS += -I/usr/include/ncurses
+        CURSES =  -DCURSES_LOC="<curses.h>"
+else
 ifeq (/usr/include/ncurses.h, $(wildcard /usr/include/ncurses.h))
         CURSES =  -DCURSES_LOC="<ncurses.h>"
+endif
 endif
 endif
 
diff -u --recursive --new-file v1.3.60/linux/scripts/lxdialog/dialog.h linux/scripts/lxdialog/dialog.h
--- v1.3.60/linux/scripts/lxdialog/dialog.h	Wed Feb  7 15:11:46 1996
+++ linux/scripts/lxdialog/dialog.h	Fri Feb  9 16:59:06 1996
@@ -1,3 +1,4 @@
+
 /*
  *  dialog.h -- common declarations for all dialog modules
  *
@@ -26,7 +27,6 @@
 #include <string.h>
 
 #include CURSES_LOC
-
 
 #define ESC 27
 #define TAB 9
diff -u --recursive --new-file v1.3.60/linux/scripts/tkgen.c linux/scripts/tkgen.c
--- v1.3.60/linux/scripts/tkgen.c	Wed Feb  7 15:11:46 1996
+++ linux/scripts/tkgen.c	Fri Feb  9 16:59:06 1996
@@ -432,7 +432,11 @@
   printf("\tglobal winx; global winy\n");
   printf("\tset winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30]\n");
   printf("\twm geometry $w +$winx+$winy\n");
-  printf("\twm resizable $w no yes\n\n");
+  /*
+   *	We have a cunning plan....
+   */
+  if(access("/usr/lib/tk4.0",0)==0)
+	  printf("\twm resizable $w no yes\n\n");
   
   /*
    * Now that the whole window is in place, we need to wait for an "update"
