diff -u --recursive --new-file v2.1.48/linux/CREDITS linux/CREDITS
--- v2.1.48/linux/CREDITS	Mon Aug  4 16:25:35 1997
+++ linux/CREDITS	Tue Aug  5 09:48:55 1997
@@ -1274,7 +1274,7 @@
 N: Thomas Sailer
 E: sailer@ife.ee.ethz.ch
 E: HB9JNX@HB9W.CHE.EU (packet radio)
-D: Baycom and Soundcard radio modem driver
+D: hfmodem, Baycom and Soundcard radio modem driver
 S: Weinbergstrasse 76
 S: 8408 Winterthur
 S: Switzerland
diff -u --recursive --new-file v2.1.48/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.48/linux/Documentation/Configure.help	Mon Aug  4 16:25:35 1997
+++ linux/Documentation/Configure.help	Wed Aug  6 13:02:58 1997
@@ -1967,20 +1967,44 @@
   possible.  The default value is 4. Minimum is 2, maximum is 12.  The
   normal answer therefore is the default one.
 
+detect and read serial NVRAM
+CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
+  Enable support for reading the serial NVRAM data on Symbios and
+  some Symbios compatible cards, and Tekram DC390W/U/F cards. Useful for
+  systems with more than one Symbios compatible controller where at least
+  one has a serial NVRAM, or for a system with a mixture of Symbios and
+  Tekram cards. Enables setting the boot order of host adaptors
+  to something other than the default order or "reverse probe" order. 
+  Also enables Symbios and Tekram cards to be distinguished so
+  CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT may be set in a system with a
+  mixture of Symbios and Tekram cards so the Symbios cards can make use of
+  the full range of Symbios features, differential, led pin, without
+  causing problems for the Tekram card(s).
+      (added by Richard Waltham: dormouse@farsrobt.demon.co.uk)
+  Also enables setting host and targets SCSI features as defined in the 
+  user setup for each host using a serial NVRAM (added by the maintainer).
+  The default answer is N, the normal answer should be Y.
+  Read drivers/scsi/README.ncr53c8xx for more information.
+
 assume boards are SYMBIOS compatible
 CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
-  This option allows you to enable some features depending on GPIO wiring.
-  These general purpose input/output pins can be used for vendor specific 
-  features or implementation of the standard SYMBIOS features.
-  Genuine SYMBIOS boards use GPIO0 in output for controller LED and GPIO3 
-  bit as a flag indicating singled-ended/differential interface.
+  This option allows you to enable some features depending on GPIO
+  wiring.  These General Purpose Input/Output pins can be used for
+  vendor specific features or implementation of the standard SYMBIOS
+  features.  Genuine SYMBIOS boards use GPIO0 in output for controller
+  LED and GPIO3 bit as a flag indicating singled-ended/differential
+  interface.
   If all the boards of your system are genuine SYMBIOS boards or use
-  BIOS and drivers from SYMBIOS, you would want to enable this option,
-  obviously at your own risks.
+  BIOS and drivers from SYMBIOS, you would want to enable this option.
   The driver behaves correctly on my system with this option enabled.
-  (SDMS 4.0 + Promise SCSI ULTRA 875 rev 0x3 + ASUS SC200 810A rev 0x12).
-  This option must be set to N if your system has at least one 53C8XX based  
-  scsi board with a vendor-specific BIOS (example: Tekram DC-390/U/W/F).
+  (SDMS 4.0 + Promise SCSI ULTRA 875 rev 0x3 + ASUS SC200 810A rev
+  0x12).  This option must be set to N if your system has at least one
+  53C8XX based scsi board with a vendor-specific BIOS (example: Tekram
+  DC-390/U/W/F).  If unsure, say N.
+  However, if all your non Symbios compatible boards have NvRAM, setting 
+  option CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT allows the driver to 
+  distinguish Symbios compatible boards from other ones.
+  So, you can answer Y if all non Symbios compatible boards have NVRAM.
 
 assume boards are SYMBIOS compatible
 CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT
@@ -2518,6 +2542,26 @@
   both 1200 baud AFSK and 9600 baud FSK if you want (but obviously you
   can only use one protocol at a time, depending on what the other end
   can understand).
+
+Shortwave radio modem driver
+CONFIG_HFMODEM
+  This experimental driver is used by a package (to be released)
+  that implements the shortwave radio protocols RTTY, Sitor (Amtor),
+  Pactor 1 and GTOR using a standard PC soundcard. If unsure,
+  say N.
+
+Shortwave radio modem driver support for SoundBlaster and compatible cards
+CONFIG_HFMODEM_SBC
+  This option enables the hfmodem driver to use SoundBlaster and
+  compatible cards. It requires a 16bit capable card, i.e.
+  SB16 or better, or ESS1688 or newer.
+
+Shortwave radio modem driver support for WSS and Crystal cards
+CONFIG_HFMODEM_WSS
+  This option enables the hfmodem driver to use WindowsSoundSystem
+  compatible cards. These cards feature a codec chip from either
+  Analog Devices (such as AD1848, AD1845) or Crystal
+  Semiconductors (such as CS4248, CS423x).
 
 Serial port KISS driver for AX.25
 CONFIG_MKISS
diff -u --recursive --new-file v2.1.48/linux/Documentation/ioctl-number.txt linux/Documentation/ioctl-number.txt
--- v2.1.48/linux/Documentation/ioctl-number.txt	Mon Aug  4 16:25:35 1997
+++ linux/Documentation/ioctl-number.txt	Thu Aug  7 09:37:04 1997
@@ -1,5 +1,5 @@
 Ioctl Numbers
-25 Jul 1997
+7 Aug 1997
 Michael Chastain
 <mec@shout.net>
 
@@ -119,5 +119,7 @@
 					<mailto:b.kohl@ipn-b.comlink.apc.org>
 0xA0	all	Small Device Project	in development:
 					<mailto:khollis@northwest.com>
+0xA3	80-8F	Port ACL		in development:
+					<mailto:tlewis@mindspring.com>
 0xA3	90-9F	DoubleTalk driver	in development:
 					<mailto:jrv@vanzandt.mv.com>
diff -u --recursive --new-file v2.1.48/linux/Documentation/networking/baycom.txt linux/Documentation/networking/baycom.txt
--- v2.1.48/linux/Documentation/networking/baycom.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/networking/baycom.txt	Tue Aug  5 09:49:50 1997
@@ -0,0 +1,118 @@
+		    LINUX DRIVER FOR BAYCOM MODEMS
+
+       Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
+
+This document describes the Linux Kernel Driver for simple Baycom style
+amateur radio modems. The driver supports the following modems:
+
+ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
+        of a modulator/demodulator chip, usually a TI TCM3105. The computer
+        is responsible for regenerating the receiver bit clock, as well as
+        for handling the HDLC protocol. The modem connects to a serial port,
+        hence the name. Since the serial port is not used as an async serial
+        port, the kernel driver for serial ports cannot be used, and this
+        driver only supports standard serial hardware (8250, 16450, 16550)
+
+par96:  This is a modem for 9600 baud FSK compatible to the G3RUH standard.
+        The modem does all the filtering and regenerates the receiver clock.
+        Data is transferred from and to the PC via a shift register.
+        The shift register is filled with 16 bits and an interrupt is signalled.
+        The PC then empties the shift register in a burst. This modem connects
+        to the parallel port, hence the name. The modem leaves the 
+        implementation of the HDLC protocol and the scrambler polynomial to
+        the PC.
+
+picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem
+        is protocol compatible to par96, but uses only three low power ICs
+        and can therefore be fed from the parallel port and does not require
+        an additional power supply. Furthermore, it incorporates a carrier
+        detect circuitry.
+
+All of the above modems only support half duplex communications. However,
+the driver supports the KISS (see below) fullduplex command. It then simply
+starts to send as soon as there's a packet to transmit and does not care
+about DCD, i.e. it starts to send even if there's someone else on the channel.
+This command is required by some implementations of the DAMA channel 
+access protocol.
+
+
+The Interface of the driver
+
+Unlike previous drivers, the driver is no longer a character device,
+but it is now a true kernel network interface. Installation is therefore
+simple. Once installed, four interfaces named bc[0-3] are available.
+sethdlc from the ax25 utilities may be used to set driver states etc.
+Users of userland AX.25 stacks may use the net2kiss utility (also available
+in the ax25 utilities package) to converts packets of a network interface
+to a KISS stream on a pseudo tty. There's also a patch available from
+me for WAMPES which allows attaching a kernel network interface directly.
+
+
+Configuring the driver
+
+Every time the driver is inserted into the kernel, it has to know which
+modems it should access at which ports. This can be done with the setbaycom
+utility. If you are only using one modem, you can also configure the
+driver from the insmod command line (or by means of an option line in
+/etc/conf.modules).
+
+Examples:
+  insmod baycom modem=1 iobase=0x3f8 irq=4 options=1
+  sethdlc -i bc0 -p type ser12 io 0x3f8 irq 4 options 1
+
+Both lines configure the first port to drive a ser12 modem at the first
+serial port (COM1 under DOS). options=1 instructs the driver to use
+the software DCD algorithm (see below).
+
+  insmod baycom modem=2 iobase=0x378 irq=7 options=1
+  sethdlc -i bc0 -p type par96 io 0x378 irq 7 options 1
+
+Both lines configure the first port to drive a par96 or par97 modem at the
+first parallel port (LPT1 under DOS). options=1 instructs the driver to use
+the software DCD algorithm (see below).
+  
+The channel access parameters can be set with sethdlc -a or kissparms.
+Note that both utilities interpret the values slightly different.
+
+
+Hardware DCD versus Software DCD
+
+To avoid collisions on the air, the driver must know when the channel is
+busy. This is the task of the DCD circuitry/software. The driver may either
+utilise a software DCD algorithm (options=1) or use a DCD signal from
+the hardware (options=0).
+
+ser12:  if software DCD is utilised, the radio's squelch should always be
+        open. It is highly recommended to use the software DCD algorithm,
+        as it is much faster than most hardware squelch circuitry. The
+        disadvantage is a slightly higher load on the system.
+
+par96:  the software DCD algorithm for this type of modem is rather poor.
+        The modem simply does not provide enough information to implement
+        a reasonable DCD algorithm in software. Therefore, if your radio
+        feeds the DCD input of the PAR96 modem, the use of the hardware
+        DCD circuitry is recommended.
+
+picpar: the picpar modem features a builtin DCD hardware, which is highly
+        recommended.
+
+
+
+Compatibility with the rest of the Linux kernel
+
+The serial driver, the line printer (lp) driver and the baycom driver compete
+for the same hardware resources. Of course only one driver can access a given
+interface at a time. The serial driver grabs all interfaces it can find at
+startup time. Therefore the baycom driver subsequently won't be able to
+access a serial port. You might therefore find it necessary to release
+a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where
+# is the number of the interface. The baycom driver does not reserve any
+port at startup, unless one is specified on the 'insmod' command line. Another
+method to solve the problem is to compile all three drivers as modules and
+leave it to kerneld to load the correct driver depending on the application.
+
+
+
+vy 73s de
+Tom Sailer, sailer@ife.ee.ethz.ch
+hb9jnx @ hb9w.ampr.org
diff -u --recursive --new-file v2.1.48/linux/Documentation/networking/soundmodem.txt linux/Documentation/networking/soundmodem.txt
--- v2.1.48/linux/Documentation/networking/soundmodem.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/networking/soundmodem.txt	Tue Aug  5 09:49:50 1997
@@ -0,0 +1,88 @@
+	     LINUX DRIVER FOR SOUNDCARDS AS AX.25 MODEMS
+
+       Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
+
+This driver allows either SoundBlaster (sbc) or WindowsSoundSystem (wss)
+compatible soundcards to be used as either 1200 baud AFSK or 9600 baud FSK
+AX.25 packet radio modems. Only half duplex operation is supported; an
+attempt to include full duplex support failed because the hardware did
+not support it (it appeared that the card only provides one DMA channel,
+although the Codec chip would support two channels). The driver needs
+some processing power! A 486DX/2 66MHz is a minimum requirement, otherwise
+interactive performance of the computer may become sluggish.
+
+
+The Interface of the driver
+
+The driver provides a kernel network drivers named sm[0-3]. sethdlc 
+from the ax25 utilities may be used to set driver states etc. Users
+of userland AX.25 stacks may use the net2kiss utility (also available
+in the ax25 utilities package) to converts packets of a network interface
+to a KISS stream on a pseudo tty. There's also a patch available from
+me for WAMPES which allows attaching a kernel network interface directly.
+
+
+Configuring the driver
+
+Some sound cards need to be initialized before they operate in either
+SoundBlaster or WSS compatibility mode. The driver does _NOT_ do this;
+you may use the standard linux sound driver to initialize the soundcard;
+compile it as a module, and do
+  insmod sound
+  rmmod sound
+The soundcard should then be initialized correctly. If this does not help,
+you'll have to write your own initialization utility.
+
+Every time the driver is inserted into the kernel, it has to know which
+modems it should access at which ports. This can be done with the setbaycom
+utility. If you are only using one modem, you can also configure the
+driver from the insmod command line (or by means of an option line in
+/etc/conf.modules).
+
+Examples:
+  insmod soundmodem hw=0 mode=0 iobase=0x220 irq=5 dma=1
+  sethdlc -i sm0 -p hw sbc type afsk1200 io 0x220 irq 5 dma 1
+
+Both lines configure the first port to drive a soundblaster card
+in 1200 baud AFSK mode.
+
+The channel access parameters can be set with sethdlc -a or kissparms.
+Note that both utilities interpret the values slightly different.
+
+
+Input and output levels
+
+It is important that the input and output levels are adjusted properly.
+There are two utilities, available in the ax25 utilities distribution,
+to facilitate this: smmixer and smdiag. smdiag allows you to display
+the input signal in an oscilloscope like display or an eye diagram.
+smmixer allows you to adjust input/output levels. See the respective
+man pages.
+
+
+Transmitter keying
+
+Since soundcards do not have a DC coupled output; PTT keying options include
+the following:
+* VOX circuitry
+* Serial port pin
+* Parallel port pin
+* MPU401 MIDI output via a retriggerable monoflop.
+Circuit schematics may be found at
+http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html.
+
+
+Compatibility with the rest of the Linux kernel
+
+The sound driver and the soundcard modem driver compete for the same
+hardware resources. Of course only one driver can access a given
+interface at a time. Worse yet, the sound driver grabs the soundcard
+at startup time. Therefore the soundcard modem driver subsequently won't
+be able to access the soundcard. You might therefore find it necessary to
+unload the sound driver before using the soundcard modem driver.
+
+
+
+vy 73s de
+Tom Sailer, sailer@ife.ee.ethz.ch
+hb9jnx @ hb9w.ampr.org
diff -u --recursive --new-file v2.1.48/linux/Makefile linux/Makefile
--- v2.1.48/linux/Makefile	Mon Aug  4 16:25:35 1997
+++ linux/Makefile	Wed Aug  6 15:18:17 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 48
+SUBLEVEL = 49
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
@@ -338,7 +338,9 @@
 	rm -f drivers/char/uni_hash.tbl drivers/char/conmakehash
 	rm -f drivers/net/soundmodem/sm_tbl_{afsk1200,afsk2666,fsk9600}.h
 	rm -f drivers/net/soundmodem/sm_tbl_{hapn4800,psk4800}.h
+	rm -f drivers/net/soundmodem/sm_tbl_{afsk2400_7,afsk2400_8}.h
 	rm -f drivers/net/soundmodem/gentbl
+	rm -f drivers/char/hfmodem/gentbl drivers/char/hfmodem/tables.h
 	rm -f .version .config* config.in config.old
 	rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp
 	rm -f scripts/lxdialog/*.o scripts/lxdialog/lxdialog
diff -u --recursive --new-file v2.1.48/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- v2.1.48/linux/arch/i386/kernel/head.S	Mon Jun 16 16:35:53 1997
+++ linux/arch/i386/kernel/head.S	Tue Aug  5 09:21:29 1997
@@ -312,15 +312,16 @@
 	pushl %eax
 	pushl %ecx
 	pushl %edx
+	push %es
 	push %ds
 	movl $(KERNEL_DS),%eax
 	mov %ax,%ds
 	mov %ax,%es
-	mov %ax,%fs
 	pushl $int_msg
 	call SYMBOL_NAME(printk)
 	popl %eax
 	pop %ds
+	pop %es
 	popl %edx
 	popl %ecx
 	popl %eax
diff -u --recursive --new-file v2.1.48/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.1.48/linux/drivers/char/Makefile	Mon Aug  4 16:25:37 1997
+++ linux/drivers/char/Makefile	Tue Aug  5 09:48:55 1997
@@ -311,6 +311,17 @@
   endif
 endif
 
+ifeq ($(CONFIG_HFMODEM),y)
+ALL_SUB_DIRS += hfmodem
+SUB_DIRS += hfmodem
+L_OBJS += hfmodem/hfmodem.o
+else
+  ifeq ($(CONFIG_HFMODEM),m)
+  ALL_SUB_DIRS += hfmodem
+  MOD_SUB_DIRS += hfmodem
+  endif
+endif
+
 include $(TOPDIR)/Rules.make
 
 fastdep: uni_hash.tbl
diff -u --recursive --new-file v2.1.48/linux/drivers/char/hfmodem/Makefile linux/drivers/char/hfmodem/Makefile
--- v2.1.48/linux/drivers/char/hfmodem/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/hfmodem/Makefile	Tue Aug  5 09:48:55 1997
@@ -0,0 +1,37 @@
+#
+# Makefile for the hfmodem device driver.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+
+O_TARGET	:= hfmodem.o
+
+O_OBJS		:= refclock.o modem.o main.o
+ifeq ($(CONFIG_HFMODEM_SBC),y)
+O_OBJS		+= sbc.o
+endif
+ifeq ($(CONFIG_HFMODEM_WSS),y)
+O_OBJS		+= wss.o
+endif
+
+M_OBJS		:= $(O_TARGET)
+
+all:		all_targets
+.PHONY:		all
+
+gentbl:		gentbl.c
+		$(HOSTCC) -Wall $< -o $@ -lm
+
+TBLHDR		:= tables.h
+
+tables.h:	gentbl
+		./gentbl > $@
+
+fastdep:	$(TBLHDR)
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.48/linux/drivers/char/hfmodem/gentbl.c linux/drivers/char/hfmodem/gentbl.c
--- v2.1.48/linux/drivers/char/hfmodem/gentbl.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/hfmodem/gentbl.c	Tue Aug  5 09:48:55 1997
@@ -0,0 +1,69 @@
+/*****************************************************************************/
+
+/*
+ *      gentbl.c  --  Linux soundcard HF FSK driver,
+ *                    Table generator.
+ *
+ *      Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *        Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+/*****************************************************************************/
+      
+#include <linux/hfmodem.h>
+#include <math.h>
+#include <stdio.h>
+
+/* --------------------------------------------------------------------- */
+
+#define SINTABBITS  9
+#define SINTABSIZE  (1<<SINTABBITS)
+
+/* --------------------------------------------------------------------- */
+
+static void gensintbl(void)
+{
+	int i;
+
+	printf("#define SINTABBITS %d\n#define SINTABSIZE  (1<<SINTABBITS)\n"
+	       "\nstatic short isintab[SINTABSIZE+SINTABSIZE/4] = {\n\t", SINTABBITS);
+	for (i = 0; i < (SINTABSIZE+SINTABSIZE/4); i++) {
+		printf("%6d", (int)(32767.0 * sin(2.0 * M_PI / SINTABSIZE * i)));
+		if (i < (SINTABSIZE+SINTABSIZE/4)-1) {
+			if ((i & 7) == 7)
+				printf(",\n\t");
+			else
+				printf(",");
+		}
+	}
+	printf("\n};\n\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+int main(int argc, char *argv[])
+{
+	printf("/*\n * This file is automatically generated by %s, DO NOT EDIT!\n*/\n\n",
+	       argv[0]);
+	gensintbl();
+	exit(0);
+}
+
+/* --------------------------------------------------------------------- */
+
diff -u --recursive --new-file v2.1.48/linux/drivers/char/hfmodem/main.c linux/drivers/char/hfmodem/main.c
--- v2.1.48/linux/drivers/char/hfmodem/main.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/hfmodem/main.c	Tue Aug  5 09:48:55 1997
@@ -0,0 +1,736 @@
+/*****************************************************************************/
+
+/*
+ *      main.c  --  Linux soundcard HF FSK driver.
+ *
+ *      Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *        Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Command line options (insmod command line)
+ *
+ *  History:
+ *   0.1  15.04.97  Adapted from baycom.c and made network driver interface
+ *   0.2  05.07.97  All floating point stuff thrown out due to Linus' rantings :)
+ *
+ */
+
+/*****************************************************************************/
+      
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/ioport.h>
+#include <linux/hfmodem.h>
+
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*static*/ const char hfmodem_drvname[] = "hfmodem";
+static const char hfmodem_drvinfo[] = KERN_INFO "hfmodem: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "hfmodem: version 0.2 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+/*
+ * currently we support only one device
+ */
+
+struct hfmodem_state hfmodem_state[NR_DEVICE];
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== port checking routines ========================
+ */
+
+
+#define UART_RBR(iobase) (iobase+0)
+#define UART_THR(iobase) (iobase+0)
+#define UART_IER(iobase) (iobase+1)
+#define UART_IIR(iobase) (iobase+2)
+#define UART_FCR(iobase) (iobase+2)
+#define UART_LCR(iobase) (iobase+3)
+#define UART_MCR(iobase) (iobase+4)
+#define UART_LSR(iobase) (iobase+5)
+#define UART_MSR(iobase) (iobase+6)
+#define UART_SCR(iobase) (iobase+7)
+#define UART_DLL(iobase) (iobase+0)
+#define UART_DLM(iobase) (iobase+1)
+
+#define SER_EXTENT 8
+
+#define LPT_DATA(iobase)    (iobase+0)
+#define LPT_STATUS(iobase)  (iobase+1)
+#define LPT_CONTROL(iobase) (iobase+2)
+#define LPT_IRQ_ENABLE      0x10
+
+#define LPT_EXTENT 3
+
+#define MIDI_DATA(iobase)     (iobase)
+#define MIDI_STATUS(iobase)   (iobase+1)
+#define MIDI_READ_FULL 0x80   /* attention: negative logic!! */
+#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */
+
+#define MIDI_EXTENT 2
+
+#define SP_SER  1
+#define SP_PAR  2
+#define SP_MIDI 4
+
+/* ---------------------------------------------------------------------- */
+/*
+ * returns 0 if ok and != 0 on error;
+ * the same behaviour as par96_check_lpt in baycom.c
+ */
+
+__initfunc(static int check_lpt(unsigned int iobase))
+{
+        unsigned char b1,b2;
+        int i;
+
+        if (iobase <= 0 || iobase > 0x1000-LPT_EXTENT)
+                return 0;
+        if (check_region(iobase, LPT_EXTENT))
+                return 0;
+        b1 = inb(LPT_DATA(iobase));
+        b2 = inb(LPT_CONTROL(iobase));
+        outb(0xaa, LPT_DATA(iobase));
+        i = inb(LPT_DATA(iobase)) == 0xaa;
+        outb(0x55, LPT_DATA(iobase));
+        i &= inb(LPT_DATA(iobase)) == 0x55;
+        outb(0x0a, LPT_CONTROL(iobase));
+        i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
+        outb(0x05, LPT_CONTROL(iobase));
+        i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
+        outb(b1, LPT_DATA(iobase));
+        outb(b2, LPT_CONTROL(iobase));
+        return !i;
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250, c_uart_16450, c_uart_16550, c_uart_16550A };
+static const char *uart_str[] __initdata = { "unknown", "8250", "16450", "16550", "16550A" };
+
+__initfunc(static enum uart check_uart(unsigned int iobase))
+{
+        unsigned char b1,b2,b3;
+        enum uart u;
+        enum uart uart_tab[] = { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+        if (iobase <= 0 || iobase > 0x1000-SER_EXTENT)
+                return c_uart_unknown;
+        if (check_region(iobase, SER_EXTENT))
+                return c_uart_unknown;
+        b1 = inb(UART_MCR(iobase));
+        outb(b1 | 0x10, UART_MCR(iobase));      /* loopback mode */
+        b2 = inb(UART_MSR(iobase));
+        outb(0x1a, UART_MCR(iobase));
+        b3 = inb(UART_MSR(iobase)) & 0xf0;
+        outb(b1, UART_MCR(iobase));        /* restore old values */
+        outb(b2, UART_MSR(iobase));
+        if (b3 != 0x90)
+                return c_uart_unknown;
+        inb(UART_RBR(iobase));
+        inb(UART_RBR(iobase));
+        outb(0x01, UART_FCR(iobase));           /* enable FIFOs */
+        u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3];
+        if (u == c_uart_16450) {
+                outb(0x5a, UART_SCR(iobase));
+                b1 = inb(UART_SCR(iobase));
+                outb(0xa5, UART_SCR(iobase));
+                b2 = inb(UART_SCR(iobase));
+                if ((b1 != 0x5a) || (b2 != 0xa5))
+                        u = c_uart_8250;
+        }
+        return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(static int check_midi(unsigned int iobase))
+{
+        unsigned long timeout;
+        unsigned long flags;
+        unsigned char b;
+
+        if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT)
+                return 0;
+        if (check_region(iobase, MIDI_EXTENT))
+                return 0;
+        timeout = jiffies + (HZ / 100);
+        while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+                if ((signed)(jiffies - timeout) > 0)
+                        return 0;
+        save_flags(flags);
+        cli();
+        outb(0xff, MIDI_DATA(iobase));
+        b = inb(MIDI_STATUS(iobase));
+        restore_flags(flags);
+        if (!(b & MIDI_WRITE_EMPTY))
+                return 0;
+        while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+                if ((signed)(jiffies - timeout) > 0)
+                        return 0;
+        return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void output_status(struct hfmodem_state *dev, int ptt)
+{
+        int dcd = 0;
+
+	ptt = !!ptt;
+        if (dev->ptt_out.flags & SP_SER) {
+                outb(dcd | (ptt << 1), UART_MCR(dev->ptt_out.seriobase));
+                outb(0x40 & (-ptt), UART_LCR(dev->ptt_out.seriobase));
+        }
+        if (dev->ptt_out.flags & SP_PAR) {
+                outb(ptt | (dcd << 1), LPT_DATA(dev->ptt_out.pariobase));
+        }
+        if (dev->ptt_out.flags & SP_MIDI && ptt) {
+                outb(0, MIDI_DATA(dev->ptt_out.midiiobase));
+        }
+}
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(static void output_check(struct hfmodem_state *dev))
+{
+        enum uart u = c_uart_unknown;
+
+        if (dev->ptt_out.seriobase > 0 && dev->ptt_out.seriobase <= 0x1000-SER_EXTENT &&
+            ((u = check_uart(dev->ptt_out.seriobase))) != c_uart_unknown)
+		printk(KERN_INFO "%s: PTT output: uart found at address 0x%x type %s\n",
+		       hfmodem_drvname, dev->ptt_out.seriobase, uart_str[u]);
+	else {
+		if (dev->ptt_out.seriobase > 0)
+			printk(KERN_WARNING "%s: PTT output: no uart found at address 0x%x\n",
+			       hfmodem_drvname, dev->ptt_out.seriobase);
+		dev->ptt_out.seriobase = 0;
+	}
+        if (dev->ptt_out.pariobase > 0 && dev->ptt_out.pariobase <= 0x1000-LPT_EXTENT &&
+            !check_lpt(dev->ptt_out.pariobase)) 
+		printk(KERN_INFO "%s: PTT output: parallel port found at address 0x%x\n",
+		       hfmodem_drvname, dev->ptt_out.pariobase);
+	else {
+		if (dev->ptt_out.pariobase > 0)
+			printk(KERN_WARNING "%s: PTT output: no parallel port found at address 0x%x\n",
+			       hfmodem_drvname, dev->ptt_out.pariobase);
+		dev->ptt_out.pariobase = 0;
+	}
+	if (dev->ptt_out.midiiobase > 0 && dev->ptt_out.midiiobase <= 0x1000-MIDI_EXTENT &&
+            check_midi(dev->ptt_out.midiiobase))
+		printk(KERN_INFO "%s: PTT output: midi port found at address 0x%x\n",
+		       hfmodem_drvname, dev->ptt_out.midiiobase);
+	else {
+		if (dev->ptt_out.midiiobase > 0)
+			printk(KERN_WARNING "%s: PTT output: no midi port found at address 0x%x\n",
+			       hfmodem_drvname, dev->ptt_out.midiiobase);
+		dev->ptt_out.midiiobase = 0;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void output_open(struct hfmodem_state *dev)
+{
+        dev->ptt_out.flags = 0;
+        if (dev->ptt_out.seriobase > 0) {
+		if (!check_region(dev->ptt_out.seriobase, SER_EXTENT)) {
+			request_region(dev->ptt_out.seriobase, SER_EXTENT, "hfmodem ser ptt");
+			dev->ptt_out.flags |= SP_SER;
+			outb(0, UART_IER(dev->ptt_out.seriobase));
+			/* 5 bits, 1 stop, no parity, no break, Div latch access */
+			outb(0x80, UART_LCR(dev->ptt_out.seriobase));
+			outb(0, UART_DLM(dev->ptt_out.seriobase));
+			outb(1, UART_DLL(dev->ptt_out.seriobase)); /* as fast as possible */
+			/* LCR and MCR set by output_status */
+		} else
+			printk(KERN_WARNING "%s: PTT output: serial port at 0x%x busy\n",
+			       hfmodem_drvname, dev->ptt_out.seriobase);
+	}
+        if (dev->ptt_out.pariobase > 0) {
+		if (!check_region(dev->ptt_out.pariobase, LPT_EXTENT)) {
+			request_region(dev->ptt_out.pariobase, LPT_EXTENT, "hfmodem par ptt");
+			dev->ptt_out.flags |= SP_PAR;
+		} else
+			printk(KERN_WARNING "%s: PTT output: parallel port at 0x%x busy\n",
+			       hfmodem_drvname, dev->ptt_out.pariobase);
+	}
+        if (dev->ptt_out.midiiobase > 0) {
+		if (!check_region(dev->ptt_out.midiiobase, MIDI_EXTENT)) {
+			request_region(dev->ptt_out.midiiobase, MIDI_EXTENT, "hfmodem midi ptt");
+			dev->ptt_out.flags |= SP_MIDI;
+		} else
+			printk(KERN_WARNING "%s: PTT output: midi port at 0x%x busy\n",
+			       hfmodem_drvname, dev->ptt_out.midiiobase);
+	}
+	output_status(dev, 0);
+        printk(KERN_INFO "%s: PTT output:", hfmodem_drvname);
+        if (dev->ptt_out.flags & SP_SER)
+                printk(" serial interface at 0x%x", dev->ptt_out.seriobase);
+        if (dev->ptt_out.flags & SP_PAR)
+                printk(" parallel interface at 0x%x", dev->ptt_out.pariobase);
+        if (dev->ptt_out.flags & SP_MIDI)
+                printk(" mpu401 (midi) interface at 0x%x", dev->ptt_out.midiiobase);
+        if (!dev->ptt_out.flags)
+                printk(" none");
+        printk("\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+static void output_close(struct hfmodem_state *dev)
+{
+        /* release regions used for PTT output */
+        output_status(dev, 0);
+        if (dev->ptt_out.flags & SP_SER)
+                release_region(dev->ptt_out.seriobase, SER_EXTENT);
+        if (dev->ptt_out.flags & SP_PAR)
+                release_region(dev->ptt_out.pariobase, LPT_EXTENT);
+        if (dev->ptt_out.flags & SP_MIDI)
+                release_region(dev->ptt_out.midiiobase, MIDI_EXTENT);
+        dev->ptt_out.flags = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#define INC_SAMPLE   (1000000/HFMODEM_SRATE)
+#define INC_FRAGMENT (HFMODEM_FRAGSAMPLES*1000000/HFMODEM_SRATE)
+#define SIZE         (HFMODEM_FRAGSAMPLES*HFMODEM_NUMFRAGS)
+
+static void hfmodem_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct hfmodem_state *dev = (struct hfmodem_state *)dev_id;
+	unsigned int dmaptr;
+	__s16 *s;
+	unsigned int curfrag, nfrags;
+	int i;
+	hfmodem_time_t l1time;
+
+	dmaptr = dev->scops->intack(dev);
+	l1time = hfmodem_refclock_current(dev, ((SIZE+dmaptr-dev->dma.last_dmaptr) % SIZE) * 
+					  INC_SAMPLE, 1);
+	curfrag = (dev->dma.last_dmaptr = dmaptr) / HFMODEM_FRAGSAMPLES;
+	l1time -= INC_SAMPLE * (SIZE+dmaptr-dev->dma.fragptr*HFMODEM_FRAGSAMPLES) % SIZE;
+	sti();
+	/*
+	 * handle receiving
+	 */
+	if (dev->dma.ptt_frames <= 0) {
+		while (dev->dma.fragptr != curfrag) {
+			if (dev->dma.fragptr < HFMODEM_EXCESSFRAGS) {
+				s = dev->dma.buf + SIZE + HFMODEM_FRAGSAMPLES * dev->dma.fragptr;
+				memcpy(s, s - SIZE, HFMODEM_FRAGSIZE);
+			} else
+				s = dev->dma.buf + HFMODEM_FRAGSAMPLES * dev->dma.fragptr;
+			if (dev->sbuf.kbuf && dev->sbuf.kptr && dev->sbuf.rem > 0) {
+				i = HFMODEM_FRAGSAMPLES;
+				if (i > dev->sbuf.rem)
+					i = dev->sbuf.rem;
+				memcpy(dev->sbuf.kptr, s, i * sizeof(s[0]));
+				dev->sbuf.rem -= i;
+				dev->sbuf.kptr += i;
+			}
+			hfmodem_input_samples(dev, l1time, INC_SAMPLE, s);
+			l1time += INC_FRAGMENT;
+			dev->dma.fragptr++;
+			if (dev->dma.fragptr >= HFMODEM_NUMFRAGS)
+				dev->dma.fragptr = 0;
+		}
+		/*
+		 * check for output
+		 */
+		if (hfmodem_next_tx_event(dev, l1time) > (long)INC_FRAGMENT/2)
+			goto int_return;
+		/*
+		 * start output
+		 */
+		output_status(dev, 1);
+		dev->scops->prepare_output(dev);
+		dev->dma.last_dmaptr = 0;
+		/*
+		 * clock adjust
+		 */
+		l1time = hfmodem_refclock_current(dev, 0, 0);
+		/*
+		 * fill first two fragments
+		 */
+		dev->dma.ptt_frames = 1;
+		for (i = 0; i < 2 && i < HFMODEM_NUMFRAGS; i++)
+			if (hfmodem_output_samples(dev, l1time+i*INC_FRAGMENT, INC_SAMPLE, 
+						   dev->dma.buf+i*HFMODEM_FRAGSAMPLES))
+				dev->dma.ptt_frames = i + 1;
+		dev->dma.lastfrag = 0;
+		dev->scops->trigger_output(dev);
+		/*
+		 * finish already pending rx requests
+		 */
+		hfmodem_finish_pending_rx_requests(dev);
+		goto int_return;
+	}
+	/*
+	 * handle transmitting
+	 */
+	nfrags = HFMODEM_NUMFRAGS + curfrag - dev->dma.lastfrag;
+	dev->dma.lastfrag = curfrag;
+	if (nfrags >= HFMODEM_NUMFRAGS)
+		nfrags -= HFMODEM_NUMFRAGS;
+	dev->dma.ptt_frames -= nfrags;
+	if (dev->dma.ptt_frames < 0)
+		dev->dma.ptt_frames = 0;
+	while (dev->dma.ptt_frames < HFMODEM_NUMFRAGS && dev->dma.ptt_frames < 4 && 
+	       hfmodem_output_samples(dev, l1time+dev->dma.ptt_frames*INC_FRAGMENT, 
+				      INC_SAMPLE, dev->dma.buf + HFMODEM_FRAGSAMPLES * 
+				      ((curfrag + dev->dma.ptt_frames) % HFMODEM_NUMFRAGS)))
+		dev->dma.ptt_frames++;
+	if (dev->dma.ptt_frames > 0)
+		goto int_return;
+	/* 
+	 * start receiving
+	 */
+	output_status(dev, 0);
+	dev->dma.last_dmaptr = 0;
+	dev->dma.lastfrag = 0;
+	dev->dma.fragptr = 0;
+	dev->dma.ptt_frames = 0;
+	dev->scops->prepare_input(dev);
+	dev->scops->trigger_input(dev);
+	hfmodem_refclock_current(dev, 0, 0);  /* needed to reset the time difference */
+int_return:
+	hfmodem_wakeup(dev);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hfmodem_close(struct inode *inode, struct file *file)
+{
+	struct hfmodem_state *dev = &hfmodem_state[0];
+
+	if (!dev->active)
+		return -EPERM;
+	dev->active = 0;
+	dev->scops->stop(dev);
+        free_irq(dev->io.irq, dev);        
+	disable_dma(dev->io.dma);
+	free_dma(dev->io.dma);
+        release_region(dev->io.base_addr, dev->scops->extent);
+	kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS));
+	hfmodem_clear_rq(dev);
+	if (dev->sbuf.kbuf) {
+		kfree_s(dev->sbuf.kbuf, dev->sbuf.size);
+		dev->sbuf.kbuf = dev->sbuf.kptr = NULL;
+		dev->sbuf.size = dev->sbuf.rem = 0;
+	}
+	output_close(dev);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hfmodem_open(struct inode *inode, struct file *file)
+{
+	struct hfmodem_state *dev = &hfmodem_state[0];
+
+	if (dev->active)
+		return -EBUSY;
+	if (!dev->scops)
+		return -EPERM;
+	/*
+	 * clear vars
+	 */
+	memset(&dev->l1, 0, sizeof(dev->l1));
+	dev->dma.last_dmaptr = 0;
+	dev->dma.lastfrag = 0;
+	dev->dma.fragptr = 0;
+	dev->dma.ptt_frames = 0;
+	/*
+	 * allocate memory
+	 */
+	if (!(dev->dma.buf = kmalloc(HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS), GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	/*
+	 * allocate resources
+	 */
+        if (request_dma(dev->io.dma, hfmodem_drvname)) {
+                kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS));
+                return -EBUSY;
+        }
+        if (request_irq(dev->io.irq, hfmodem_interrupt, SA_INTERRUPT, hfmodem_drvname, dev)) {
+                free_dma(dev->io.dma);
+                kfree_s(dev->dma.buf, HFMODEM_FRAGSIZE * (HFMODEM_NUMFRAGS+HFMODEM_EXCESSFRAGS));
+                return -EBUSY;
+        }
+        request_region(dev->io.base_addr, dev->scops->extent, hfmodem_drvname);
+	
+	/* clear requests */
+      	dev->active++;
+	MOD_INC_USE_COUNT;
+	hfmodem_refclock_init(dev);
+	output_open(dev);
+	dev->scops->init(dev);
+	dev->scops->prepare_input(dev);
+	dev->scops->trigger_input(dev);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct file_operations hfmodem_fops = {
+	NULL,		    /* hfmodem_seek */
+	NULL,               /* hfmodem_read */
+	NULL,               /* hfmodem_write */
+	NULL, 		    /* hfmodem_readdir */
+#if LINUX_VERSION_CODE >= 0x20100
+	hfmodem_poll,       /* hfmodem_poll */
+#else 
+	hfmodem_select,     /* hfmodem_select */
+#endif
+	hfmodem_ioctl,      /* hfmodem_ioctl */
+	NULL,		    /* hfmodem_mmap */
+	hfmodem_open,       /* hfmodem_open */
+	hfmodem_close,      /* hfmodem_close */
+	NULL,               /* hfmodem_fsync */
+	NULL,               /* hfmodem_fasync */
+	NULL,               /* hfmodem_check_media_change */
+	NULL                /* hfmodem_revalidate */
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct miscdevice hfmodem_device = {
+	HFMODEM_MINOR, hfmodem_drvname, &hfmodem_fops
+};
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * Command line parameters
+ */
+
+static int hw = 0;
+static unsigned int iobase = 0x220;
+static unsigned int irq = 7;
+static unsigned int dma = 1;
+
+static unsigned int serio = 0;
+static unsigned int pario = 0;
+static unsigned int midiio = 0;
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(hw, "i");
+MODULE_PARM_DESC(hw, "hardware type: 0=SBC, 1=WSS");
+MODULE_PARM(iobase, "i");
+MODULE_PARM_DESC(iobase, "io base address");
+MODULE_PARM(irq, "i");
+MODULE_PARM_DESC(irq, "interrupt number");
+MODULE_PARM(dma, "i");
+MODULE_PARM_DESC(dma, "dma number (>=4 for SB16/32/64/etc, <=3 for the rest)");
+MODULE_PARM(serio, "i");
+MODULE_PARM_DESC(serio, "address of serial port to output PTT");
+MODULE_PARM(pario, "i");
+MODULE_PARM_DESC(pario, "address of parial port to output PTT");
+MODULE_PARM(midiio, "i");
+MODULE_PARM_DESC(midiio, "address of midi (MPU401) port to output PTT");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("HF FSK modem code");
+
+/* these are the module parameters from refclock.c */
+
+MODULE_PARM(scale_tvusec, "i");
+MODULE_PARM_DESC(scale_tvusec, "Scaling value for the tv_usec field (can be obta
+ined by refclock)");
+
+#ifdef __i386__
+MODULE_PARM(scale_rdtsc, "i");
+MODULE_PARM_DESC(scale_rdtsc, "Scaling value for the rdtsc counter (can be obtai
+ned by refclock)");
+MODULE_PARM(rdtsc_ok, "i");
+MODULE_PARM_DESC(rdtsc_ok, "Set to 0 to disable the use of the rdtsc instruction
+");
+#endif /* __i386__ */
+
+#endif
+
+__initfunc(int init_module(void))
+{
+	int i;
+
+	printk(hfmodem_drvinfo);
+	memset(hfmodem_state, 0, sizeof(hfmodem_state));
+        memset(hfmodem_correlator_cache, 0, sizeof(hfmodem_correlator_cache));
+	hfmodem_state[0].io.base_addr = iobase;
+	hfmodem_state[0].io.irq = irq;
+	hfmodem_state[0].io.dma = dma;
+	hfmodem_state[0].ptt_out.seriobase = serio;
+	hfmodem_state[0].ptt_out.pariobase = pario;
+	hfmodem_state[0].ptt_out.midiiobase = midiio;
+	hfmodem_refclock_probe();
+	output_check(&hfmodem_state[0]);
+#if defined(CONFIG_HFMODEM_WSS) && defined(CONFIG_HFMODEM_SBC)
+        if (hw) 
+                i = hfmodem_wssprobe(&hfmodem_state[0]);
+        else
+                i = hfmodem_sbcprobe(&hfmodem_state[0]);
+#else
+        i = -EINVAL;
+#ifdef CONFIG_HFMODEM_WSS
+        i = hfmodem_wssprobe(&hfmodem_state[0]);
+#endif
+#ifdef CONFIG_HFMODEM_SBC
+        i = hfmodem_sbcprobe(&hfmodem_state[0]);
+#endif
+#endif
+	if (i)
+		return i;
+	if ((i =  misc_register(&hfmodem_device))) {
+		printk(KERN_ERR "%s: cannot register misc device\n", hfmodem_drvname);
+		return i;
+	}
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	misc_deregister(&hfmodem_device);
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+
+static int hw = 0;
+
+__initfunc(void hfmodem_setup(char *str, int *ints))
+{
+        if (ints[0] < 7) {
+                printk(KERN_WARNING "%s: setup: too few parameters\n", hfmodem_drvname);
+                return;
+        }       
+        memset(hfmodem_state, 0, sizeof(hfmodem_state));
+        memset(hfmodem_correlator_cache, 0, sizeof(hfmodem_correlator_cache));
+        hw = ints[1];
+        hfmodem_state[0].io.base_addr = ints[2];
+        hfmodem_state[0].io.irq = ints[3];
+        hfmodem_state[0].io.dma = ints[4];
+        if (ints[0] >= 8)
+                hfmodem_state[0].ptt_out.seriobase = ints[5];
+        if (ints[0] >= 9)
+                hfmodem_state[0].ptt_out.pariobase = ints[6];
+        if (ints[0] >= 10)
+                hfmodem_state[0].ptt_out.midiiobase = ints[7];
+        hfmodem_refclock_setscale(ints[ints[0]-2], ints[ints[0]-1], ints[ints[0]]);
+}
+
+__initfunc(void hfmodem_init(void))
+{
+        int i;
+
+	printk(hfmodem_drvinfo);
+        hfmodem_refclock_probe();
+        output_check(&hfmodem_state[0]);
+#if defined(CONFIG_HFMODEM_WSS) && defined(CONFIG_HFMODEM_SBC)
+        if (hw) 
+                i = hfmodem_wssprobe(&hfmodem_state[0]);
+        else
+                i = hfmodem_sbcprobe(&hfmodem_state[0]);
+#else
+        i = -EINVAL;
+#ifdef CONFIG_HFMODEM_WSS
+        i = hfmodem_wssprobe(&hfmodem_state[0]);
+#endif
+#ifdef CONFIG_HFMODEM_SBC
+        i = hfmodem_sbcprobe(&hfmodem_state[0]);
+#endif
+#endif
+        if (i) {
+                printk(KERN_ERR "%s: soundcard probe failed\n", hfmodem_drvname);
+                return;
+        }
+        if ((i =  misc_register(&hfmodem_device))) {
+                printk(KERN_ERR "%s: cannot register misc device\n", hfmodem_drvname);
+                return;
+        }
+}
+
+/* --------------------------------------------------------------------- */
+#endif /* MODULE */
+
diff -u --recursive --new-file v2.1.48/linux/drivers/char/hfmodem/modem.c linux/drivers/char/hfmodem/modem.c
--- v2.1.48/linux/drivers/char/hfmodem/modem.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/hfmodem/modem.c	Tue Aug  5 09:48:55 1997
@@ -0,0 +1,792 @@
+/*****************************************************************************/
+
+/*
+ *      modem.c  --  Linux soundcard HF FSK driver,
+ *                   Modem code.
+ *
+ *      Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *        Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+/*****************************************************************************/
+      
+
+#include <linux/wait.h>
+#include <linux/malloc.h>
+#include <linux/hfmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+struct hfmodem_correlator_cache hfmodem_correlator_cache[HFMODEM_CORRELATOR_CACHE];
+
+/* --------------------------------------------------------------------- */
+
+#include "tables.h"
+
+#define M_PI        3.14159265358979323846      /* pi */
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int isimplecos(unsigned int arg)
+{
+	return isintab[((arg+0x4000) >> (16-SINTABBITS)) & (SINTABSIZE-1)];
+}
+
+extern __inline__ int isimplesin(unsigned int arg)
+{
+	return isintab[(arg >> (16-SINTABBITS)) & (SINTABSIZE-1)];
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int itblcos(unsigned int arg)
+{
+	unsigned int x;
+	int dx;
+	int s, c;
+
+	x = (arg + (0x8000 >> SINTABBITS)) & (0xffffu & (0xffffu << (16-SINTABBITS)));
+	dx = arg - x;
+	x >>= (16-SINTABBITS);
+	c = isintab[x+(0x4000 >> (16-SINTABBITS))];
+	s = isintab[x];
+	return c - ((s * dx * (int)(M_PI*64.0)) >> 21);
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void itblcossin(unsigned int arg, int *cos, int *sin)
+{
+	unsigned int x;
+	int dx;
+	int s, c;
+
+	x = (arg + (0x8000 >> SINTABBITS)) & (0xffffu & (0xffffu << (16-SINTABBITS)));
+	dx = arg - x;
+	x >>= (16-SINTABBITS);
+	c = isintab[x+(0x4000 >> (16-SINTABBITS))];
+	s = isintab[x];
+	*cos = c - ((s * dx * (int)(M_PI*64.0)) >> 21);
+	*sin = s + ((c * dx * (int)(M_PI*64.0)) >> 21);
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned short random_seed;
+
+extern __inline__ unsigned short random_num(void)
+{
+        random_seed = 28629 * random_seed + 157;
+        return random_seed;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * correlator cache routines
+ */
+
+extern __inline__ void cc_lock(unsigned int u)
+{
+	if (u >= HFMODEM_CORRELATOR_CACHE)
+		return;
+	hfmodem_correlator_cache[u].refcnt++;
+}
+
+extern __inline__ void cc_unlock(unsigned int u)
+{
+	if (u >= HFMODEM_CORRELATOR_CACHE)
+		return;
+	if ((--hfmodem_correlator_cache[u].refcnt) <= 0) {
+		unsigned int i;
+
+		for (i = 0; i < HFMODEM_CORRELATOR_CACHE; i++) 
+			if (hfmodem_correlator_cache[i].lru < 32767)
+				hfmodem_correlator_cache[i].lru++;
+		hfmodem_correlator_cache[u].lru = 0;
+		hfmodem_correlator_cache[u].refcnt = 0;
+	}
+}
+
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int cc_lookup(unsigned short phinc0, unsigned short phinc1)
+{
+	unsigned int j;
+
+	/* find correlator cache entry */
+	for (j = 0; j < HFMODEM_CORRELATOR_CACHE; j++) 
+		if (hfmodem_correlator_cache[j].phase_incs[0] == phinc0 &&
+		    hfmodem_correlator_cache[j].phase_incs[1] == phinc1)
+			return j;
+	return ~0;
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int cc_replace(void)
+{
+	unsigned int j, k = HFMODEM_CORRELATOR_CACHE;
+	int l = -1;
+
+	for (j = 0; j < HFMODEM_CORRELATOR_CACHE; j++)
+		if (hfmodem_correlator_cache[j].refcnt <= 0 && hfmodem_correlator_cache[j].lru > l) {
+			k = j;
+			l = hfmodem_correlator_cache[j].lru;
+		}
+	if (k < HFMODEM_CORRELATOR_CACHE)
+		return k;
+	printk(KERN_ERR "%s: modem: out of filter coefficient cache entries\n", hfmodem_drvname);
+	return random_num() % HFMODEM_CORRELATOR_CACHE;
+}
+
+/* --------------------------------------------------------------------- */
+
+#define SH1  8     /* min. ceil(log2(L1CORR_LEN)) - (31-(2*15-1)) */
+#define SH2  (3*15-2*SH1)
+
+#ifdef __i386__
+
+extern __inline__ int icorr(int n, const int *coeff, const short *inp)
+{
+	int ret, rethi, tmp1 = 0, tmp2 = 0;
+
+	__asm__("\n1:\n\t"
+		"movswl (%0),%%eax\n\t"
+		"imull (%1)\n\t"
+		"subl $2,%0\n\t"
+		"addl $4,%1\n\t"
+		"addl %%eax,%3\n\t"
+		"adcl %%edx,%4\n\t"
+		"decl %2\n\t"
+		"jne 1b\n\t"
+		: "=&S" (inp), "=&D" (coeff), "=&c" (n), "=m" (tmp1), "=m" (tmp2)
+		: "0" (inp), "1" (coeff), "2" (n)
+		: "ax", "dx");
+	__asm__("shrdl %2,%1,%0\n\t"
+		"# sarl %2,%1\n\t"
+		: "=&r" (ret), "=&r" (rethi)
+		: "i" (SH1), "0" (tmp1), "1" (tmp2));
+
+
+	return ret;
+}
+
+#else /* __i386__ */
+
+extern __inline__ int icorr(int n, const int *coeff, const short *inp)
+{
+	long long sum = 0;
+	int i;
+
+	for (i = n; i > 0; i--, coeff++, inp--)
+		sum += (*coeff) * (*inp);
+	sum >>= SH1;
+	return sum;
+}
+
+#endif /* __i386__ */
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ long long isqr(int x) __attribute__ ((const));
+
+extern __inline__ long long isqr(int x)
+{
+	return ((long long)x) * ((long long)x);
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ hfmodem_soft_t do_filter(struct hfmodem_l1_rxslot *slot, short *s)
+{
+	unsigned int cc = slot->corr_cache;
+	long long ll;
+
+	if (cc >= HFMODEM_CORRELATOR_CACHE) {
+		printk(KERN_ERR "do_filter: correlator cache index overrange\n");
+		return 0;
+	}
+	ll = isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[1][0], s)) +
+		isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[1][1], s)) -
+		isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[0][0], s)) -
+		isqr(icorr(slot->corrlen, hfmodem_correlator_cache[cc].correlator[0][1], s));
+	ll >>= SH2;
+       	return (ll * slot->scale) >> 23;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void cc_prepare(struct hfmodem_l1_rxslot *slot, unsigned short phinc0, unsigned short phinc1)
+{
+	unsigned int j, k, l, ph, phinc;
+
+	slot->scale = (1<<23) / (slot->corrlen*slot->corrlen);
+
+	j = cc_lookup(phinc0, phinc1);
+	if (j >= HFMODEM_CORRELATOR_CACHE) {
+		j = cc_replace();
+		/* calculate the correlator values */
+		printk(KERN_DEBUG "%s: corr cache calc: %u  phases: 0x%04x 0x%04x\n", 
+		       hfmodem_drvname, j, phinc0, phinc1);
+		hfmodem_correlator_cache[j].phase_incs[0] = phinc0;
+		hfmodem_correlator_cache[j].phase_incs[1] = phinc1;
+		for (k = 0; k < 2; k++) {
+			phinc = hfmodem_correlator_cache[j].phase_incs[k];
+			for (ph = l = 0; l < HFMODEM_MAXCORRLEN; l++, ph = (ph + phinc) & 0xffff)
+				itblcossin(ph, &hfmodem_correlator_cache[j].correlator[k][0][l],
+					   &hfmodem_correlator_cache[j].correlator[k][1][l]);
+		}
+		hfmodem_correlator_cache[j].refcnt = 0;
+
+#if 0
+		printk(KERN_DEBUG "%s: corr: %u ph: 0x%04x 0x%04x\n", hfmodem_drvname, j, 
+		       hfmodem_correlator_cache[j].phase_incs[0],
+		       hfmodem_correlator_cache[j].phase_incs[1]);
+		for (l = 0; l < HFMODEM_MAXCORRLEN; l++)
+			printk(KERN_DEBUG "%s: corr: %6d %6d %6d %6d\n", hfmodem_drvname, 
+			       hfmodem_correlator_cache[j].correlator[0][0][l],
+			       hfmodem_correlator_cache[j].correlator[0][1][l],
+			       hfmodem_correlator_cache[j].correlator[1][0][l],
+			       hfmodem_correlator_cache[j].correlator[1][1][l]);
+#endif
+	}
+	slot->corr_cache = j;
+	cc_lock(j);
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_clear_rq(struct hfmodem_state *dev)
+{
+	unsigned long flags;
+	unsigned int i;
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+		if (dev->l1.rxslots[i].state == ss_unused)
+			continue;
+		dev->l1.rxslots[i].state = ss_unused;
+		kfree_s(dev->l1.rxslots[i].data, dev->l1.rxslots[i].nbits * sizeof(hfmodem_soft_t));
+	}
+	for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+		if (dev->l1.txslots[i].state == ss_unused)
+			continue;
+		dev->l1.txslots[i].state = ss_unused;
+		kfree_s(dev->l1.txslots[i].data, (dev->l1.txslots[i].nbits + 7) >> 3);
+	}
+	for (i = 0; i < HFMODEM_CORRELATOR_CACHE; i++)
+		hfmodem_correlator_cache[i].refcnt = 0;
+	restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+int hfmodem_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct hfmodem_state *dev = &hfmodem_state[0];
+	struct hfmodem_ioctl_fsk_tx_request txrq;
+	struct hfmodem_ioctl_fsk_rx_request rxrq;
+	struct hfmodem_ioctl_mixer_params mix;
+	struct hfmodem_ioctl_sample_params spar;
+	unsigned long flags;
+	unsigned int len;
+	int ret, i, idx;
+	void *data, *userdata;
+	hfmodem_id_t id;
+	hfmodem_time_t tm = 0;
+	
+	if (!dev->active)
+		return -EBUSY;
+	switch(cmd) {
+	default:
+		return -EINVAL;
+		
+	case HFMODEM_IOCTL_FSKTXREQUEST:
+		if ((ret = copy_from_user(&txrq, (void *)arg, sizeof(txrq))))
+			return ret;
+		if (txrq.nbits > HFMODEM_MAXBITS)
+			return -EINVAL;
+		len = (txrq.nbits + 7) >> 3;
+		if (!(data = kmalloc(len, GFP_KERNEL)))
+			return -ENOMEM;
+		if (copy_from_user(data, txrq.data, len)) {
+			kfree_s(data, len);
+			return -EFAULT;
+		}
+		save_flags(flags);
+		cli();
+		for (i = 0; i < HFMODEM_NUMTXSLOTS && dev->l1.txslots[i].state != ss_unused; i++);
+		if (i >= HFMODEM_NUMTXSLOTS) {
+			restore_flags(flags);
+			kfree_s(data, len);
+			return -EBUSY;
+		}
+		dev->l1.txslots[i].state = ss_ready;
+		dev->l1.txslots[i].tstart = txrq.tstart;
+		dev->l1.txslots[i].tinc = txrq.tinc;
+		dev->l1.txslots[i].data = data;
+		dev->l1.txslots[i].nbits = txrq.nbits;
+		dev->l1.txslots[i].cntbits = 0;
+		dev->l1.txslots[i].inv = txrq.inv ? 0xff : 0;
+		dev->l1.txslots[i].id = txrq.id;
+		dev->l1.txslots[i].phase_incs[0] = ((txrq.freq[0]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE)
+			& 0xffff;
+		dev->l1.txslots[i].phase_incs[1] = ((txrq.freq[1]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE)
+			& 0xffff;
+		restore_flags(flags);
+		return 0;
+		
+	case HFMODEM_IOCTL_FSKRXREQUEST:
+		if ((ret = copy_from_user(&rxrq, (void *)arg, sizeof(rxrq))))
+			return ret;
+		if (rxrq.nbits > HFMODEM_MAXBITS)
+			return -EINVAL;
+		if (rxrq.baud < HFMODEM_MINBAUD || rxrq.baud > HFMODEM_MAXBAUD)
+			return -EINVAL;
+		len = rxrq.nbits * sizeof(hfmodem_soft_t);
+		if (verify_area(VERIFY_WRITE, rxrq.data, len))
+			return -EFAULT;
+		if (!(data = kmalloc(len, GFP_KERNEL)))
+			return -ENOMEM;
+		save_flags(flags);
+		cli();
+		for (i = 0; i < HFMODEM_NUMRXSLOTS && dev->l1.rxslots[i].state != ss_unused; i++);
+		if (i >= HFMODEM_NUMRXSLOTS) {
+			restore_flags(flags);
+			kfree_s(data, len);
+			return -EBUSY;
+		}
+		dev->l1.rxslots[i].state = ss_ready;
+		dev->l1.rxslots[i].tstart = rxrq.tstart;
+		dev->l1.rxslots[i].tinc = rxrq.tinc;
+		dev->l1.rxslots[i].data = data;
+		dev->l1.rxslots[i].userdata = rxrq.data;
+		dev->l1.rxslots[i].nbits = rxrq.nbits;
+		dev->l1.rxslots[i].cntbits = 0;
+		dev->l1.rxslots[i].id = rxrq.id;
+		dev->l1.rxslots[i].corrlen = HFMODEM_SRATE/rxrq.baud;
+		cc_prepare(dev->l1.rxslots+i, 
+			   ((rxrq.freq[0]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE) & 0xffff,
+			   ((rxrq.freq[1]*0x10000+(HFMODEM_SRATE/2))/HFMODEM_SRATE) & 0xffff);
+		restore_flags(flags);
+		return 0;
+		
+	case HFMODEM_IOCTL_CLEARRQ:
+		hfmodem_clear_rq(dev);
+		return 0;
+		
+	case HFMODEM_IOCTL_GETCURTIME:
+		return put_user(dev->l1.last_time + 20000L, (hfmodem_time_t *)arg); /* heuristic */
+	
+	case HFMODEM_IOCTL_WAITRQ:
+		save_flags(flags);
+		cli();
+		ret = 0;
+		for (idx = -1, i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+			if (dev->l1.rxslots[i].state == ss_unused)
+				continue;
+			if (dev->l1.rxslots[i].state != ss_retired) {
+				ret++;
+				continue;
+			}
+			if (idx < 0 || (signed)(tm - dev->l1.rxslots[i].tstart) > 0) {
+				idx = i;
+				tm = dev->l1.rxslots[i].tstart;
+			}
+		}
+		if (idx >= 0) {
+			cc_unlock(dev->l1.rxslots[idx].corr_cache);
+			id = dev->l1.rxslots[idx].id;
+			data = dev->l1.rxslots[idx].data;
+			userdata = dev->l1.rxslots[idx].userdata;
+			len = dev->l1.rxslots[idx].nbits * sizeof(hfmodem_soft_t);
+			dev->l1.rxslots[idx].state = ss_unused;
+			restore_flags(flags);
+			ret = copy_to_user(userdata, data, len);
+			kfree_s(data, len);
+			return (put_user(id, (hfmodem_id_t *)arg)) ? -EFAULT : ret;
+		}
+		for (idx = -1, i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+			if (dev->l1.txslots[i].state == ss_unused)
+				continue;
+			if (dev->l1.txslots[i].state != ss_retired) {
+				ret++;
+				continue;
+			}
+			if (idx < 0 || (signed)(tm - dev->l1.txslots[i].tstart) > 0) {
+				idx = i;
+				tm = dev->l1.txslots[i].tstart;
+			}
+		}
+		if (idx >= 0) {
+			id = dev->l1.txslots[idx].id;
+			data = dev->l1.txslots[idx].data;
+			len = (dev->l1.txslots[idx].nbits + 7) >> 3;
+			dev->l1.txslots[idx].state = ss_unused;
+			restore_flags(flags);
+			kfree_s(data, len);
+			return put_user(id, (hfmodem_id_t *)arg);
+		}
+		restore_flags(flags);
+		return ret ? -EAGAIN : -EPIPE;
+
+	case HFMODEM_IOCTL_MIXERPARAMS:
+		if ((ret = copy_from_user(&mix, (void *)arg, sizeof(mix))))
+			return ret;
+		dev->scops->mixer(dev, mix.src, mix.igain, mix.ogain);
+		return 0;
+		
+	case HFMODEM_IOCTL_SAMPLESTART:
+		save_flags(flags);
+		cli();
+		if (dev->sbuf.kbuf) 
+			kfree_s(dev->sbuf.kbuf, dev->sbuf.size);
+		dev->sbuf.kbuf = dev->sbuf.kptr = NULL;
+		dev->sbuf.size = dev->sbuf.rem = 0;
+		restore_flags(flags);
+		if ((ret = copy_from_user(&spar, (void *)arg, sizeof(spar))))
+			return ret;
+		if (spar.len == 0)
+			return 0;
+		if (spar.len < 2 || spar.len > 8192)
+			return -EINVAL;
+		if (verify_area(VERIFY_WRITE, spar.data, spar.len * sizeof(__s16)))
+			return -EFAULT;
+		if (!(dev->sbuf.kbuf = kmalloc(spar.len * sizeof(__s16), GFP_KERNEL)))
+			return -ENOMEM;
+		save_flags(flags);
+		cli();
+		dev->sbuf.kptr = dev->sbuf.kbuf;
+		dev->sbuf.size = spar.len * sizeof(__s16);
+		dev->sbuf.rem = spar.len;
+		dev->sbuf.ubuf = spar.data;
+		restore_flags(flags);
+		return 0;
+		
+	case HFMODEM_IOCTL_SAMPLEFINISHED:
+		save_flags(flags);
+		cli();
+		if (dev->sbuf.rem > 0) {
+			restore_flags(flags);
+			return -EAGAIN;
+		}
+		if (!dev->sbuf.kbuf || !dev->sbuf.size) {
+			restore_flags(flags);
+			return -EPIPE;
+		}
+		restore_flags(flags);
+		ret = copy_to_user(dev->sbuf.ubuf, dev->sbuf.kbuf, dev->sbuf.size);
+		kfree_s(dev->sbuf.kbuf, dev->sbuf.size);
+		dev->sbuf.kbuf = dev->sbuf.kptr = NULL;
+		dev->sbuf.size = dev->sbuf.rem = 0;
+		return ret;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= 0x20100
+
+unsigned int hfmodem_poll(struct file *file, poll_table *wait)
+{
+	struct hfmodem_state *dev = &hfmodem_state[0];
+	unsigned long flags;
+	int i, cnt1, cnt2;
+	
+	save_flags(flags);
+	cli();
+	for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+		if (dev->l1.txslots[i].state == ss_retired)
+			cnt1++;
+		if (dev->l1.txslots[i].state != ss_unused)
+			cnt2++;
+	}
+	for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+		if (dev->l1.rxslots[i].state == ss_retired)
+			cnt1++;
+		if (dev->l1.rxslots[i].state != ss_unused)
+			cnt2++;
+	}
+	restore_flags(flags);
+	poll_wait(&dev->wait, wait);
+        if (cnt1 || !cnt2)
+                return POLLIN | POLLRDNORM;
+        return 0;
+}
+
+#else 
+
+int hfmodem_select(struct inode *inode, struct file *file, int sel_type, select_table *wait)
+{
+	struct hfmodem_state *dev = &hfmodem_state[0];
+	unsigned long flags;
+	int i, cnt1, cnt2;
+	
+	if (sel_type == SEL_IN) {
+		save_flags(flags);
+		cli();
+		for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+			if (dev->l1.txslots[i].state == ss_retired)
+				cnt1++;
+			if (dev->l1.txslots[i].state != ss_unused)
+				cnt2++;
+		}
+		for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+			if (dev->l1.rxslots[i].state == ss_retired)
+				cnt1++;
+			if (dev->l1.rxslots[i].state != ss_unused)
+				cnt2++;
+		}
+		restore_flags(flags);
+		if (cnt1 || !cnt2)
+			return 1;
+		select_wait(&dev->wait, wait);
+    	}
+	return 0;
+}
+
+#endif
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int l1fsk_phinc(struct hfmodem_l1_txslot *txs, unsigned int nbit)
+{
+	return txs->phase_incs[!!((txs->data[nbit >> 3] ^ txs->inv) & (1 << (nbit & 7)))];
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_input_samples(struct hfmodem_state *dev, hfmodem_time_t tstart, 
+			 hfmodem_time_t tinc, __s16 *samples)
+{
+	hfmodem_time_t tst, tend;
+	__s16 *s;
+	int i, j;
+	hfmodem_soft_t sample;
+
+	dev->l1.last_time = tstart + (HFMODEM_FRAGSAMPLES-1) * tinc;
+	for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+		struct hfmodem_l1_rxslot *rxs = dev->l1.rxslots + i;
+
+		if (rxs->state == ss_unused || rxs->state == ss_retired)
+			continue;
+		tst = tstart - (rxs->corrlen-1) * tinc;
+		tend = tst + (HFMODEM_FRAGSAMPLES-1) * tinc;
+		if (rxs->state == ss_ready) {
+			if ((signed)(rxs->tstart - tend) > 0) 
+				continue;
+			rxs->state = ss_oper;
+		}
+		for (s = samples, j = 0; j < HFMODEM_FRAGSAMPLES; j++, s++, tst += tinc)
+			if ((signed)(rxs->tstart - tst) <= 0) {
+				sample = do_filter(rxs, s);
+				while ((signed)(rxs->tstart - tst) <= 0 && 
+				       rxs->cntbits < rxs->nbits) {
+					rxs->data[rxs->cntbits] = sample;
+					rxs->cntbits++;
+					rxs->tstart += rxs->tinc;
+				}
+				if (rxs->cntbits >= rxs->nbits) {
+					rxs->state = ss_retired;
+					break;
+				}
+			}
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int output_one_sample(struct hfmodem_state *dev, hfmodem_time_t tm)
+{
+	int i, j, k;
+	struct hfmodem_l1_txslot *txs;
+	/*
+	 * first activate new output slots
+	 */
+	for (j = -1, i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+		txs = dev->l1.txslots + i;
+		if (txs->state == ss_ready && (signed)(txs->tstart - tm) <= 0) {
+			for (k = 0; k < HFMODEM_NUMTXSLOTS; k++) {
+				if (dev->l1.txslots[k].state != ss_oper)
+					continue;
+				dev->l1.txslots[k].state = ss_retired;
+			}
+			txs->state = ss_oper;
+			txs->tstart += txs->tinc;
+			txs->phinc = l1fsk_phinc(txs, 0);
+			txs->cntbits = 1;
+		};
+		if (txs->state != ss_oper)
+			continue;
+		j = i;
+	}
+	if (j < 0 || j >= HFMODEM_NUMTXSLOTS)
+		return 0;
+	/*
+	 * calculate the current slot
+	 */
+	txs = dev->l1.txslots + j;
+	while ((signed)(txs->tstart - tm) <= 0) {
+		if (txs->cntbits >= txs->nbits) {
+			txs->state = ss_retired;
+			return 0;
+		}
+		txs->tstart += txs->tinc;
+		txs->phinc = l1fsk_phinc(txs, txs->cntbits);
+		txs->cntbits++;
+	}
+	return txs->phinc;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hfmodem_output_samples(struct hfmodem_state *dev, hfmodem_time_t tstart, 
+			   hfmodem_time_t tinc, __s16 *samples)
+{
+	int i, j;
+	hfmodem_time_t tend = tstart + (HFMODEM_FRAGSAMPLES-1) * tinc;
+
+	for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+		if (dev->l1.txslots[i].state == ss_oper)
+			break;
+		if (dev->l1.txslots[i].state == ss_ready && 
+		    (signed)(dev->l1.txslots[i].tstart - tend) <= 0)
+			break;
+	}
+	if (i >= HFMODEM_NUMTXSLOTS)
+		return 0;
+	for (j = 0; j < HFMODEM_FRAGSAMPLES; j++, tstart += tinc, samples++) {
+		*samples = isimplecos(dev->l1.tx_phase);
+		dev->l1.tx_phase += output_one_sample(dev, tstart);
+	}
+	return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+long hfmodem_next_tx_event(struct hfmodem_state *dev, hfmodem_time_t curr)
+{
+	long diff = LONG_MAX, t;
+	int i;
+
+	for (i = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+		if (dev->l1.txslots[i].state == ss_oper)
+			if (diff > 0)
+				diff = 0;
+		if (dev->l1.txslots[i].state == ss_ready) {
+			t = dev->l1.txslots[i].tstart - curr;
+			if (t < diff)
+				diff = t;
+		}
+	}
+	return diff;
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_finish_pending_rx_requests(struct hfmodem_state *dev)
+{
+	int i;
+
+	for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+		if (dev->l1.rxslots[i].state != ss_oper)
+			continue;
+		while (dev->l1.rxslots[i].cntbits < dev->l1.rxslots[i].nbits) {
+			dev->l1.rxslots[i].data[dev->l1.rxslots[i].cntbits] = 0;
+			dev->l1.rxslots[i].cntbits++;
+		}
+		dev->l1.rxslots[i].state = ss_retired;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_wakeup(struct hfmodem_state *dev)
+{
+	int i, cnt1, cnt2;
+	
+	for (i = cnt1 = cnt2 = 0; i < HFMODEM_NUMTXSLOTS; i++) {
+		if (dev->l1.txslots[i].state == ss_retired)
+			cnt1++;
+		if (dev->l1.txslots[i].state != ss_unused)
+			cnt2++;
+	}
+	for (i = 0; i < HFMODEM_NUMRXSLOTS; i++) {
+		if (dev->l1.rxslots[i].state == ss_retired)
+			cnt1++;
+		if (dev->l1.rxslots[i].state != ss_unused)
+			cnt2++;
+	}
+	if (cnt1 || !cnt2)
+		wake_up_interruptible(&dev->wait);
+}
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.48/linux/drivers/char/hfmodem/refclock.c linux/drivers/char/hfmodem/refclock.c
--- v2.1.48/linux/drivers/char/hfmodem/refclock.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/hfmodem/refclock.c	Tue Aug  5 09:48:55 1997
@@ -0,0 +1,189 @@
+/*****************************************************************************/
+
+/*
+ *      refclock.c  --  Linux soundcard HF FSK driver,
+ *                      Reference clock routines.
+ *
+ *      Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *        Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/hfmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+/*
+ * command line params
+ */
+
+static unsigned int scale_tvusec = 1UL<<24;
+
+#ifdef __i386__
+static unsigned int scale_rdtsc = 0;
+static int rdtsc_ok = 1;
+#endif /* __i386__ */
+
+/* --------------------------------------------------------------------- */
+
+#ifdef __i386__
+
+__initfunc(static void i386_capability(void))
+{
+        unsigned long flags;
+        unsigned long fl1;
+        union {
+                struct {
+                        unsigned int ebx, edx, ecx;
+                } r;
+                unsigned char s[13];
+        } id;
+        unsigned int eax;
+	unsigned int x86_capability;
+
+        save_flags(flags);
+        flags |= 0x200000;
+        restore_flags(flags);
+        save_flags(flags);
+        fl1 = flags;
+        flags &= ~0x200000;
+        restore_flags(flags);
+        save_flags(flags);
+        if (!(fl1 & 0x200000) || (flags & 0x200000)) {
+                printk(KERN_WARNING "%s: cpu does not support CPUID\n", hfmodem_drvname);
+                return;
+        }
+        __asm__ ("cpuid" : "=a" (eax), "=b" (id.r.ebx), "=c" (id.r.ecx), "=d" (id.r.edx) :
+                 "0" (0));
+        id.s[12] = 0;
+        if (eax < 1) {
+                printk(KERN_WARNING "%s: cpu (vendor string %s) does not support capability "
+                       "list\n", hfmodem_drvname, id.s);
+                return;
+        }
+        printk(KERN_INFO "%s: cpu: vendor string %s ", hfmodem_drvname, id.s);
+        __asm__ ("cpuid" : "=a" (eax), "=d" (x86_capability) : "0" (1) : "ebx", "ecx");
+        printk("fam %d mdl %d step %d cap 0x%x\n", (eax >> 8) & 15, (eax >> 4) & 15, eax & 15, 
+	       x86_capability);
+	if (x86_capability & 0x10)
+		rdtsc_ok = 1;
+	else
+		printk(KERN_INFO "%s: cpu does not support the rdtsc instruction\n", hfmodem_drvname);
+}       
+#endif /* __i386__ */   
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(void hfmodem_refclock_probe(void))
+{
+#ifdef __i386__
+	if (rdtsc_ok) {
+		rdtsc_ok = 0;
+		i386_capability();
+		if (rdtsc_ok) {
+			unsigned int tmp0, tmp1, tmp2, tmp3;
+			__asm__("rdtsc" : "=a" (tmp0), "=d" (tmp1));
+			__asm__("rdtsc" : "=a" (tmp2), "=d" (tmp3));
+			if (tmp0 == tmp2 && tmp1 == tmp3) {
+				rdtsc_ok = 0;
+				printk(KERN_WARNING "%s: rdtsc unusable, does not change\n",
+				       hfmodem_drvname);
+			}
+		}
+	}
+	printk(KERN_INFO "%s: using %s as timing source\n", hfmodem_drvname,
+	       rdtsc_ok ? "rdtsc" : "gettimeofday");
+#endif /* __i386__ */
+}
+
+/* --------------------------------------------------------------------- */
+
+void hfmodem_refclock_init(struct hfmodem_state *dev)
+{
+	struct timeval tv;
+
+	dev->clk.lasttime = 0;
+#ifdef __i386__
+	if (rdtsc_ok) {
+		__asm__("rdtsc;" : "=&d" (dev->clk.starttime_hi), "=&a" (dev->clk.starttime_lo));
+		return;
+	}
+#endif /* __i386__ */
+	do_gettimeofday(&tv);
+	dev->clk.last_tvusec = tv.tv_usec;
+	dev->clk.time_cnt = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+hfmodem_time_t hfmodem_refclock_current(struct hfmodem_state *dev, hfmodem_time_t expected, int exp_valid)
+{
+	struct timeval tv;
+	hfmodem_time_t curtime;
+	long diff;
+
+#ifdef __i386__
+	if (rdtsc_ok) {
+		unsigned int tmp0, tmp1;
+		unsigned int tmp2, tmp3;
+		
+		__asm__("rdtsc;\n\t"
+			"subl %2,%%eax\n\t"
+			"sbbl %3,%%edx\n\t" : "=&a" (tmp0), "=&d" (tmp1) 
+			: "m" (dev->clk.starttime_lo), "m" (dev->clk.starttime_hi) : "ax", "dx");
+		__asm__("mull %1" : "=d" (tmp2) : "m" (scale_rdtsc), "a" (tmp0) : "ax");
+		__asm__("mull %1" : "=a" (tmp3) : "m" (scale_rdtsc), "a" (tmp1) : "dx");
+		curtime = tmp2 + tmp3;
+		goto time_known;
+	}
+#endif /* __i386__ */
+	do_gettimeofday(&tv);
+	dev->clk.time_cnt += (unsigned)(1000000 + tv.tv_usec - dev->clk.last_tvusec) % 1000000;
+	dev->clk.last_tvusec = tv.tv_usec;
+	curtime = (dev->clk.time_cnt * scale_tvusec) >> 24;
+  time_known:
+	if (exp_valid && abs(diff = (curtime - dev->clk.lasttime - expected)) >= 1000)
+		printk(KERN_DEBUG "%s: refclock adjustment %ld more than 1ms\n", 
+		       hfmodem_drvname, diff);
+	return (dev->clk.lasttime = curtime);
+}
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.48/linux/drivers/char/hfmodem/sbc.c linux/drivers/char/hfmodem/sbc.c
--- v2.1.48/linux/drivers/char/hfmodem/sbc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/hfmodem/sbc.c	Tue Aug  5 09:48:55 1997
@@ -0,0 +1,741 @@
+/*****************************************************************************/
+
+/*
+ *      sbc.c  --  Linux soundcard HF FSK driver, 
+ *                 Soundblaster specific functions.
+ *
+ *      Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *        Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+/*****************************************************************************/
+     
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/hfmodem.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+/* --------------------------------------------------------------------- */
+/* 
+ * the sbc converter's registers 
+ */
+#define DSP_RESET(iobase)        (iobase+0x6)
+#define DSP_READ_DATA(iobase)    (iobase+0xa)
+#define DSP_WRITE_DATA(iobase)   (iobase+0xc)
+#define DSP_WRITE_STATUS(iobase) (iobase+0xc)
+#define DSP_DATA_AVAIL(iobase)   (iobase+0xe)
+#define DSP_MIXER_ADDR(iobase)   (iobase+0x4)
+#define DSP_MIXER_DATA(iobase)   (iobase+0x5)
+#define DSP_INTACK_16BIT(iobase) (iobase+0xf)
+#define SBC_EXTENT               16
+
+/* --------------------------------------------------------------------- */
+/*
+ * SBC commands
+ */
+
+#define SBC_OUTPUT             0x14
+#define SBC_INPUT              0x24
+#define SBC_BLOCKSIZE          0x48
+#define SBC_HI_OUTPUT          0x91 
+#define SBC_HI_INPUT           0x99 
+#define SBC_LO_OUTPUT_AUTOINIT 0x1c
+#define SBC_LO_INPUT_AUTOINIT  0x2c
+#define SBC_HI_OUTPUT_AUTOINIT 0x90 
+#define SBC_HI_INPUT_AUTOINIT  0x98
+#define SBC_IMMED_INT          0xf2
+#define SBC_GET_REVISION       0xe1
+#define ESS_GET_REVISION       0xe7
+#define ESS_EXTENDED_MODE      0xc6
+#define SBC_SPEAKER_ON         0xd1
+#define SBC_SPEAKER_OFF        0xd3
+#define SBC_DMA_ON             0xd0
+#define SBC_DMA_OFF            0xd4
+#define SBC_SAMPLE_RATE        0x40
+#define SBC_SAMPLE_RATE_OUT    0x41
+#define SBC_SAMPLE_RATE_IN     0x42
+#define SBC_MONO_8BIT          0xa0
+#define SBC_MONO_16BIT         0xa4
+#define SBC_STEREO_8BIT        0xa8
+#define SBC_STEREO_16BIT       0xac
+
+#define SBC4_OUT8_AI           0xc6
+#define SBC4_IN8_AI            0xce
+#define SBC4_MODE_UNS_MONO     0x00
+#define SBC4_MODE_SIGN_MONO    0x10
+
+#define SBC4_OUT16_AI          0xb6
+#define SBC4_IN16_AI           0xbe
+#define SBC4_OUT16_AI_NO_FIFO  0xb4
+#define SBC4_IN16_AI_NO_FIFO   0xbc
+
+/* --------------------------------------------------------------------- */
+
+extern const struct hfmodem_scops sbc4_scops;
+extern const struct hfmodem_scops ess_scops;
+
+/* --------------------------------------------------------------------- */
+
+static int reset_dsp(struct hfmodem_state *dev)
+{
+        int i;
+
+        outb(1, DSP_RESET(dev->io.base_addr));
+	udelay(3);
+        outb(0, DSP_RESET(dev->io.base_addr));
+        for (i = 0; i < 0xffff; i++)
+                if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80)
+                        if (inb(DSP_READ_DATA(dev->io.base_addr)) == 0xaa)
+                                return 1;
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void write_dsp(struct hfmodem_state *dev, unsigned char data)
+{
+        int i;
+        
+        for (i = 0; i < 0xffff; i++)
+                if (!(inb(DSP_WRITE_STATUS(dev->io.base_addr)) & 0x80)) {
+                        outb(data, DSP_WRITE_DATA(dev->io.base_addr));
+                        return;
+                }
+}
+
+/* --------------------------------------------------------------------- */
+
+static int read_dsp(struct hfmodem_state *dev, unsigned char *data)
+{
+        int i;
+
+        if (!data)
+                return 0;
+        for (i = 0; i < 0xffff; i++) 
+                if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80) {
+                        *data = inb(DSP_READ_DATA(dev->io.base_addr));
+                        return 1;
+                }
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void write_ess(struct hfmodem_state *dev, unsigned char reg, unsigned char data)
+{
+	write_dsp(dev, reg);
+	write_dsp(dev, data);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int read_ess(struct hfmodem_state *dev, unsigned char reg, unsigned char *data)
+{
+	write_dsp(dev, 0xc0);
+	write_dsp(dev, reg);
+	return read_dsp(dev, data);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int reset_ess(struct hfmodem_state *dev)
+{
+        int i;
+
+        outb(3, DSP_RESET(dev->io.base_addr)); /* reset FIFOs too */
+	udelay(3);
+        outb(0, DSP_RESET(dev->io.base_addr));
+        for (i = 0; i < 0xffff; i++)
+                if (inb(DSP_DATA_AVAIL(dev->io.base_addr)) & 0x80)
+                        if (inb(DSP_READ_DATA(dev->io.base_addr)) == 0xaa) {
+				write_dsp(dev, ESS_EXTENDED_MODE);
+                                return 1;
+			}
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int config_resources(struct hfmodem_state *dev)
+{
+        unsigned char irqreg = 0, dmareg = 0, realirq, realdma;
+        unsigned long flags;
+
+        switch (dev->io.irq) {
+        case 2:
+        case 9:
+                irqreg |= 0x01;
+                break;
+
+        case 5:
+                irqreg |= 0x02;
+                break;
+
+        case 7:
+                irqreg |= 0x04;
+                break;
+
+        case 10:
+                irqreg |= 0x08;
+                break;
+                
+        default:
+                return -ENODEV;
+        }
+
+        switch (dev->io.dma) {
+        case 0:
+                dmareg |= 0x01;
+                break;
+
+        case 1:
+                dmareg |= 0x02;
+                break;
+
+        case 3:
+                dmareg |= 0x08;
+                break;
+
+	case 5:
+		dmareg |= 0x20;
+		break;
+		
+	case 6:
+		dmareg |= 0x40;
+		break;
+		
+	case 7:
+		dmareg |= 0x80;
+		break;
+		
+         default:
+                return -ENODEV;
+        }
+        save_flags(flags);
+        cli();
+        outb(0x80, DSP_MIXER_ADDR(dev->io.base_addr));
+        outb(irqreg, DSP_MIXER_DATA(dev->io.base_addr));
+        realirq = inb(DSP_MIXER_DATA(dev->io.base_addr));
+        outb(0x81, DSP_MIXER_ADDR(dev->io.base_addr));
+        outb(dmareg, DSP_MIXER_DATA(dev->io.base_addr));
+        realdma = inb(DSP_MIXER_DATA(dev->io.base_addr));
+        restore_flags(flags);
+        if ((~realirq) & irqreg || (~realdma) & dmareg) {
+                printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device "
+                       "and IRQ/DMA specified wrongly?\n", hfmodem_drvname);
+                return -EINVAL;
+        }
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void sbc_int_ack_8bit(struct hfmodem_state *dev)
+{
+        inb(DSP_DATA_AVAIL(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void sbc_int_ack_16bit(struct hfmodem_state *dev)
+{
+        inb(DSP_INTACK_16BIT(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void set_mixer(struct hfmodem_state *dev, unsigned char reg, unsigned char data)
+{
+	outb(reg, DSP_MIXER_ADDR(dev->io.base_addr));
+	outb(data, DSP_MIXER_DATA(dev->io.base_addr));
+}	
+
+/* --------------------------------------------------------------------- */
+
+int hfmodem_sbcprobe(struct hfmodem_state *dev)
+{
+	unsigned char revhi, revlo, essrevhi, essrevlo, tmp;
+	int ret;
+
+	if (dev->io.base_addr <= 0 || dev->io.base_addr > 0x1000-SBC_EXTENT || 
+            dev->io.irq < 2 || dev->io.irq > 15 || dev->io.dma > 7 || dev->io.dma == 2)
+                return -ENXIO;
+        if (check_region(dev->io.base_addr, SBC_EXTENT))
+                return -EACCES;
+        /*
+         * check if a card is available
+         */
+        if (!reset_dsp(dev)) {
+                printk(KERN_ERR "%s: sbc: no card at io address 0x%x\n", 
+		       hfmodem_drvname, dev->io.base_addr);
+                return -ENODEV;
+        }
+	set_mixer(dev, 0, 0); /* reset mixer */
+        write_dsp(dev, SBC_GET_REVISION);
+        if (!read_dsp(dev, &revhi) || !read_dsp(dev, &revlo))
+                return -ENODEV;
+        printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%02d\n", hfmodem_drvname, revhi, revlo);
+	if (revhi == 3 && revlo == 1) {
+		write_dsp(dev, ESS_GET_REVISION);
+		if (!read_dsp(dev, &essrevhi) || !read_dsp(dev, &essrevlo))
+			return -ENODEV;
+		if (essrevhi == 0x48 && (essrevlo & 0xf0) == 0x80) {
+			printk(KERN_INFO "%s: ESS ES488 AudioDrive (rev %d): unsupported.\n",
+			       hfmodem_drvname, essrevlo & 0x0f);
+			return -ENODEV;
+		}
+		if (essrevhi == 0x68 && (essrevlo & 0xf0) == 0x80) {
+			printk(KERN_INFO "%s: ESS ES%s688 AudioDrive (rev %d)\n",
+			       hfmodem_drvname, ((essrevlo & 0x0f) >= 8) ? "1" : "", essrevlo & 0x0f);
+			if (dev->io.dma > 3) {
+				printk(KERN_INFO "%s: DMA number out of range\n", hfmodem_drvname);
+				return -ENXIO;
+			}
+			printk(KERN_INFO "%s: ess: irq: ", hfmodem_drvname);
+			read_ess(dev, 0xb1, &tmp);
+			switch (tmp & 0xf) {
+			case 0:
+				printk("2, 9, \"all others\"");
+				break;
+			       
+			case 5:
+				printk("5");
+				break;
+
+			case 10:
+				printk("7");
+				break;
+
+			case 15:
+				printk("10");
+				break;
+
+			default:
+				printk("unknown (%d)", tmp & 0xf);
+				break;
+			}
+			printk(" dma: ");
+			read_ess(dev, 0xb2, &tmp);
+			switch (tmp & 0xf) {
+			case 0:
+				printk("\"all others\"");
+				break;
+			       
+			case 5:
+				printk("0");
+				break;
+
+			case 10:
+				printk("1");
+				break;
+
+			case 15:
+				printk("3");
+				break;
+
+			default:
+				printk("unknown (%d)", tmp & 0xf);
+				break;
+			}
+			printk("\n");
+			dev->scops = &ess_scops;
+			return 0;
+		}
+	}
+	if (revhi < 4) {
+		printk(KERN_INFO "%s: at least SB16 required\n", hfmodem_drvname);
+		return -ENODEV;
+	}
+	if (dev->io.dma < 4) {
+		printk(KERN_INFO "%s: DMA number out of range\n", hfmodem_drvname);
+		return -ENXIO;
+	}
+	if ((ret = config_resources(dev)))
+		return ret;
+	dev->scops = &sbc4_scops;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_init(struct hfmodem_state *dev)
+{
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_prepare_input(struct hfmodem_state *dev)
+{
+	unsigned long flags;
+
+        if (!reset_dsp(dev)) {
+                printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", hfmodem_drvname);
+                return;
+        }
+        save_flags(flags);
+        cli();
+        disable_dma(dev->io.dma);
+        clear_dma_ff(dev->io.dma);
+        set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+        set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+        set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+        enable_dma(dev->io.dma);
+        sbc_int_ack_16bit(dev);
+        write_dsp(dev, SBC_SAMPLE_RATE_IN); /* set sampling rate */
+        write_dsp(dev, HFMODEM_SRATE >> 8);
+        write_dsp(dev, HFMODEM_SRATE & 0xff);
+        write_dsp(dev, SBC_SPEAKER_OFF); 
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_trigger_input(struct hfmodem_state *dev)
+{
+	unsigned long flags;
+
+        save_flags(flags);
+        cli();
+	write_dsp(dev, SBC4_IN16_AI_NO_FIFO);
+	write_dsp(dev, SBC4_MODE_UNS_MONO);
+	write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) & 0xff);
+	write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) >> 8);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_prepare_output(struct hfmodem_state *dev)
+{
+	unsigned long flags;
+
+        if (!reset_dsp(dev)) {
+                printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", hfmodem_drvname);
+                return;
+        }
+        save_flags(flags);
+        cli();
+        disable_dma(dev->io.dma);
+        clear_dma_ff(dev->io.dma);
+        set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+        set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+        set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+        enable_dma(dev->io.dma);
+        sbc_int_ack_16bit(dev);
+        write_dsp(dev, SBC_SAMPLE_RATE_OUT); /* set sampling rate */
+        write_dsp(dev, HFMODEM_SRATE >> 8);
+        write_dsp(dev, HFMODEM_SRATE & 0xff);
+        write_dsp(dev, SBC_SPEAKER_ON); 
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_trigger_output(struct hfmodem_state *dev)
+{
+	unsigned long flags;
+
+        save_flags(flags);
+        cli();
+	write_dsp(dev, SBC4_OUT16_AI_NO_FIFO);
+	write_dsp(dev, SBC4_MODE_UNS_MONO);
+	write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) & 0xff);
+	write_dsp(dev, (HFMODEM_FRAGSAMPLES-1) >> 8);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_stop(struct hfmodem_state *dev)
+{
+	reset_dsp(dev);
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned int sbc4_intack(struct hfmodem_state *dev)
+{
+	unsigned int dmaptr;
+	unsigned long flags;
+	unsigned char intsrc;
+
+	save_flags(flags);
+        cli();
+        outb(0x82, DSP_MIXER_ADDR(dev->io.base_addr));
+        intsrc = inb(DSP_MIXER_DATA(dev->io.base_addr));
+        if (intsrc & 0x01) 
+                sbc_int_ack_8bit(dev);
+        if (intsrc & 0x02)
+		sbc_int_ack_16bit(dev);
+        disable_dma(dev->io.dma);
+        clear_dma_ff(dev->io.dma);
+        dmaptr = get_dma_residue(dev->io.dma);
+        enable_dma(dev->io.dma);
+	restore_flags(flags);
+	if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE)
+		dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE;
+	return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc4_mixer(struct hfmodem_state *dev, int src, int igain, int ogain)
+{
+	unsigned long flags;
+	static const unsigned char srcbits[3] = { 0x18, 0x01, 0x06 };
+
+	save_flags(flags);
+	cli();
+	if (src >= 0 && src <= 2) {
+		set_mixer(dev, 0x3d, srcbits[src]);
+		set_mixer(dev, 0x3e, srcbits[src]);
+	}
+	if (ogain >= 0 && ogain <= 255) {
+		set_mixer(dev, 0x30, ogain);
+		set_mixer(dev, 0x31, ogain);
+	}
+	if (igain >= 0 && igain <= 255) {
+		set_mixer(dev, 0x36, igain);
+		set_mixer(dev, 0x37, igain);
+		set_mixer(dev, 0x38, igain);
+		set_mixer(dev, 0x39, igain);
+		set_mixer(dev, 0x3a, igain);
+	}
+	set_mixer(dev, 0x32, 0xff);
+	set_mixer(dev, 0x33, 0xff);
+	set_mixer(dev, 0x34, 0);
+	set_mixer(dev, 0x35, 0);
+	set_mixer(dev, 0x3b, 0); /* pc spkr vol */
+	set_mixer(dev, 0x3c, 0); /* output src */
+	set_mixer(dev, 0x3f, 0); /* inp gain */
+	set_mixer(dev, 0x40, 0);
+	set_mixer(dev, 0x41, 0); /* outp gain */
+	set_mixer(dev, 0x42, 0);
+	set_mixer(dev, 0x43, 1); /* mic agc off */
+	set_mixer(dev, 0x44, 8<<4); /* treble */
+	set_mixer(dev, 0x45, 8<<4); 
+	set_mixer(dev, 0x46, 8<<4); /* bass */
+	set_mixer(dev, 0x47, 8<<4); 	
+	restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ess_prepare_input(struct hfmodem_state *dev)
+{
+	unsigned long flags;
+	unsigned char tmp;
+
+        if (!reset_ess(dev)) {
+                printk(KERN_ERR "%s: sbc: cannot reset ess dsp\n", hfmodem_drvname);
+                return;
+        }
+        save_flags(flags);
+        cli();
+        disable_dma(dev->io.dma);
+        clear_dma_ff(dev->io.dma);
+        set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+        set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+        set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+        enable_dma(dev->io.dma);
+        sbc_int_ack_8bit(dev);
+	write_ess(dev, 0xa1, 128 - (397700 + HFMODEM_SRATE/2) / HFMODEM_SRATE);
+	/*
+	 * Set filter divider register
+	 * Rolloff at 90% of the half sampling rate
+	 */
+	write_ess(dev, 0xa2, 256-(7160000 / (82 * (HFMODEM_SRATE * 9 / 20))));
+        write_dsp(dev, SBC_SPEAKER_OFF); 
+	write_ess(dev, 0xb8, 0x0e); /* Auto init DMA mode */
+	read_ess(dev, 0xa8, &tmp);
+	write_ess(dev, 0xa8, (tmp & ~0x03) | 2);     /* Mono */
+	write_ess(dev, 0xb9, 2);    /* Demand mode (4 bytes/DMA request) */
+	/* 16 bit mono */
+	write_ess(dev, 0xb7, 0x71);
+	write_ess(dev, 0xb7, 0xf4);
+
+	read_ess(dev, 0xb1, &tmp);
+	write_ess(dev, 0xb1, (tmp & 0x0f) | 0x50);
+	read_ess(dev, 0xb2, &tmp);
+	write_ess(dev, 0xb2, (tmp & 0x0f) | 0x50);
+ 
+	write_ess(dev, 0xa4, (unsigned char) ((-HFMODEM_FRAGSIZE) & 0xff));
+	write_ess(dev, 0xa5, (unsigned char) (((-HFMODEM_FRAGSIZE) >> 8) & 0xff));
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ess_trigger_input(struct hfmodem_state *dev)
+{
+	unsigned long flags;
+	unsigned char tmp;
+
+        save_flags(flags);
+        cli();
+	read_ess(dev, 0xb8, &tmp);
+	write_ess(dev, 0xb8, tmp | 0x0f);         /* Go */
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+void ess_prepare_output(struct hfmodem_state *dev)
+{
+	unsigned long flags;
+	unsigned char tmp;
+
+        if (!reset_ess(dev)) {
+                printk(KERN_ERR "%s: sbc: cannot reset ess dsp\n", hfmodem_drvname);
+                return;
+        }
+        save_flags(flags);
+        cli();
+        disable_dma(dev->io.dma);
+        clear_dma_ff(dev->io.dma);
+        set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+        set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+        set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+        enable_dma(dev->io.dma);
+        sbc_int_ack_8bit(dev);
+	write_ess(dev, 0xa1, 128 - (397700 + HFMODEM_SRATE/2) / HFMODEM_SRATE);
+	/*
+	 * Set filter divider register
+	 * Rolloff at 90% of the half sampling rate
+	 */
+	write_ess(dev, 0xa2, 256-(7160000 / (82 * (HFMODEM_SRATE * 9 / 20))));
+	write_ess(dev, 0xb8, 0x04); /* Auto init DMA mode */
+	read_ess(dev, 0xa8, &tmp);
+	write_ess(dev, 0xa8, (tmp & ~0x03) | 2);     /* Mono */
+	write_ess(dev, 0xb9, 2);    /* Demand mode (4 bytes/DMA request) */
+	/* 16 bit mono */
+	write_ess(dev, 0xb6, 0x00);
+	write_ess(dev, 0xb7, 0x71);
+	write_ess(dev, 0xb7, 0xf4);
+
+	read_ess(dev, 0xb1, &tmp);
+	write_ess(dev, 0xb1, (tmp & 0x0f) | 0x50);
+	read_ess(dev, 0xb2, &tmp);
+	write_ess(dev, 0xb2, (tmp & 0x0f) | 0x50);
+
+	write_ess(dev, 0xa4, (unsigned char) ((-HFMODEM_FRAGSIZE) & 0xff));
+	write_ess(dev, 0xa5, (unsigned char) (((-HFMODEM_FRAGSIZE) >> 8) & 0xff));
+
+        write_dsp(dev, SBC_SPEAKER_ON); 
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+void ess_trigger_output(struct hfmodem_state *dev)
+{
+	unsigned long flags;
+	unsigned char tmp;
+
+        save_flags(flags);
+        cli();
+	read_ess(dev, 0xb8, &tmp);
+	write_ess(dev, 0xb8, tmp | 0x05);         /* Go */
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+unsigned int ess_intack(struct hfmodem_state *dev)
+{
+	unsigned int dmaptr;
+	unsigned long flags;
+	unsigned char st;
+#if 0
+	static unsigned int cnt = 0;
+#endif
+
+	save_flags(flags);
+        cli();
+	st = inb(DSP_WRITE_STATUS(dev->io.base_addr));
+	sbc_int_ack_8bit(dev);
+        disable_dma(dev->io.dma);
+        clear_dma_ff(dev->io.dma);
+        dmaptr = get_dma_residue(dev->io.dma);
+        enable_dma(dev->io.dma);
+	restore_flags(flags);
+#if 0
+	cnt = (cnt + 1) & 0x3f;
+	if (!cnt)
+		printk(KERN_DEBUG "%s: ess: FIFO: full:%c empty:%c half empty:%c  IRQ: cpu:%c half empty:%c DMA:%c\n",
+		       hfmodem_drvname, '1'-!(st&0x20), '1'-!(st&0x10), '1'-!(st&0x8), 
+		       '1'-!(st&0x4), '1'-!(st&0x2), '1'-!(st&0x1));
+#endif
+	if (st & 0x20) /* FIFO full, 256 bytes */
+		dmaptr += 256;
+	else if (!(st & 0x10)) /* FIFO not empty, assume half full 128 bytes */
+		dmaptr += 128;
+	if (dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE)
+		dmaptr -= HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE;
+	if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE)
+		dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE;
+	return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ess_mixer(struct hfmodem_state *dev, int src, int igain, int ogain)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (src >= 0 && src <= 2) 
+		set_mixer(dev, 0x0c, ((src+3) & 3) << 1);
+	if (ogain >= 0 && ogain <= 255)
+		set_mixer(dev, 0x22, (ogain & 0xf0) | ((ogain >> 4) & 0xf));
+	if (igain >= 0 && igain <= 255) {
+		set_mixer(dev, 0x36, igain);
+		set_mixer(dev, 0x37, igain);
+		set_mixer(dev, 0x38, igain);
+		set_mixer(dev, 0x39, igain);
+		set_mixer(dev, 0x3a, igain);
+	}
+	set_mixer(dev, 0x4, 0xff);
+	set_mixer(dev, 0xe, 0x0);
+	set_mixer(dev, 0x26, 0);
+	set_mixer(dev, 0x28, 0);
+	set_mixer(dev, 0x2e, 0);
+	restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const struct hfmodem_scops sbc4_scops = {
+	SBC_EXTENT, sbc4_init, sbc4_prepare_input, sbc4_trigger_input, 
+	sbc4_prepare_output, sbc4_trigger_output, sbc4_stop, sbc4_intack, sbc4_mixer
+};
+
+static const struct hfmodem_scops ess_scops = {
+	SBC_EXTENT, sbc4_init, ess_prepare_input, ess_trigger_input, 
+	ess_prepare_output, ess_trigger_output, sbc4_stop, ess_intack, ess_mixer
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.48/linux/drivers/char/hfmodem/wss.c linux/drivers/char/hfmodem/wss.c
--- v2.1.48/linux/drivers/char/hfmodem/wss.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/hfmodem/wss.c	Tue Aug  5 09:48:55 1997
@@ -0,0 +1,437 @@
+/*****************************************************************************/
+
+/*
+ *      wss.c  --  Linux soundcard HF FSK driver, 
+ *                 WindowsSoundSystem specific functions.
+ *
+ *      Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *        Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+/*****************************************************************************/
+     
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+ 
+#include <linux/hfmodem.h>
+
+/* --------------------------------------------------------------------- */
+
+#define WSS_CONFIG(iobase)       (iobase+0)
+#define WSS_STATUS(iobase)       (iobase+3)
+#define WSS_CODEC_IA(iobase)     (iobase+4)
+#define WSS_CODEC_ID(iobase)     (iobase+5)
+#define WSS_CODEC_STATUS(iobase) (iobase+6)
+#define WSS_CODEC_DATA(iobase)   (iobase+7)
+
+#define WSS_EXTENT   8
+
+/* --------------------------------------------------------------------- */
+
+extern const struct hfmodem_scops wss_scops;
+
+/* --------------------------------------------------------------------- */
+
+static void write_codec(struct hfmodem_state *dev, unsigned char idx,
+                        unsigned char data)
+{
+        int timeout = 900000;
+
+        /* wait until codec ready */
+        while (timeout > 0 && inb(WSS_CODEC_IA(dev->io.base_addr)) & 0x80)
+                timeout--;
+        outb(idx, WSS_CODEC_IA(dev->io.base_addr));
+        outb(data, WSS_CODEC_ID(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned char read_codec(struct hfmodem_state *dev, unsigned char idx)
+{
+        int timeout = 900000;
+
+        /* wait until codec ready */
+        while (timeout > 0 && inb(WSS_CODEC_IA(dev->io.base_addr)) & 0x80)
+                timeout--;
+        outb(idx & 0x1f, WSS_CODEC_IA(dev->io.base_addr));
+        return inb(WSS_CODEC_ID(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void wss_ack_int(struct hfmodem_state *dev)
+{
+        outb(0, WSS_CODEC_STATUS(dev->io.base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_srate_tab[16] = {
+        8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050,
+        -1, 37800, -1, 44100, 48000, 33075, 9600, 6620
+};
+
+static int wss_srate_index(int srate)
+{
+        int i;
+
+        for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++)
+                if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0)
+                        return i;
+        return -1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_set_codec_fmt(struct hfmodem_state *dev, unsigned char fmt)
+{
+        unsigned long time;
+        unsigned long flags;
+
+        save_flags(flags);
+        cli();
+        /* Clock and data format register */
+        write_codec(dev, 0x48, fmt);
+	/* MCE and interface config reg */
+	write_codec(dev, 0x49, 0xc);
+        outb(0xb, WSS_CODEC_IA(dev->io.base_addr)); /* leave MCE */
+        /*
+         * wait for ACI start
+         */
+        time = 1000;
+        while (!(read_codec(dev, 0x0b) & 0x20))
+                if (!(--time)) {
+                        printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n", 
+                               hfmodem_drvname);
+                        restore_flags(flags);
+                        return -1;
+                }
+        /*
+         * wait for ACI end
+         */
+        sti();
+        time = jiffies + HZ/4;
+        while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0));
+        restore_flags(flags);
+        if ((signed)(jiffies - time) >= 0) {
+                printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n", 
+                       hfmodem_drvname);
+                return -1;
+        }
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_init_codec(struct hfmodem_state *dev)
+{
+        unsigned char tmp, revwss, revid;
+        static const signed char irqtab[16] = { 
+		-1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1, -1, -1 
+	};
+        static const signed char dmatab[4] = { 1, 2, -1, 3 };
+	int fmt;
+        
+	if ((fmt = wss_srate_index(HFMODEM_SRATE)) < 0) {
+		printk(KERN_ERR "%s: WSS: sampling rate not supported\n", hfmodem_drvname);
+		return -1;
+	}
+	fmt &= 0x0f;
+#ifdef __BIG_ENDIAN
+	fmt |= 0xc0;
+#else /* __BIG_ENDIAN */
+	fmt |= 0x40;
+#endif /* __BIG_ENDIAN */
+        tmp = inb(WSS_STATUS(dev->io.base_addr));
+        if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 && 
+            (tmp & 0x3f) != 0x0f) {
+                printk(KERN_WARNING "%s: WSS card id register not found, "
+                       "address 0x%x, ID register 0x%02x\n", hfmodem_drvname,
+                       dev->io.base_addr, (int)tmp);
+                /* return -1; */
+                revwss = 0;
+        } else {
+                if ((tmp & 0x80) && ((dev->io.dma == 0) || ((dev->io.irq >= 8) && (dev->io.irq != 9)))) {
+                        printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 "
+                               "(except IRQ9) cannot be used on an 8bit "
+                               "card\n", hfmodem_drvname);
+                        return -1;
+                }               
+                if (dev->io.irq > 15 || irqtab[dev->io.irq] == -1) {
+                        printk(KERN_ERR "%s: WSS: invalid interrupt %d\n", 
+                               hfmodem_drvname, (int)dev->io.irq);
+                        return -1;
+                }
+                if (dev->io.dma > 3 || dmatab[dev->io.dma] == -1) {
+                        printk(KERN_ERR "%s: WSS: invalid dma channel %d\n", 
+                               hfmodem_drvname, (int)dev->io.dma);
+                        return -1;
+                }
+                tmp = irqtab[dev->io.irq] | dmatab[dev->io.dma];
+                /* irq probe */
+                outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->io.base_addr));
+                if (!(inb(WSS_STATUS(dev->io.base_addr)) & 0x40)) {
+                        outb(0, WSS_CONFIG(dev->io.base_addr));
+                        printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n", 
+                               hfmodem_drvname, dev->io.irq);
+                }
+                outb(tmp, WSS_CONFIG(dev->io.base_addr));
+                revwss = inb(WSS_STATUS(dev->io.base_addr)) & 0x3f;
+        }
+        /*
+         * initialize the codec
+         */
+        write_codec(dev, 9, 0);
+	write_codec(dev, 12, 0);
+        write_codec(dev, 0, 0x45);
+        if (read_codec(dev, 0) != 0x45)
+                goto codec_err;
+        write_codec(dev, 0, 0xaa);
+        if (read_codec(dev, 0) != 0xaa)
+                goto codec_err;
+        if (wss_set_codec_fmt(dev, fmt))
+                goto codec_err;
+        write_codec(dev, 0, 0x40); /* left input control */
+        write_codec(dev, 1, 0x40); /* right input control */
+        write_codec(dev, 2, 0x80); /* left aux#1 input control */
+        write_codec(dev, 3, 0x80); /* right aux#1 input control */
+        write_codec(dev, 4, 0x80); /* left aux#2 input control */
+        write_codec(dev, 5, 0x80); /* right aux#2 input control */
+        write_codec(dev, 6, 0x80); /* left dac control */
+        write_codec(dev, 7, 0x80); /* right dac control */
+        write_codec(dev, 0xa, 0x2); /* pin control register */
+        write_codec(dev, 0xd, 0x0); /* digital mix control */
+        revid = read_codec(dev, 0xc) & 0xf;
+        /*
+         * print revisions
+         */        
+	printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n", 
+	       hfmodem_drvname, (int)revwss, (int)revid);
+        return 0;
+ codec_err:
+        outb(0, WSS_CONFIG(dev->io.base_addr));
+        printk(KERN_ERR "%s: no WSS soundcard found at address 0x%x\n", 
+               hfmodem_drvname, dev->io.base_addr);
+        return -1;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hfmodem_wssprobe(struct hfmodem_state *dev)
+{
+	if (dev->io.base_addr <= 0 || dev->io.base_addr > 0x1000-WSS_EXTENT || 
+            dev->io.irq < 2 || dev->io.irq > 15 || dev->io.dma > 3 || dev->io.dma == 2)
+                return -ENXIO;
+        if (check_region(dev->io.base_addr, WSS_EXTENT))
+                return -EACCES;
+        /*
+         * check if a card is available
+         */
+        if (wss_init_codec(dev)) {
+                printk(KERN_ERR "%s: sbc: no card at io address 0x%x\n", 
+		       hfmodem_drvname, dev->io.base_addr);
+                return -ENODEV;
+        }
+	dev->scops = &wss_scops;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_init(struct hfmodem_state *dev)
+{
+	wss_init_codec(dev);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_stop(struct hfmodem_state *dev)
+{
+	unsigned long flags;
+        unsigned char oldcodecmode;
+        long abrt;
+
+        save_flags(flags);
+        cli();
+        /*
+         * perform the final DMA sequence to disable the codec request
+         */
+        oldcodecmode = read_codec(dev, 9);
+        write_codec(dev, 9, 0xc); /* disable codec */
+        wss_ack_int(dev);
+        if (read_codec(dev, 11) & 0x10) {
+                disable_dma(dev->io.dma);
+                clear_dma_ff(dev->io.dma);
+                set_dma_mode(dev->io.dma, (oldcodecmode & 1) ? 
+			     (DMA_MODE_WRITE | DMA_MODE_AUTOINIT) : (DMA_MODE_READ | DMA_MODE_AUTOINIT));
+                set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+                set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+                enable_dma(dev->io.dma);
+                abrt = 0;
+                while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000));
+        }
+        disable_dma(dev->io.dma);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_prepare_input(struct hfmodem_state *dev)
+{
+	unsigned long flags;
+
+	wss_stop(dev);
+	save_flags(flags);
+        cli();
+        disable_dma(dev->io.dma);
+        clear_dma_ff(dev->io.dma);
+        set_dma_mode(dev->io.dma, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+        set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+        set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+        enable_dma(dev->io.dma);
+        write_codec(dev, 15, (HFMODEM_FRAGSAMPLES-1) & 0xff);
+        write_codec(dev, 14, (HFMODEM_FRAGSAMPLES-1) >> 8);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_trigger_input(struct hfmodem_state *dev)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+        cli();
+        write_codec(dev, 9, 0x0e);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_prepare_output(struct hfmodem_state *dev)
+{
+	unsigned long flags;
+
+	wss_stop(dev);
+        save_flags(flags);
+        cli();
+        disable_dma(dev->io.dma);
+        clear_dma_ff(dev->io.dma);
+        set_dma_mode(dev->io.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+        set_dma_addr(dev->io.dma, virt_to_bus(dev->dma.buf));
+        set_dma_count(dev->io.dma, HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE);
+        enable_dma(dev->io.dma);
+        write_codec(dev, 15, (HFMODEM_FRAGSAMPLES-1) & 0xff);
+        write_codec(dev, 14, (HFMODEM_FRAGSAMPLES-1) >> 8);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_trigger_output(struct hfmodem_state *dev)
+{
+	unsigned long flags;
+
+        save_flags(flags);
+        cli();
+        write_codec(dev, 9, 0x0d);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned int wss_intack(struct hfmodem_state *dev)
+{
+	unsigned int dmaptr, nums;
+	unsigned long flags;
+
+	save_flags(flags);
+        cli();
+	wss_ack_int(dev);
+        disable_dma(dev->io.dma);
+        clear_dma_ff(dev->io.dma);
+        dmaptr = get_dma_residue(dev->io.dma);
+	if (dmaptr == 0 || dmaptr > HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE)
+		dmaptr = HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE;
+	nums = (((dmaptr - 1) % HFMODEM_FRAGSIZE) - 1) / 2;
+        write_codec(dev, 15, nums  & 0xff);
+        write_codec(dev, 14, nums >> 8);
+        enable_dma(dev->io.dma);
+	restore_flags(flags);
+	return (HFMODEM_NUMFRAGS * HFMODEM_FRAGSIZE - dmaptr) / 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_mixer(struct hfmodem_state *dev, int src, int igain, int ogain)
+{
+	unsigned long flags;
+	static const unsigned char srctoreg[3] = { 1, 2, 0 };
+	static const unsigned char regtosrc[4] = { 2, 0, 1, 0 };
+	unsigned char tmp;
+
+	save_flags(flags);
+	cli();
+	tmp = read_codec(dev, 0x00);
+	if (src < 0 || src > 2)
+		src = regtosrc[(tmp >> 6) & 3];
+	if (igain < 0 || igain > 255) {
+		if (src == 1)
+			igain = ((tmp & 0xf) + ((tmp & 0x20) ? 13 : 0)) << 3;
+		else
+			igain = (tmp & 0xf) << 4;
+	}
+	if (src == 1) {
+		if (igain > (28<<3))
+			tmp = 0x2f;
+		else if (igain >= (13<<3))
+			tmp = 0x20 + (((igain >> 3) - 13) & 0xf);
+		else 
+			tmp = (igain >> 3) & 0xf;
+	} else 
+		tmp = (igain >> 4) & 0xf;
+	tmp |= srctoreg[src] << 6;
+	write_codec(dev, 0, tmp);
+	write_codec(dev, 1, tmp);
+	if (ogain > 0 && ogain <= 255) {
+		tmp = 63 - (ogain >> 2);
+		write_codec(dev, 6, tmp);
+		write_codec(dev, 7, tmp);
+	} else if (ogain == 0) {
+		write_codec(dev, 6, 0x80);
+		write_codec(dev, 7, 0x80);
+	}
+	restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const struct hfmodem_scops wss_scops = {
+	WSS_EXTENT, wss_init, wss_prepare_input, wss_trigger_input, 
+	wss_prepare_output, wss_trigger_output, wss_stop, wss_intack, wss_mixer
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.48/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.1.48/linux/drivers/char/misc.c	Mon Aug  4 16:25:37 1997
+++ linux/drivers/char/misc.c	Tue Aug  5 09:48:55 1997
@@ -246,6 +246,9 @@
 #ifdef CONFIG_NVRAM
 	nvram_init();
 #endif
+#ifdef CONFIG_HFMODEM
+	hfmodem_init();
+#endif
 #endif /* !MODULE */
 	if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) {
 		printk("unable to get major %d for misc devices\n",
diff -u --recursive --new-file v2.1.48/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.1.48/linux/drivers/char/serial.c	Thu Jun 26 12:33:39 1997
+++ linux/drivers/char/serial.c	Tue Aug  5 09:24:32 1997
@@ -3503,7 +3503,7 @@
  * Print a string to the serial port trying not to disturb any possible
  * real use of the port...
  */
-static int serial_console_write(const char *s, unsigned count)
+static void serial_console_write(const char *s, unsigned count)
 {
 	struct serial_state *ser;
 	int ier;
@@ -3538,14 +3538,12 @@
 	 */
 	wait_for_xmitr(ser);
 	outb(ier, ser->port + UART_IER);
-
-	return (0);
 }
 
 /*
  * Receive character from the serial port
  */
-static int serial_console_wait_key(void)
+static void serial_console_wait_key(void)
 {
 	struct serial_state *ser;
 	int ier;
@@ -3569,8 +3567,6 @@
 
 	/* Restore the interrupts */
 	outb(ier, ser->port + UART_IER);
-
-	return c;
 }
 
 static int serial_console_device(void)
diff -u --recursive --new-file v2.1.48/linux/drivers/char/vt.c linux/drivers/char/vt.c
--- v2.1.48/linux/drivers/char/vt.c	Mon Jun 16 16:35:55 1997
+++ linux/drivers/char/vt.c	Wed Aug  6 13:10:58 1997
@@ -490,8 +490,6 @@
 	case KIOCSOUND:
 		if (!perm)
 			return -EPERM;
-		if (arg)
-			arg = 1193180 / arg;
 		kd_mksound(arg, 0);
 		return 0;
 
@@ -506,8 +504,7 @@
 		 * If the time is zero, turn off sound ourselves.
 		 */
 		ticks = HZ * ((arg >> 16) & 0xffff) / 1000;
-		if ((arg & 0xffff) == 0 ) arg |= 1; /* jp: huh? */
-		count = ticks ? (1193180 / (arg & 0xffff)) : 0;
+		count = ticks ? arg : 0;
 		kd_mksound(count, ticks);
 		return 0;
 	}
diff -u --recursive --new-file v2.1.48/linux/drivers/isdn/avmb1/capi.c linux/drivers/isdn/avmb1/capi.c
--- v2.1.48/linux/drivers/isdn/avmb1/capi.c	Thu May 29 21:53:05 1997
+++ linux/drivers/isdn/avmb1/capi.c	Wed Aug  6 13:02:58 1997
@@ -253,7 +253,7 @@
 capi_poll(struct file *file, poll_table * wait)
 {
 	unsigned int mask = 0;
-	unsigned int minor = MINOR(file->f_inode->i_rdev);
+	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
 	struct capidev *cdev;
 
 	if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
diff -u --recursive --new-file v2.1.48/linux/drivers/isdn/hisax/l3_1tr6.c linux/drivers/isdn/hisax/l3_1tr6.c
--- v2.1.48/linux/drivers/isdn/hisax/l3_1tr6.c	Thu May 29 21:53:06 1997
+++ linux/drivers/isdn/hisax/l3_1tr6.c	Wed Aug  6 13:02:58 1997
@@ -700,7 +700,7 @@
 
 	if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) {
 		if (st->l3.debug & L3_DEB_PROTERR) {
-			sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %ld state %d",
+			sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d state %d",
 				(pr == DL_DATA) ? " " : "(broadcast) ",
 				skb->data[0], skb->len, st->l3.state);
 			l3_debug(st, tmp);
diff -u --recursive --new-file v2.1.48/linux/drivers/isdn/hisax/l3dss1.c linux/drivers/isdn/hisax/l3dss1.c
--- v2.1.48/linux/drivers/isdn/hisax/l3dss1.c	Thu May 29 21:53:06 1997
+++ linux/drivers/isdn/hisax/l3dss1.c	Wed Aug  6 13:02:58 1997
@@ -721,7 +721,7 @@
 
 	if (skb->data[0] != PROTO_DIS_EURO) {
 		if (st->l3.debug & L3_DEB_PROTERR) {
-			sprintf(tmp, "dss1up%sunexpected discriminator %x message len %ld state %d",
+			sprintf(tmp, "dss1up%sunexpected discriminator %x message len %d state %d",
 				(pr == DL_DATA) ? " " : "(broadcast) ",
 				skb->data[0], skb->len, st->l3.state);
 			l3_debug(st, tmp);
diff -u --recursive --new-file v2.1.48/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- v2.1.48/linux/drivers/isdn/isdn_common.c	Thu May 29 21:53:06 1997
+++ linux/drivers/isdn/isdn_common.c	Wed Aug  6 13:02:58 1997
@@ -1012,7 +1012,7 @@
 isdn_poll(struct file *file, poll_table * wait)
 {
 	unsigned int mask = 0;
-	unsigned int minor = MINOR(file->f_inode->i_rdev);
+	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
 	int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
 
 	if (minor == ISDN_MINOR_STATUS) {
diff -u --recursive --new-file v2.1.48/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
--- v2.1.48/linux/drivers/isdn/isdn_ppp.c	Thu May 29 21:53:06 1997
+++ linux/drivers/isdn/isdn_ppp.c	Wed Aug  6 13:02:58 1997
@@ -700,7 +700,7 @@
 	is = file->private_data;
 
 	if (is->debug & 0x2)
-		printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", MINOR(file->f_inode->i_rdev));
+		printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", MINOR(file->f_dentry->d_inode->i_rdev));
 
 	poll_wait(&is->wq, wait);
 
diff -u --recursive --new-file v2.1.48/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
--- v2.1.48/linux/drivers/misc/parport_pc.c	Mon Aug  4 16:25:37 1997
+++ linux/drivers/misc/parport_pc.c	Wed Aug  6 13:02:59 1997
@@ -44,10 +44,12 @@
 	return;
 }
 
+#if 0
 static void pc_write_epp(struct parport *p, unsigned int d)
 {
 	outb(d, p->base+EPPREG);
 }
+#endif
 
 static unsigned int pc_read_epp(struct parport *p)
 {
@@ -320,6 +322,7 @@
 		return dma;
 }
 
+#if 0
 /* Only called if port supports ECP mode.
  *
  * The only restriction on DMA channels is that it has to be
@@ -385,6 +388,8 @@
 	
 	return retv;
 }
+#endif
+
 /******************************************************
  *  MODE detection section:
  */
diff -u --recursive --new-file v2.1.48/linux/drivers/net/8390.c linux/drivers/net/8390.c
--- v2.1.48/linux/drivers/net/8390.c	Thu Jul 17 10:06:05 1997
+++ linux/drivers/net/8390.c	Tue Aug  5 09:24:32 1997
@@ -191,7 +191,8 @@
 	printk("%s: Tx request while isr active.\n",dev->name);
 	outb_p(ENISR_ALL, e8390_base + EN0_IMR);
 	ei_local->stat.tx_errors++;
-	return 1;
+	dev_kfree_skb(skb, FREE_WRITE);
+	return 0;
     }
     ei_local->irqlock = 1;
 
@@ -305,6 +306,7 @@
     }
     
     dev->interrupt = 1;
+    sti();
     
     /* Change to page 0 and read the intr status reg. */
     outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
diff -u --recursive --new-file v2.1.48/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.48/linux/drivers/net/Config.in	Thu Jul 17 10:06:05 1997
+++ linux/drivers/net/Config.in	Tue Aug  5 09:48:55 1997
@@ -161,6 +161,11 @@
 #        bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800
 #      fi
     fi
+    tristate 'Shortwave radio modem driver' CONFIG_HFMODEM
+    if [ "$CONFIG_HFMODEM" != "n" ]; then
+      bool 'HFmodem support for Soundblaster and compatible cards' CONFIG_HFMODEM_SBC
+      bool 'HFmodem support for WSS and Crystal cards' CONFIG_HFMODEM_WSS
+    fi
   fi
   tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
   tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN
diff -u --recursive --new-file v2.1.48/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.48/linux/drivers/net/Makefile	Thu Jul 17 10:06:05 1997
+++ linux/drivers/net/Makefile	Tue Aug  5 09:49:50 1997
@@ -692,6 +692,7 @@
 ifeq ($(CONFIG_SOUNDMODEM),y)
 ALL_SUB_DIRS += soundmodem
 SUB_DIRS += soundmodem
+L_OBJS += soundmodem/soundmodem.o
 CONFIG_HDLCDRV_BUILTIN = y
 else
   ifeq ($(CONFIG_SOUNDMODEM),m)
diff -u --recursive --new-file v2.1.48/linux/drivers/net/README.baycom linux/drivers/net/README.baycom
--- v2.1.48/linux/drivers/net/README.baycom	Wed Oct 30 04:15:12 1996
+++ linux/drivers/net/README.baycom	Tue Aug  5 09:49:50 1997
@@ -1,118 +0,0 @@
-		    LINUX DRIVER FOR BAYCOM MODEMS
-
-       Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
-
-This document describes the Linux Kernel Driver for simple Baycom style
-amateur radio modems. The driver supports the following modems:
-
-ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
-        of a modulator/demodulator chip, usually a TI TCM3105. The computer
-        is responsible for regenerating the receiver bit clock, as well as
-        for handling the HDLC protocol. The modem connects to a serial port,
-        hence the name. Since the serial port is not used as an async serial
-        port, the kernel driver for serial ports cannot be used, and this
-        driver only supports standard serial hardware (8250, 16450, 16550)
-
-par96:  This is a modem for 9600 baud FSK compatible to the G3RUH standard.
-        The modem does all the filtering and regenerates the receiver clock.
-        Data is transferred from and to the PC via a shift register.
-        The shift register is filled with 16 bits and an interrupt is signalled.
-        The PC then empties the shift register in a burst. This modem connects
-        to the parallel port, hence the name. The modem leaves the 
-        implementation of the HDLC protocol and the scrambler polynomial to
-        the PC.
-
-picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem
-        is protocol compatible to par96, but uses only three low power ICs
-        and can therefore be fed from the parallel port and does not require
-        an additional power supply. Furthermore, it incorporates a carrier
-        detect circuitry.
-
-All of the above modems only support half duplex communications. However,
-the driver supports the KISS (see below) fullduplex command. It then simply
-starts to send as soon as there's a packet to transmit and does not care
-about DCD, i.e. it starts to send even if there's someone else on the channel.
-This command is required by some implementations of the DAMA channel 
-access protocol.
-
-
-The Interface of the driver
-
-Unlike previous drivers, the driver is no longer a character device,
-but it is now a true kernel network interface. Installation is therefore
-simple. Once installed, four interfaces named bc[0-3] are available.
-sethdlc from the ax25 utilities may be used to set driver states etc.
-Users of userland AX.25 stacks may use the net2kiss utility (also available
-in the ax25 utilities package) to converts packets of a network interface
-to a KISS stream on a pseudo tty. There's also a patch available from
-me for WAMPES which allows attaching a kernel network interface directly.
-
-
-Configuring the driver
-
-Every time the driver is inserted into the kernel, it has to know which
-modems it should access at which ports. This can be done with the setbaycom
-utility. If you are only using one modem, you can also configure the
-driver from the insmod command line (or by means of an option line in
-/etc/conf.modules).
-
-Examples:
-  insmod baycom modem=1 iobase=0x3f8 irq=4 options=1
-  sethdlc -i bc0 -p type ser12 io 0x3f8 irq 4 options 1
-
-Both lines configure the first port to drive a ser12 modem at the first
-serial port (COM1 under DOS). options=1 instructs the driver to use
-the software DCD algorithm (see below).
-
-  insmod baycom modem=2 iobase=0x378 irq=7 options=1
-  sethdlc -i bc0 -p type par96 io 0x378 irq 7 options 1
-
-Both lines configure the first port to drive a par96 or par97 modem at the
-first parallel port (LPT1 under DOS). options=1 instructs the driver to use
-the software DCD algorithm (see below).
-  
-The channel access parameters can be set with sethdlc -a or kissparms.
-Note that both utilities interpret the values slightly different.
-
-
-Hardware DCD versus Software DCD
-
-To avoid collisions on the air, the driver must know when the channel is
-busy. This is the task of the DCD circuitry/software. The driver may either
-utilise a software DCD algorithm (options=1) or use a DCD signal from
-the hardware (options=0).
-
-ser12:  if software DCD is utilised, the radio's squelch should always be
-        open. It is highly recommended to use the software DCD algorithm,
-        as it is much faster than most hardware squelch circuitry. The
-        disadvantage is a slightly higher load on the system.
-
-par96:  the software DCD algorithm for this type of modem is rather poor.
-        The modem simply does not provide enough information to implement
-        a reasonable DCD algorithm in software. Therefore, if your radio
-        feeds the DCD input of the PAR96 modem, the use of the hardware
-        DCD circuitry is recommended.
-
-picpar: the picpar modem features a builtin DCD hardware, which is highly
-        recommended.
-
-
-
-Compatibility with the rest of the Linux kernel
-
-The serial driver, the line printer (lp) driver and the baycom driver compete
-for the same hardware resources. Of course only one driver can access a given
-interface at a time. The serial driver grabs all interfaces it can find at
-startup time. Therefore the baycom driver subsequently won't be able to
-access a serial port. You might therefore find it necessary to release
-a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where
-# is the number of the interface. The baycom driver does not reserve any
-port at startup, unless one is specified on the 'insmod' command line. Another
-method to solve the problem is to compile all three drivers as modules and
-leave it to kerneld to load the correct driver depending on the application.
-
-
-
-vy 73s de
-Tom Sailer, sailer@ife.ee.ethz.ch
-hb9jnx @ hb9w.ampr.org
diff -u --recursive --new-file v2.1.48/linux/drivers/net/README.soundmodem linux/drivers/net/README.soundmodem
--- v2.1.48/linux/drivers/net/README.soundmodem	Wed Oct 30 04:15:12 1996
+++ linux/drivers/net/README.soundmodem	Tue Aug  5 09:49:50 1997
@@ -1,88 +0,0 @@
-	     LINUX DRIVER FOR SOUNDCARDS AS AX.25 MODEMS
-
-       Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
-
-This driver allows either SoundBlaster (sbc) or WindowsSoundSystem (wss)
-compatible soundcards to be used as either 1200 baud AFSK or 9600 baud FSK
-AX.25 packet radio modems. Only half duplex operation is supported; an
-attempt to include full duplex support failed because the hardware did
-not support it (it appeared that the card only provides one DMA channel,
-although the Codec chip would support two channels). The driver needs
-some processing power! A 486DX/2 66MHz is a minimum requirement, otherwise
-interactive performance of the computer may become sluggish.
-
-
-The Interface of the driver
-
-The driver provides a kernel network drivers named sm[0-3]. sethdlc 
-from the ax25 utilities may be used to set driver states etc. Users
-of userland AX.25 stacks may use the net2kiss utility (also available
-in the ax25 utilities package) to converts packets of a network interface
-to a KISS stream on a pseudo tty. There's also a patch available from
-me for WAMPES which allows attaching a kernel network interface directly.
-
-
-Configuring the driver
-
-Some sound cards need to be initialized before they operate in either
-SoundBlaster or WSS compatibility mode. The driver does _NOT_ do this;
-you may use the standard linux sound driver to initialize the soundcard;
-compile it as a module, and do
-  insmod sound
-  rmmod sound
-The soundcard should then be initialized correctly. If this does not help,
-you'll have to write your own initialization utility.
-
-Every time the driver is inserted into the kernel, it has to know which
-modems it should access at which ports. This can be done with the setbaycom
-utility. If you are only using one modem, you can also configure the
-driver from the insmod command line (or by means of an option line in
-/etc/conf.modules).
-
-Examples:
-  insmod soundmodem hw=0 mode=0 iobase=0x220 irq=5 dma=1
-  sethdlc -i sm0 -p hw sbc type afsk1200 io 0x220 irq 5 dma 1
-
-Both lines configure the first port to drive a soundblaster card
-in 1200 baud AFSK mode.
-
-The channel access parameters can be set with sethdlc -a or kissparms.
-Note that both utilities interpret the values slightly different.
-
-
-Input and output levels
-
-It is important that the input and output levels are adjusted properly.
-There are two utilities, available in the ax25 utilities distribution,
-to facilitate this: smmixer and smdiag. smdiag allows you to display
-the input signal in an oscilloscope like display or an eye diagram.
-smmixer allows you to adjust input/output levels. See the respective
-man pages.
-
-
-Transmitter keying
-
-Since soundcards do not have a DC coupled output; PTT keying options include
-the following:
-* VOX circuitry
-* Serial port pin
-* Parallel port pin
-* MPU401 MIDI output via a retriggerable monoflop.
-Circuit schematics may be found at
-http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html.
-
-
-Compatibility with the rest of the Linux kernel
-
-The sound driver and the soundcard modem driver compete for the same
-hardware resources. Of course only one driver can access a given
-interface at a time. Worse yet, the sound driver grabs the soundcard
-at startup time. Therefore the soundcard modem driver subsequently won't
-be able to access the soundcard. You might therefore find it necessary to
-unload the sound driver before using the soundcard modem driver.
-
-
-
-vy 73s de
-Tom Sailer, sailer@ife.ee.ethz.ch
-hb9jnx @ hb9w.ampr.org
diff -u --recursive --new-file v2.1.48/linux/drivers/net/baycom.c linux/drivers/net/baycom.c
--- v2.1.48/linux/drivers/net/baycom.c	Mon Jun 16 16:35:55 1997
+++ linux/drivers/net/baycom.c	Tue Aug  5 09:49:50 1997
@@ -68,7 +68,8 @@
  *  History:
  *   0.1  26.06.96  Adapted from baycom.c and made network driver interface
  *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
- *   0.3  26.04.96  init code/data tagged
+ *   0.3  26.04.97  init code/data tagged
+ *   0.4  08.07.97  alternative ser12 decoding algorithm (uses delta CTS ints)
  */
 
 /*****************************************************************************/
@@ -149,12 +150,13 @@
  * modem options; bit mask
  */
 #define BAYCOM_OPTIONS_SOFTDCD  1
+#define BAYCOM_ALT_SER12
 
 /* --------------------------------------------------------------------- */
 
 static const char bc_drvname[] = "baycom";
 static const char bc_drvinfo[] = KERN_INFO "baycom: (C) 1996 Thomas Sailer, HB9JNX/AE4WA\n"
-KERN_INFO "baycom: version 0.3 compiled " __TIME__ " " __DATE__ "\n";
+KERN_INFO "baycom: version 0.4 compiled " __TIME__ " " __DATE__ "\n";
 
 /* --------------------------------------------------------------------- */
 
@@ -214,14 +216,20 @@
 		unsigned char flags;
 		unsigned int shreg;
 		struct modem_state_ser12 {
+			unsigned char tx_bit;
+			int dcd_sum0, dcd_sum1, dcd_sum2;
 			unsigned char last_sample;
-			unsigned char interm_sample;
-			unsigned int bit_pll;
+			unsigned char last_rxbit;
 			unsigned int dcd_shreg;
-			int dcd_sum0, dcd_sum1, dcd_sum2;
 			unsigned int dcd_time;
-			unsigned char last_rxbit;
-			unsigned char tx_bit;
+			unsigned int bit_pll;
+#ifdef BAYCOM_ALT_SER12
+			unsigned long last_jiffies;
+			unsigned int pll_time;
+			unsigned int txshreg;
+#else /* BAYCOM_ALT_SER12 */
+			unsigned char interm_sample;
+#endif /* BAYCOM_ALT_SER12 */
 		} ser12;
 		struct modem_state_par96 {
 			int dcd_count;
@@ -272,6 +280,183 @@
  * ===================== SER12 specific routines =========================
  */
 
+#ifdef BAYCOM_ALT_SER12
+
+#define SER12_BAUD 1200
+
+/* --------------------------------------------------------------------- */
+
+extern inline unsigned int hweight16(unsigned short w)
+        __attribute__ ((unused));
+extern inline unsigned int hweight8(unsigned char w)
+        __attribute__ ((unused));
+
+extern inline unsigned int hweight16(unsigned short w)
+{
+        unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
+        res = (res & 0x3333) + ((res >> 2) & 0x3333);
+        res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
+        return (res & 0x00FF) + ((res >> 8) & 0x00FF);
+}
+
+extern inline unsigned int hweight8(unsigned char w)
+{
+        unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
+        res = (res & 0x33) + ((res >> 2) & 0x33);
+        return (res & 0x0F) + ((res >> 4) & 0x0F);
+}
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void ser12_rxsample(struct device *dev, struct baycom_state *bc, unsigned char news)
+{
+	bc->modem.ser12.dcd_shreg <<= 1;
+	bc->modem.ser12.bit_pll += 0x2000;
+	if (bc->modem.ser12.last_sample != news) {
+		bc->modem.ser12.last_sample = news;
+		bc->modem.ser12.dcd_shreg |= 1;
+		if (bc->modem.ser12.bit_pll < 0x9000)
+			bc->modem.ser12.bit_pll += 0x1000;
+		else
+			bc->modem.ser12.bit_pll -= 0x1000;
+		bc->modem.ser12.dcd_sum0 += 4 * hweight8(bc->modem.ser12.dcd_shreg & 0x38)
+			- hweight16(bc->modem.ser12.dcd_shreg & 0x7c0);
+	}
+	hdlcdrv_channelbit(&bc->hdrv, !!bc->modem.ser12.last_sample);
+	if ((--bc->modem.ser12.dcd_time) <= 0) {
+		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + 
+					   bc->modem.ser12.dcd_sum1 + 
+					   bc->modem.ser12.dcd_sum2) < 0);
+		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+		bc->modem.ser12.dcd_sum0 = 2; /* slight bias */
+		bc->modem.ser12.dcd_time = 120;
+	}
+	if (bc->modem.ser12.bit_pll >= 0x10000) {
+		bc->modem.ser12.bit_pll &= 0xffff;
+		bc->modem.shreg >>= 1;
+		if (bc->modem.ser12.last_rxbit == bc->modem.ser12.last_sample)
+			bc->modem.shreg |=  0x10000;
+		bc->modem.ser12.last_rxbit = bc->modem.ser12.last_sample;
+		if (bc->modem.shreg & 1) {
+			hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
+			bc->modem.shreg = 0x10000;
+		}
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void ser12_rx(struct device *dev, struct baycom_state *bc, unsigned char curs)
+{
+	unsigned long curjiff;
+	struct timeval tv;
+	unsigned int timediff;
+
+	/*
+	 * get current time
+	 */
+	curjiff = jiffies;
+	do_gettimeofday(&tv);
+	if ((signed)(curjiff - bc->modem.ser12.last_jiffies) >= HZ/4) {
+		/* long inactivity; clear HDLC and DCD */
+		bc->modem.ser12.dcd_sum1 = 0;
+		bc->modem.ser12.dcd_sum2 = 0;
+		bc->modem.ser12.dcd_sum0 = 2;
+		bc->modem.ser12.dcd_time = 120;
+		hdlcdrv_setdcd(&bc->hdrv, 0);
+		hdlcdrv_putbits(&bc->hdrv, 0xffff);
+		bc->modem.ser12.last_jiffies = curjiff;
+		bc->modem.ser12.pll_time = tv.tv_usec;
+	}
+	bc->modem.ser12.last_jiffies = curjiff;
+	timediff = tv.tv_usec + 1000000 - bc->modem.ser12.pll_time;
+	timediff %= 1000000;
+	timediff /= 125000/SER12_BAUD;
+	bc->modem.ser12.pll_time = (bc->modem.ser12.pll_time + timediff * (125000/SER12_BAUD)) % 1000000;
+	for (; timediff > 1; timediff--)
+		ser12_rxsample(dev, bc, bc->modem.ser12.last_sample);
+	if (timediff >= 1)
+		ser12_rxsample(dev, bc, curs);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+	unsigned char iir, msr = 0;
+	unsigned int txcount = 0;
+	unsigned int rxcount = 0;
+
+	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	
+	for (;;) {
+		iir = inb(IIR(dev->base_addr));
+		if (iir & 1)
+			break;
+		switch (iir & 6) {
+		case 6:
+			inb(LSR(dev->base_addr));
+			continue;
+			
+		case 4:
+			inb(RBR(dev->base_addr));
+			continue;
+			
+		case 2:
+			/*
+			 * make sure the next interrupt is generated;
+			 * 0 must be used to power the modem; the modem draws its
+			 * power from the TxD line
+			 */
+			outb(0x00, THR(dev->base_addr));
+			bc->modem.arb_divider--;
+			baycom_int_freq(bc);
+			if (hdlcdrv_ptt(&bc->hdrv)) {
+				/*
+				 * first output the last bit (!) then call HDLC transmitter,
+				 * since this may take quite long
+				 */
+				outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
+				txcount++;
+			} else
+				outb(0x0d, MCR(dev->base_addr));       /* transmitter off */
+			continue;
+			
+		default:
+			msr = inb(MSR(dev->base_addr));
+			if (msr & 1)  /* delta CTS interrupt */
+				rxcount++;
+			continue;
+		}
+	}
+	if (rxcount)
+		ser12_rx(dev, bc, msr & 0x10);
+	if (txcount) {
+#ifdef BAYCOM_DEBUG
+		if (bc->debug_vals.cur_pllcorr < txcount)
+			bc->debug_vals.cur_pllcorr = txcount;
+#endif /* BAYCOM_DEBUG */
+		if (bc->modem.ser12.txshreg <= 1)
+			bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
+		bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1));
+		bc->modem.ser12.txshreg >>= 1;
+	}
+	sti();
+	if (bc->modem.arb_divider <= 0) {
+		bc->modem.arb_divider = SER12_BAUD/100;
+		hdlcdrv_arbitrate(dev, &bc->hdrv);
+	}
+	hdlcdrv_transmitter(dev, &bc->hdrv);
+	hdlcdrv_receiver(dev, &bc->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+#else /* BAYCOM_ALT_SER12 */
+
 static void inline ser12_set_divisor(struct device *dev,
 				     unsigned char divisor)
 {
@@ -503,13 +688,15 @@
 	hdlcdrv_transmitter(dev, &bc->hdrv);
 	hdlcdrv_receiver(dev, &bc->hdrv);
 }
+#endif /* BAYCOM_ALT_SER12 */
 
 /* --------------------------------------------------------------------- */
 
 enum uart { c_uart_unknown, c_uart_8250,
-	c_uart_16450, c_uart_16550, c_uart_16550A};
-static const char *uart_str[] =
-	{ "unknown", "8250", "16450", "16550", "16550A" };
+	    c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] = { 
+	"unknown", "8250", "16450", "16550", "16550A" 
+};
 
 static enum uart ser12_check_uart(unsigned int iobase)
 {
@@ -568,6 +755,30 @@
 			"baycom_ser12", dev))
 		return -EBUSY;
 	request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12");
+#ifdef BAYCOM_ALT_SER12
+	bc->hdrv.par.bitrate = SER12_BAUD;
+	/*
+	 * set the SIO to 6 Bits/character and 19600 baud, so that
+	 * we get exactly (hopefully) one interrupt per radio symbol
+	 */
+	outb(0x81, LCR(dev->base_addr));	/* DLAB = 1 */
+	outb(115200/8/SER12_BAUD, DLL(dev->base_addr));
+	outb(0, DLM(dev->base_addr));
+	outb(0x01, LCR(dev->base_addr));	/* word length = 6 */
+	/*
+	 * enable transmitter empty interrupt and modem status interrupt
+	 */
+	outb(0x0a, IER(dev->base_addr));
+	/*
+	 * make sure the next interrupt is generated;
+	 * 0 must be used to power the modem; the modem draws its
+	 * power from the TxD line
+	 */
+	outb(0x00, THR(dev->base_addr));
+	printk(KERN_INFO "%s: ser12(alt modem) at iobase 0x%lx irq %u options "
+	       "0x%x uart %s\n", bc_drvname, dev->base_addr, dev->irq,
+	       bc->options, uart_str[u]);
+#else /* BAYCOM_ALT_SER12 */
 	/*
 	 * enable transmitter empty interrupt
 	 */
@@ -581,6 +792,7 @@
 	printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u options "
 	       "0x%x uart %s\n", bc_drvname, dev->base_addr, dev->irq,
 	       bc->options, uart_str[u]);
+#endif /* BAYCOM_ALT_SER12 */
 	MOD_INC_USE_COUNT;
 	return 0;
 }
diff -u --recursive --new-file v2.1.48/linux/drivers/net/hdlcdrv.c linux/drivers/net/hdlcdrv.c
--- v2.1.48/linux/drivers/net/hdlcdrv.c	Mon Jun 16 16:35:55 1997
+++ linux/drivers/net/hdlcdrv.c	Tue Aug  5 09:49:50 1997
@@ -33,6 +33,8 @@
  *   0.2  21.11.96  various small changes
  *   0.3  03.03.97  fixed (hopefully) IP not working with ax.25 as a module
  *   0.4  16.04.97  init code/data tagged
+ *   0.5  30.07.97  made HDLC buffers bigger (solves a problem with the
+ *                  soundmodem driver)
  */
 
 /*****************************************************************************/
@@ -1019,7 +1021,7 @@
 __initfunc(int init_module(void))
 {
 	printk(KERN_INFO "hdlcdrv: (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
-	printk(KERN_INFO "hdlcdrv: version 0.4 compiled " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "hdlcdrv: version 0.5 compiled " __TIME__ " " __DATE__ "\n");
 #if LINUX_VERSION_CODE < 0x20115
         register_symtab(&hdlcdrv_syms);
 #endif
diff -u --recursive --new-file v2.1.48/linux/drivers/net/ne.c linux/drivers/net/ne.c
--- v2.1.48/linux/drivers/net/ne.c	Wed Apr 23 19:01:20 1997
+++ linux/drivers/net/ne.c	Tue Aug  5 09:24:32 1997
@@ -425,7 +425,7 @@
     /* Snarf the interrupt now.  There's no point in waiting since we cannot
        share and the board will usually be enabled. */
     {
-	int irqval = request_irq(dev->irq, ei_interrupt, 0, name, NULL);
+	int irqval = request_irq(dev->irq, ei_interrupt, SA_INTERRUPT, name, NULL);
 	if (irqval) {
 	    printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
 	    return EAGAIN;
diff -u --recursive --new-file v2.1.48/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v2.1.48/linux/drivers/net/plip.c	Mon Aug  4 16:25:37 1997
+++ linux/drivers/net/plip.c	Tue Aug  5 09:24:32 1997
@@ -150,7 +150,7 @@
 static int plip_config(struct device *dev, struct ifmap *map);
 static int plip_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
 static int plip_preempt(void *handle);
-static int plip_wakeup(void *handle);
+static void plip_wakeup(void *handle);
 
 enum plip_connection_state {
 	PLIP_CN_NONE=0,
diff -u --recursive --new-file v2.1.48/linux/drivers/net/soundmodem/gentbl.c linux/drivers/net/soundmodem/gentbl.c
--- v2.1.48/linux/drivers/net/soundmodem/gentbl.c	Mon Jun 16 16:35:56 1997
+++ linux/drivers/net/soundmodem/gentbl.c	Tue Aug  5 09:49:51 1997
@@ -81,7 +81,6 @@
 static void gentbl_afsk1200(FILE *f)
 {
         int i, v, sum;
-	float fv, fsum;
 
 #define ARGLO(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_LO/(double)AFSK12_SAMPLE_RATE
 #define ARGHI(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_HI/(double)AFSK12_SAMPLE_RATE
@@ -93,34 +92,7 @@
 		"#define AFSK12_CORRLEN %u\n\n",
 		AFSK12_SAMPLE_RATE, AFSK12_TX_FREQ_LO, 
 		AFSK12_TX_FREQ_HI, AFSK12_CORRLEN);
-	fprintf(f, "#if defined(CONFIG_SOUNDMODEM_FLOAT) && "
-		"(defined(CONFIG_M586) || defined(CONFIG_M686))\n\n"
-		"static const float afsk12_tx_lo_i_f[] = {\n\t");
-        for(fsum = i = 0; i < AFSK12_CORRLEN; i++) {
-		fsum += (fv = cos(ARGLO(i)));
-                fprintf(f, " %7f%c", fv, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
-	}
-        fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_Q %7f\n\n"
-		"static const float afsk12_tx_lo_q_f[] = {\n\t", fsum);
-        for(fsum = i = 0; i < AFSK12_CORRLEN; i++) {
-		fsum += (fv = sin(ARGLO(i)));
-                fprintf(f, " %7f%c", fv, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
-	}
-        fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_Q %7f\n\n"
-		"static const float afsk12_tx_hi_i_f[] = {\n\t", fsum);
-        for(fsum = i = 0; i < AFSK12_CORRLEN; i++) {
-		fsum += (fv = cos(ARGHI(i)));
-                fprintf(f, " %7f%c", fv, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
-	}
-        fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_I %7f\n\n"
-		"static const float afsk12_tx_hi_q_f[] = {\n\t", fsum);
-        for(fsum = i = 0; i < AFSK12_CORRLEN; i++) {
-		fsum += (fv = sin(ARGHI(i)));
-                fprintf(f, " %7f%c", fv, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
-	}
-	fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %7f\n\n"
-		"#else /* CONFIG_SOUNDMODEM_FLOAT */\n\n"
-		"static const int afsk12_tx_lo_i[] = {\n\t", fsum);
+	fprintf(f, "static const int afsk12_tx_lo_i[] = {\n\t");
         for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
 		sum += (v = 127.0*cos(ARGLO(i)));
                 fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
@@ -143,8 +115,7 @@
 		sum += (v = 127.0*sin(ARGHI(i)));
                 fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
 	}
-	fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %d\n\n"
-		"#endif /* CONFIG_SOUNDMODEM_FLOAT */\n\n", sum);
+	fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %d\n\n", sum);
 #undef ARGLO
 #undef ARGHI
 }
@@ -594,7 +565,6 @@
 static void gentbl_afsk2400(FILE *f, float tcm3105clk)
 {
 	int i, sum, v;
-	float fsum, fv;
 
 	fprintf(f, "\n/*\n * afsk2400 specific tables (tcm3105 clk %7fHz)\n */\n"
 		"#define AFSK24_TX_FREQ_LO %d\n"
@@ -608,34 +578,7 @@
 #define ARGHI(x) 2.0*M_PI*(double)x*(tcm3105clk/2015.0)/(double)AFSK24_SAMPLERATE
 #define WINDOW(x) hamming((float)(x)/(AFSK24_CORRLEN-1.0))
 
-	fprintf(f, "#if defined(CONFIG_SOUNDMODEM_FLOAT) && "
-		"(defined(CONFIG_M586) || defined(CONFIG_M686))\n\n"
-		"static const float afsk24_tx_lo_i_f[] = {\n\t");
-        for(fsum = i = 0; i < AFSK24_CORRLEN; i++) {
-		fsum += (fv = cos(ARGLO(i))*WINDOW(i));
-                fprintf(f, " %7f%c", fv, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
-	}
-        fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_Q %7f\n\n"
-		"static const float afsk24_tx_lo_q_f[] = {\n\t", fsum);
-        for(fsum = i = 0; i < AFSK24_CORRLEN; i++) {
-		fsum += (fv = sin(ARGLO(i))*WINDOW(i));
-                fprintf(f, " %7f%c", fv, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
-	}
-        fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_Q %7f\n\n"
-		"static const float afsk24_tx_hi_i_f[] = {\n\t", fsum);
-        for(fsum = i = 0; i < AFSK24_CORRLEN; i++) {
-		fsum += (fv = cos(ARGHI(i))*WINDOW(i));
-                fprintf(f, " %7f%c", fv, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
-	}
-        fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_I %7f\n\n"
-		"static const float afsk24_tx_hi_q_f[] = {\n\t", fsum);
-        for(fsum = i = 0; i < AFSK24_CORRLEN; i++) {
-		fsum += (fv = sin(ARGHI(i))*WINDOW(i));
-                fprintf(f, " %7f%c", fv, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
-	}
-	fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %7f\n\n"
-		"#else /* CONFIG_SOUNDMODEM_FLOAT */\n\n"
-		"static const int afsk24_tx_lo_i[] = {\n\t", fsum);
+	fprintf(f, "static const int afsk24_tx_lo_i[] = {\n\t");
         for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
 		sum += (v = 127.0*cos(ARGLO(i))*WINDOW(i));
                 fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
@@ -658,8 +601,7 @@
 		sum += (v = 127.0*sin(ARGHI(i))*WINDOW(i));
                 fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
 	}
-	fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %d\n\n"
-		"#endif /* CONFIG_SOUNDMODEM_FLOAT */\n\n", sum);
+	fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %d\n\n", sum);
 #undef ARGLO
 #undef ARGHI
 #undef WINDOW
@@ -675,11 +617,6 @@
 		"DO NOT EDIT!\n */\n\n", progname);
 }
 
-static void gentbl_needs_config(FILE *f)
-{
-	fprintf(f, "\n#include <linux/config.h>\n\n");
-}
-
 /* -------------------------------------------------------------------- */
 
 void main(int argc, char *argv[])
@@ -690,7 +627,6 @@
 	if (!(f = fopen("sm_tbl_afsk1200.h", "w")))
 		exit(1);
 	gentbl_banner(f);
-	gentbl_needs_config(f);
 	gentbl_offscostab(f, 6);
 	gentbl_costab(f, 6);
 	gentbl_afsk1200(f);
@@ -722,7 +658,6 @@
 	if (!(f = fopen("sm_tbl_afsk2400_8.h", "w")))
 		exit(1);
 	gentbl_banner(f);
-	gentbl_needs_config(f);
 	gentbl_offscostab(f, 6);
 	gentbl_costab(f, 6);
 	gentbl_afsk2400(f, 8000000);
@@ -730,7 +665,6 @@
 	if (!(f = fopen("sm_tbl_afsk2400_7.h", "w")))
 		exit(1);
 	gentbl_banner(f);
-	gentbl_needs_config(f);
 	gentbl_offscostab(f, 6);
 	gentbl_costab(f, 6);
 	gentbl_afsk2400(f, 7372800);
diff -u --recursive --new-file v2.1.48/linux/drivers/net/soundmodem/sm.c linux/drivers/net/soundmodem/sm.c
--- v2.1.48/linux/drivers/net/soundmodem/sm.c	Mon Jun 16 16:35:56 1997
+++ linux/drivers/net/soundmodem/sm.c	Tue Aug  5 09:49:51 1997
@@ -39,6 +39,7 @@
  *   0.4  21.01.97  Separately compileable soundcard/modem modules
  *   0.5  03.03.97  fixed LPT probing (check_lpt result was interpreted the wrong way round)
  *   0.6  16.04.97  init code/data tagged
+ *   0.7  30.07.97  fixed halfduplex interrupt handlers/hotfix for CS423X
  */
 
 /*****************************************************************************/
@@ -113,7 +114,7 @@
 
 /*static*/ const char sm_drvname[] = "soundmodem";
 static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-1997 Thomas Sailer, HB9JNX/AE4WA\n"
-KERN_INFO "soundmodem: version 0.6 compiled " __TIME__ " " __DATE__ "\n";
+KERN_INFO "soundmodem: version 0.7 compiled " __TIME__ " " __DATE__ "\n";
 
 /* --------------------------------------------------------------------- */
 
diff -u --recursive --new-file v2.1.48/linux/drivers/net/soundmodem/sm_sbc.c linux/drivers/net/soundmodem/sm_sbc.c
--- v2.1.48/linux/drivers/net/soundmodem/sm_sbc.c	Mon Aug  4 16:25:37 1997
+++ linux/drivers/net/soundmodem/sm_sbc.c	Tue Aug  5 09:49:51 1997
@@ -343,6 +343,7 @@
 		if (hdlcdrv_ptt(&sm->hdrv)) {
 			/* starting to transmit */
 			disable_dma(dev->dma);
+			hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
 			dma_start_transmit(sm);
 			setup_dma_dsp(dev, sm, 1);
 			dma_transmit(sm);
@@ -416,11 +417,7 @@
 	 */
 	dma_init_receive(sm);
 	dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
-	if (sm->dma.i16bit)
-		dmasz <<= 1;
 	u = NUM_FRAGMENTS * sm->dma.ofragsz;
-	if (sm->dma.o16bit)
-		u <<= 1;
 	if (u > dmasz)
 		dmasz = u;
 	if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
diff -u --recursive --new-file v2.1.48/linux/drivers/net/soundmodem/sm_wss.c linux/drivers/net/soundmodem/sm_wss.c
--- v2.1.48/linux/drivers/net/soundmodem/sm_wss.c	Mon Aug  4 16:25:37 1997
+++ linux/drivers/net/soundmodem/sm_wss.c	Tue Aug  5 09:49:51 1997
@@ -98,6 +98,8 @@
 
 #define WSS_EXTENT   8
 
+#define CS423X_HOTFIX
+
 /* --------------------------------------------------------------------- */
 
 static void write_codec(struct device *dev, unsigned char idx,
@@ -170,6 +172,8 @@
 		/* MCE and interface config reg */
 		write_codec(dev, 0x49, fdx ? 0x8 : 0xc);
 	outb(0xb, WSS_CODEC_IA(dev->base_addr)); /* leave MCE */
+	if (SCSTATE->crystal && !fullcalib)
+		return 0;
 	/*
 	 * wait for ACI start
 	 */
@@ -361,15 +365,16 @@
 		abrt = 0;
 		while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000));
 	}
+#ifdef CS423X_HOTFIX
+	if (read_codec(dev, 0x8) != fmt || SCSTATE->crystal)
+		wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
+#else /* CS423X_HOTFIX */
 	if (read_codec(dev, 0x8) != fmt)
 		wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
+#endif /* CS423X_HOTFIX */
 	numsamps = dma_setup(sm, send, dev->dma) - 1;
 	write_codec(dev, 15, numsamps & 0xff);
 	write_codec(dev, 14, numsamps >> 8);
-	if (SCSTATE->crystal) {
-		write_codec(dev, 31, numsamps & 0xff);
-		write_codec(dev, 30, numsamps >> 8);
-	}
 	write_codec(dev, 9, codecmode[send]);
         restore_flags(flags);
 }
@@ -393,10 +398,6 @@
 	nums = dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag) - 1;
 	write_codec(dev, 15, nums  & 0xff);
 	write_codec(dev, 14, nums >> 8);
-	if (SCSTATE->crystal) {
-		write_codec(dev, 31, nums & 0xff);
-		write_codec(dev, 30, nums >> 8);
-	}
 	enable_dma(dev->dma);
 	sm_int_freq(sm);
 	sti();
@@ -406,6 +407,7 @@
 		if (hdlcdrv_ptt(&sm->hdrv)) {
 			/* starting to transmit */
 			disable_dma(dev->dma);
+			hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
 			dma_start_transmit(sm);
 			setup_dma_wss(dev, sm, 1);
 			dma_transmit(sm);
@@ -413,7 +415,6 @@
 	} else if (dma_end_transmit(sm, curfrag)) {
 		/* stopping transmission */
 		disable_dma(dev->dma);
-		sti();
 		dma_init_receive(sm);
 		setup_dma_wss(dev, sm, 0);
         } else
@@ -451,11 +452,7 @@
 	 */
 	dma_init_receive(sm);
 	dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
-	if (sm->dma.i16bit)
-		dmasz <<= 1;
 	u = NUM_FRAGMENTS * sm->dma.ofragsz;
-	if (sm->dma.o16bit)
-		u <<= 1;
 	if (u > dmasz)
 		dmasz = u;
 	if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
diff -u --recursive --new-file v2.1.48/linux/drivers/net/soundmodem/smdma.h linux/drivers/net/soundmodem/smdma.h
--- v2.1.48/linux/drivers/net/soundmodem/smdma.h	Mon Jun 16 16:35:56 1997
+++ linux/drivers/net/soundmodem/smdma.h	Tue Aug  5 09:49:51 1997
@@ -37,6 +37,13 @@
 #define DMA_MODE_AUTOINIT      0x10
 #define NUM_FRAGMENTS          4
 
+/*
+ * NOTE: make sure that hdlcdrv_hdlcbuffer contains enough space
+ * for the modulator to fill the whole DMA buffer without underrun
+ * at the highest possible baud rate, otherwise the TX state machine will
+ * not work correctly. That is (9k6 FSK): HDLCDRV_HDLCBUFFER > 6*NUM_FRAGMENTS
+ */ 
+
 /* --------------------------------------------------------------------- */
 /*
  * ===================== DMA buffer management ===========================
diff -u --recursive --new-file v2.1.48/linux/drivers/net/tlan.h linux/drivers/net/tlan.h
--- v2.1.48/linux/drivers/net/tlan.h	Thu Jul 17 10:06:05 1997
+++ linux/drivers/net/tlan.h	Wed Aug  6 13:02:59 1997
@@ -479,7 +479,3 @@
 
 } 
 
-
-
-
-#endif
diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/53c7,8xx.c linux/drivers/scsi/53c7,8xx.c
--- v2.1.48/linux/drivers/scsi/53c7,8xx.c	Mon Aug  4 16:25:38 1997
+++ linux/drivers/scsi/53c7,8xx.c	Wed Aug  6 13:02:59 1997
@@ -678,6 +678,7 @@
  * Returns : NULL on failure, pointer to host structure on success.
  */
 
+#if 0
 static struct Scsi_Host *
 find_host (int host) {
     struct Scsi_Host *h;
@@ -767,6 +768,7 @@
 	hostdata->options &= ~OPTION_DISCONNECT;
     return 0;
 }
+#endif
 
 /*
  * Function : static void NCR53c7x0_driver_init (struct Scsi_Host *host)
@@ -5468,6 +5470,7 @@
  * Returns : char * representation of state, "unknown" on error.
  */
 
+#if 0
 static const char *
 ncr_state (int state) {
     switch (state) {
@@ -5479,6 +5482,7 @@
     default: return "unknown";
     }
 }
+#endif
 
 /*
  * Function : int NCR53c7xx_abort (Scsi_Cmnd *cmd)
diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c
--- v2.1.48/linux/drivers/scsi/BusLogic.c	Wed Apr 23 19:01:21 1997
+++ linux/drivers/scsi/BusLogic.c	Mon Aug 11 00:10:00 1997
@@ -27,8 +27,8 @@
 */
 
 
-#define BusLogic_DriverVersion		"2.0.9"
-#define BusLogic_DriverDate		"29 March 1997"
+#define BusLogic_DriverVersion		"2.0.10"
+#define BusLogic_DriverDate		"11 August 1997"
 
 
 #include <linux/module.h>
@@ -43,7 +43,6 @@
 #include <linux/stat.h>
 #include <linux/pci.h>
 #include <linux/bios32.h>
-#include <linux/init.h>
 #include <asm/dma.h>
 #include <asm/io.h>
 #include <asm/system.h>
@@ -223,8 +222,7 @@
   Host Adapter.
 */
 
-__initfunc(static boolean
-BusLogic_CreateMailboxes(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_CreateMailboxes(BusLogic_HostAdapter_T *HostAdapter)
 {
   /*
     FlashPoint Host Adapters do not use Outgoing and Incoming Mailboxes.
@@ -306,8 +304,7 @@
   BusLogic_CreateInitialCCBs allocates the initial CCBs for Host Adapter.
 */
 
-__initfunc(static boolean
-BusLogic_CreateInitialCCBs(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_CreateInitialCCBs(BusLogic_HostAdapter_T *HostAdapter)
 {
   int Allocated;
   for (Allocated = 0; Allocated < HostAdapter->InitialCCBs; Allocated++)
@@ -420,8 +417,8 @@
   structure for Host Adapter.
 */
 
-__initfunc(static boolean
-BusLogic_CreateTargetDeviceStatistics(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_CreateTargetDeviceStatistics(BusLogic_HostAdapter_T
+						     *HostAdapter)
 {
   HostAdapter->TargetDeviceStatistics =
     (BusLogic_TargetDeviceStatistics_T *)
@@ -508,13 +505,14 @@
     Wait for the Host Adapter Ready bit to be set and the Command/Parameter
     Register Busy bit to be reset in the Status Register.
   */
-  TimeoutCounter = loops_per_sec >> 3;
+  TimeoutCounter = 10000;
   while (--TimeoutCounter >= 0)
     {
       StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
       if (StatusRegister.Bits.HostAdapterReady &&
 	  !StatusRegister.Bits.CommandParameterRegisterBusy)
 	break;
+      udelay(100);
     }
   if (TimeoutCounter < 0)
     {
@@ -590,11 +588,11 @@
     case BusLogic_InquireInstalledDevicesID8to15:
     case BusLogic_InquireTargetDevices:
       /* Approximately 60 seconds. */
-      TimeoutCounter = loops_per_sec << 2;
+      TimeoutCounter = 60*10000;
       break;
     default:
       /* Approximately 1 second. */
-      TimeoutCounter = loops_per_sec >> 4;
+      TimeoutCounter = 10000;
       break;
     }
   /*
@@ -614,6 +612,7 @@
 	else BusLogic_ReadDataInRegister(HostAdapter);
       if (OperationCode == BusLogic_FetchHostAdapterLocalRAM &&
 	  StatusRegister.Bits.HostAdapterReady) break;
+      udelay(100);
     }
   if (TimeoutCounter < 0)
     {
@@ -711,7 +710,7 @@
   only from the list of standard BusLogic MultiMaster ISA I/O Addresses.
 */
 
-static inline void BusLogic_InitializeProbeInfoListISA(void)
+static void BusLogic_InitializeProbeInfoListISA(void)
 {
   int StandardAddressIndex;
   /*
@@ -749,9 +748,8 @@
   of increasing PCI Bus and Device Number.
 */
 
-__initfunc(static void
-BusLogic_SortProbeInfo(BusLogic_ProbeInfo_T *ProbeInfoList,
-		       int ProbeInfoCount))
+static void BusLogic_SortProbeInfo(BusLogic_ProbeInfo_T *ProbeInfoList,
+				   int ProbeInfoCount)
 {
   int LastInterchange = ProbeInfoCount-1, Bound, j;
   while (LastInterchange > 0)
@@ -785,12 +783,12 @@
   I/O Addresses.  It returns the number of PCI MultiMaster Host Adapters found.
 */
 
-__initfunc(static int BusLogic_InitializeMultiMasterProbeInfo(void))
+static int BusLogic_InitializeMultiMasterProbeInfo(void)
 {
   boolean StandardAddressSeen[BusLogic_ISA_StandardAddressesCount];
   BusLogic_ProbeInfo_T *PrimaryProbeInfo =
     &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount];
-  int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount;
+  int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount + 1;
   int NonPrimaryPCIMultiMasterCount = 0, PCIMultiMasterCount = 0;
   boolean ForceBusDeviceScanningOrder = false;
   boolean ForceBusDeviceScanningOrderChecked = false;
@@ -1016,7 +1014,7 @@
   number of FlashPoint Host Adapters found.
 */
 
-__initfunc(static int BusLogic_InitializeFlashPointProbeInfo(void))
+static int BusLogic_InitializeFlashPointProbeInfo(void)
 {
   int FlashPointIndex = BusLogic_ProbeInfoCount, FlashPointCount = 0;
   unsigned char Bus, DeviceFunction, IRQ_Channel;
@@ -1123,7 +1121,7 @@
   particular probe order.
 */
 
-static inline void BusLogic_InitializeProbeInfoList(void)
+static void BusLogic_InitializeProbeInfoList(void)
 {
   /*
     If BusLogic_Setup has provided an I/O Address probe list, do not override
@@ -1232,8 +1230,7 @@
   BusLogic_ProbeHostAdapter probes for a BusLogic Host Adapter.
 */
 
-__initfunc(static boolean
-BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
 {
   BusLogic_StatusRegister_T StatusRegister;
   BusLogic_InterruptRegister_T InterruptRegister;
@@ -1245,13 +1242,16 @@
     {
       FlashPoint_Info_T *FlashPointInfo = (FlashPoint_Info_T *)
 	scsi_init_malloc(sizeof(FlashPoint_Info_T), GFP_ATOMIC);
+      int Retries = 10;
       if (FlashPointInfo == NULL)
 	return BusLogic_Failure(HostAdapter, "ALLOCATING FLASHPOINT INFO");
       FlashPointInfo->BaseAddress = HostAdapter->IO_Address;
       FlashPointInfo->IRQ_Channel = HostAdapter->IRQ_Channel;
       FlashPointInfo->Present = false;
-      if (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 &&
-	    FlashPointInfo->Present))
+      while (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 &&
+	       FlashPointInfo->Present) &&
+	     --Retries >= 0) ;
+      if (!FlashPointInfo->Present)
 	{
 	  scsi_init_free((char *) FlashPointInfo, sizeof(FlashPoint_Info_T));
 	  BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at "
@@ -1326,7 +1326,7 @@
 					     *HostAdapter)
 {
   BusLogic_StatusRegister_T StatusRegister;
-  int TimeoutCounter = loops_per_sec;
+  int TimeoutCounter;
   /*
     FlashPoint Host Adapters are Hard Reset by the FlashPoint SCCB Manager.
   */
@@ -1349,10 +1349,12 @@
   /*
     Wait until Diagnostic Active is set in the Status Register.
   */
+  TimeoutCounter = 5*10000;
   while (--TimeoutCounter >= 0)
     {
       StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
       if (StatusRegister.Bits.DiagnosticActive) break;
+      udelay(100);
     }
   if (BusLogic_GlobalOptions.Bits.TraceHardReset)
     BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Active, "
@@ -1368,10 +1370,12 @@
   /*
     Wait until Diagnostic Active is reset in the Status Register.
   */
+  TimeoutCounter = 10*10000;
   while (--TimeoutCounter >= 0)
     {
       StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
       if (!StatusRegister.Bits.DiagnosticActive) break;
+      udelay(100);
     }
   if (BusLogic_GlobalOptions.Bits.TraceHardReset)
     BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Completed, "
@@ -1382,6 +1386,7 @@
     Wait until at least one of the Diagnostic Failure, Host Adapter Ready,
     or Data In Register Ready bits is set in the Status Register.
   */
+  TimeoutCounter = 10000;
   while (--TimeoutCounter >= 0)
     {
       StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
@@ -1389,6 +1394,7 @@
 	  StatusRegister.Bits.HostAdapterReady ||
 	  StatusRegister.Bits.DataInRegisterReady)
 	break;
+      udelay(100);
     }
   if (BusLogic_GlobalOptions.Bits.TraceHardReset)
     BusLogic_Notice("BusLogic_HardReset(0x%X): Host Adapter Ready, "
@@ -1427,8 +1433,7 @@
   Host Adapter.  It also determines the IRQ Channel for non-PCI Host Adapters.
 */
 
-__initfunc(static boolean
-BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
 {
   BusLogic_Configuration_T Configuration;
   BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation;
@@ -1491,8 +1496,8 @@
   from Host Adapter and initializes the Host Adapter structure.
 */
 
-__initfunc(static boolean
-BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
+						     *HostAdapter)
 {
   BusLogic_BoardID_T BoardID;
   BusLogic_Configuration_T Configuration;
@@ -2005,8 +2010,8 @@
   Host Adapter.
 */
 
-__initfunc(static boolean
-BusLogic_ReportHostAdapterConfiguration(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_ReportHostAdapterConfiguration(BusLogic_HostAdapter_T
+						       *HostAdapter)
 {
   unsigned short AllTargetsMask = (1 << HostAdapter->MaxTargetDevices) - 1;
   unsigned short SynchronousPermitted, FastPermitted;
@@ -2231,8 +2236,7 @@
   Host Adapter.
 */
 
-__initfunc(static boolean
-BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter)
 {
   BusLogic_HostAdapter_T *FirstHostAdapter =
     BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel];
@@ -2318,8 +2322,7 @@
   interrupts do not get through as a result.
 */
 
-__initfunc(static boolean
-BusLogic_TestInterrupts(BusLogic_HostAdapter_T *HostAdapter))
+static boolean BusLogic_TestInterrupts(BusLogic_HostAdapter_T *HostAdapter)
 {
   unsigned int InitialInterruptCount, FinalInterruptCount;
   int TestCount = 5, i;
@@ -2608,10 +2611,9 @@
   through explicit acquisition and release of the Host Adapter's Lock.
 */
 
-__initfunc(static void
-BusLogic_InitializeHostStructure(BusLogic_HostAdapter_T
-				 *HostAdapter,
-				 SCSI_Host_T *Host))
+static void BusLogic_InitializeHostStructure(BusLogic_HostAdapter_T
+					       *HostAdapter,
+					     SCSI_Host_T *Host)
 {
   Host->max_id = HostAdapter->MaxTargetDevices;
   Host->max_lun = HostAdapter->MaxLogicalUnits;
@@ -2682,7 +2684,7 @@
   registered.
 */
 
-__initfunc(int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate))
+int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate)
 {
   int BusLogicHostAdapterCount = 0, CommandLineEntryIndex = 0, ProbeIndex;
   char *MessageBuffer = NULL;
@@ -3007,8 +3009,6 @@
 			     "Incoming Mailbox\n", HostAdapter,
 			     CCB->SerialNumber, CCB->Status);
 	  }
-      else BusLogic_Warning("Aborted CCB #%ld to Target %d Not Found\n",
-			    HostAdapter, CCB->SerialNumber, CCB->TargetID);
       NextIncomingMailbox->CompletionCode = BusLogic_IncomingMailboxFree;
       if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox)
 	NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx
--- v2.1.48/linux/drivers/scsi/ChangeLog.ncr53c8xx	Tue May 13 22:41:12 1997
+++ linux/drivers/scsi/ChangeLog.ncr53c8xx	Wed Aug  6 13:02:59 1997
@@ -1,3 +1,73 @@
+Sat July 26 18:00 1997 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.4
+	Several clean-ups:
+	  - Asynchronous pre-scaler calculation.
+	    Synchronous divisor calculation.
+	  - Use FE_ as feature identifier prefix instead of _F_.
+	  - Change 'ns_sync' identifier to "minsync".
+	  - Some others.
+	Apply some SPI2-R12 recommendations.
+	  - Use Slow, Fast-10, Fast-20, Fast-40 SCSI instead of SCSI-2, 
+	    FAST SCSI-2, ULTRA, ULTRA-2. 
+	  - Reset the SCSI on bus mode change.
+
+Wed July 02 22:58 1997 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.3c
+	- Add define SCSI_NCR_PCI_FIX_UP_SUPPORT for conditionnal compilation 
+	  of the corresponding pci fix-up code when a small driver is needed.
+	- Use "ncr53c8xx" as driver name for both request_irq() and 
+          request_region(). Using different names confused 'lsdev'.
+          (Suggestion sent by Henrik Storner).
+
+Wed June 24 22:08 1997 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.3b
+	- Print an error message on unexpected boot command line option.
+	- Switch to asynchronous data transfer mode after SCSI wide 
+	  negotiation.
+
+Wed June 14 22:00 1997 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.3a
+	- Add PCI LATENCY TIMER fixup code.
+	  Increase it if necessary according to burst size.
+	  Boot option bit : 'pcifix:4'
+	- On phase mismatch, calculate residual data size for all OUTPUT 
+	  phases. That's only required for interrupted DATA OUT phase, but 
+	  this information is usefull for problem solving.
+	- Add KERN_INFO to some messages printed to the log.
+	  (Patch sent by Wolfram Kleff).
+
+Tue June 02 22:30 1997 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.3
+	- NvRAM support code slightly improved (I think):
+	  Use IO or MMIO according to driver setup for reading the NvRAM.
+	  Use structures for NvRAM data instead of raw data.
+	- Prevent from queuing more than 1 command to the scsi SCRIPT with 
+	  negotiation attached when tagged command queueing is enabled.
+	- Fix-up for old 53C8XX chips that support PCI READ LINE but not 
+	  CACHE LINE SIZE. If the cache line size is unknown, set burst 
+	  to 8 dwords and disable READ LINE, otherwise set burst max to 
+	  the cache line size value.
+
+Sat May 24 12:30 1997 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.2c (for linux-2.1.40)
+	- Remove reference to 'x86' symbol when MODULE is defined, since this 
+	  symbol is not exported for module loading.
+	  The value of 'x86' is used for fixing up the PCI CACHE LINE SIZE 
+	  configuration register.
+	- Bytes/words read one bit at a time from the serial NVRAM were'nt 
+          initialized with zero.
+	- Some comments added. Minor cosmetic changes.
+
+Mon May 19 20:30 1997 Gerard Roudier (groudier@club-internet.fr)
+	* revision 2.2b
+	- Patch for NVRAM support by Richard Waltham applied.
+	  The code detects Symbios NVRAM format and Tekram NVRAM format. 
+	  This enhancement allows to get hosts and devices user set up 
+	  from the NVRAM.
+	- Use the NVRAM contents when present to initialize user definable 
+	  target parameters.
+	- Update the README file. 
+
 Sun May 11 22:30 1997 Gerard Roudier (groudier@club-internet.fr)
 	* revision 2.1b
 	- Cosmetic changes.
diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.1.48/linux/drivers/scsi/Config.in	Mon Aug  4 16:25:38 1997
+++ linux/drivers/scsi/Config.in	Wed Aug  6 13:02:59 1997
@@ -65,6 +65,7 @@
 if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then
   dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI
   if [ "$CONFIG_SCSI_NCR53C8XX" != "n" ]; then
+    bool '  detect and read serial NVRAMs' CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
     bool '  enable tagged command queueing' CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
     bool '  use normal IO' CONFIG_SCSI_NCR53C8XX_IOMAPPED
     int  '  maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 4
diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/FlashPoint.c linux/drivers/scsi/FlashPoint.c
--- v2.1.48/linux/drivers/scsi/FlashPoint.c	Tue Mar  4 10:25:24 1997
+++ linux/drivers/scsi/FlashPoint.c	Mon Aug 11 00:10:00 1997
@@ -31,6 +31,18 @@
 #endif
 
 
+/*
+  FlashPoint support is only available for the Intel x86 Architecture.
+*/
+
+#ifndef __i386__
+
+#undef CONFIG_SCSI_OMIT_FLASHPOINT
+#define CONFIG_SCSI_OMIT_FLASHPOINT
+
+#endif
+
+
 #ifndef CONFIG_SCSI_OMIT_FLASHPOINT
 
 
diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.1.48/linux/drivers/scsi/Makefile	Mon Aug  4 16:25:38 1997
+++ linux/drivers/scsi/Makefile	Wed Aug  6 13:02:59 1997
@@ -427,10 +427,6 @@
 seagate.o: seagate.c
 	$(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -DPARITY -c seagate.c 
 
-# For debugging, use the -g flag
-53c7,8xx.o : 53c7,8xx.c
-	$(CC) $(CFLAGS) -g -c 53c7,8xx.c
-
 53c8xx_d.h 53c8xx_u.h : 53c7,8xx.scr script_asm.pl
 	ln -sf 53c7,8xx.scr fake.c
 	$(CPP) -traditional -DCHIP=810 fake.c | grep -v '^#' | perl script_asm.pl 
@@ -438,18 +434,12 @@
 	mv scriptu.h 53c8xx_u.h
 	rm fake.c
 
-53c7xx.o : 53c7xx_d.h 53c7xx.c
-	$(CC) $(CFLAGS) -c 53c7xx.c
-
 53c7xx_d.h 53c7xx_u.h : 53c7xx.scr script_asm.pl
 	ln -sf 53c7xx.scr fake.c
 	$(CPP) -traditional -DCHIP=710 fake.c | grep -v '^#' | perl -s script_asm.pl -ncr7x0_family
 	mv script.h 53c7xx_d.h
 	mv scriptu.h 53c7xx_u.h
 	rm fake.c
-
-ncr53c8xx.o : ncr53c8xx.c
-	$(CC) $(CFLAGS) -c ncr53c8xx.c
 
 scsi_mod.o: $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \
 		scsicam.o scsi_proc.o
diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/README.BusLogic linux/drivers/scsi/README.BusLogic
--- v2.1.48/linux/drivers/scsi/README.BusLogic	Fri Apr  4 08:52:23 1997
+++ linux/drivers/scsi/README.BusLogic	Mon Aug 11 00:10:00 1997
@@ -1,10 +1,10 @@
 	   BusLogic MultiMaster and FlashPoint SCSI Driver for Linux
 
-			  Version 2.0.9 for Linux 2.0
+			 Version 2.0.10 for Linux 2.0
 
 			      PRODUCTION RELEASE
 
-				 29 March 1997
+				11 August 1997
 
 			       Leonard N. Zubkoff
 			       Dandelion Digital
@@ -284,6 +284,14 @@
   so as to recognize the host adapters in the same order as they are enumerated
   by the host adapter's BIOS.
 
+o Mega-Transfers/Second
+
+  The driver reports on the synchronous transfer parameters negotiated between
+  the host adapter and target devices in units of "mega-transfers/second".  For
+  wide devices, the unit of transfer is 16 bits if wide negotiation has been
+  successfully completed.  Therefore, the total transfer rate to wide devices
+  will generally be twice the synchronous tranfer rate reported by the driver.
+
 
 			     COMMAND LINE OPTIONS
 
@@ -372,16 +380,17 @@
 
 				 INSTALLATION
 
-This distribution was prepared for Linux kernel version 2.0.29, but should be
-compatible with 2.0.4 or any later 2.0 series kernel.
+This distribution was prepared for Linux kernel version 2.0.30, but should be
+compatible with 2.0.4 or any later 2.0 series kernel if BusLogic.patch is also
+applied.
 
 To install the new BusLogic SCSI driver, you may use the following commands,
 replacing "/usr/src" with wherever you keep your Linux kernel source tree:
 
   cd /usr/src
-  tar -xvzf BusLogic-2.0.9.tar.gz
+  tar -xvzf BusLogic-2.0.10.tar.gz
   mv README.* LICENSE.* BusLogic.[ch] FlashPoint.c linux/drivers/scsi
-  patch -p < BusLogic.patch
+  patch -p < BusLogic.patch	    # Only for kernels prior to 2.0.30
   cd linux
   make config
   make depend
diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx
--- v2.1.48/linux/drivers/scsi/README.ncr53c8xx	Tue May 13 22:41:12 1997
+++ linux/drivers/scsi/README.ncr53c8xx	Wed Aug  6 13:02:59 1997
@@ -4,7 +4,7 @@
 21 Rue Carnot
 95170 DEUIL LA BARRE - FRANCE
 
-9 May 1997
+19 June 1997
 ===============================================================================
 
 1.  Introduction
@@ -28,6 +28,8 @@
       10.1 Syntax
       10.2 Available arguments
       10.3 Advised boot setup commands
+      10.4 PCI configuration fix-up boot option
+      10.5 Serial NVRAM support boot option
 11. Some constants and flags of the ncr53c8xx.h header file
 12. Installation
       12.1 Provided files
@@ -40,6 +42,10 @@
 16. Synchonous transfer negotiation tables
       16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
       16.2 Synchronous timings for fast SCSI-2 53C8XX controllers
+17. Serial NVRAM support (by Richard Waltham)
+      17.1 Features
+      17.2 Symbios NVRAM layout
+      17.3 Tekram  NVRAM layout
 
 ===============================================================================
 
@@ -124,6 +130,7 @@
 	Scatter / gather
 	Shared interrupt
         Boot setup commands
+        Serial NVRAM:          Symbios and Tekram formats
 
 
 4. Memory mapped I/O versus normal I/O
@@ -412,35 +419,6 @@
     If you select an error type, it will be triggered by the driver every 
     30 seconds.
 
-8.9 PCI configuration fix-up
-
-    pcifix <option bits>
-
-    Available option bits:
-        0x1:     Set PCI cache-line size register if not set.
-        0x2:     Set write and invalidate bit in PCI command register.
-
-    Use 'pcifix:3' in order to allow the driver to fix both PCI features.
-
-    These options only apply to new SYMBIOS chips 810A, 825A, 860 and 875 
-    and are only supported for Pentium and 486 class processors.
-    Recent SYMBIOS 53C8XX scsi processors are able to use PCI read multiple 
-    and PCI write and invalidate commands. These features require the 
-    cache line size register to be properly set in the PCI configuration 
-    space of the chips. On the other hand, chips will use PCI write and 
-    invalidate commands only if the corresponding bit is set to 1 in the 
-    PCI command register.
-
-    Not all PCI bioses set the PCI cache line register and the PCI write and 
-    invalidate bit in the PCI configuration space of 53C8XX chips.
-    Optimized PCI accesses may be broken for some PCI/memory controllers or 
-    make problems with some PCI boards.
-
-    This fix-up works flawlessly on my system.
-    (MB Triton HX / 53C875 / 53C810A)
-    I use these options at my own risks as you will do if you decide to 
-    use them too.
-
 9. Configuration parameters
 
 If the firmware of all your devices is perfect enough, all the
@@ -489,6 +467,18 @@
     GPIO wiring. So, this option must not be enabled if your system has 
     such a board installed.
 
+CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
+    Enable support for reading the serial NVRAM data on Symbios and
+    some Symbios compatible cards, and Tekram DC390W/U/F cards. Useful for
+    systems with more than one Symbios compatible controller where at least
+    one has a serial NVRAM, or for a system with a mixture of Symbios and
+    Tekram cards. Enables setting the boot order of host adaptors
+    to something other than the default order or "reverse probe" order. 
+    Also enables Symbios and Tekram cards to be distinguished so
+    CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT may be set in a system with a
+    mixture of Symbios and Tekram cards so the Symbios cards can make use of
+    the full range of Symbios features, differential, led pin, without
+    causing problems for the Tekram card(s).
 
 10. Boot setup commands
 
@@ -628,6 +618,20 @@
                 810, 815, 820, 860, 875, 885, 895, 896
     revprob:y   probe chip ids in the reverse order.
 
+Fix up PCI configuration space
+    pcifix:<option bits>
+
+    Available option bits:
+        0x1:   Set PCI cache-line size register if not set.
+        0x2:   Set write and invalidate bit in PCI command register.
+        0x4:   Increase if necessary PCI latency timer according to burst max.
+
+    Use 'pcifix:7' in order to allow the driver to fix up all PCI features.
+
+Serial NVRAM
+    nvram:n     do not look for serial NVRAM
+    nvram:y     test controllers for onboard serial NVRAM
+
 Boot fail safe
     safe:y	load the following assumed fail safe initial setup
 
@@ -638,6 +642,8 @@
   ultra scsi			disabled		ultra:n
   force sync negotiation	disabled		fsn:n
   reverse probe			disabled		revprob:n
+  PCI fix up                    disabled                pcifix:0
+  serial NVRAM                  enabled                 nvram:y
   verbosity level		2			verb:2
   tagged command queuing	disabled		tags:0
   synchronous negotiation	disabled		sync:255
@@ -675,6 +681,83 @@
 to your boot setup command in order to check the actual setup the driver is 
 using.
 
+10.4 PCI configuration fix-up boot option
+
+pcifix:<option bits>
+
+Available option bits:
+    0x1:     Set PCI cache-line size register if not set.
+    0x2:     Set write and invalidate bit in PCI command register.
+
+Use 'pcifix:3' in order to allow the driver to fix both PCI features.
+
+These options only apply to new SYMBIOS chips 810A, 825A, 860 and 875 
+and are only supported for Pentium and 486 class processors.
+Recent SYMBIOS 53C8XX scsi processors are able to use PCI read multiple 
+and PCI write and invalidate commands. These features require the 
+cache line size register to be properly set in the PCI configuration 
+space of the chips. On the other hand, chips will use PCI write and 
+invalidate commands only if the corresponding bit is set to 1 in the 
+PCI command register.
+
+Not all PCI bioses set the PCI cache line register and the PCI write and 
+invalidate bit in the PCI configuration space of 53C8XX chips.
+Optimized PCI accesses may be broken for some PCI/memory controllers or 
+make problems with some PCI boards.
+
+This fix-up works flawlessly on my system.
+(MB Triton HX / 53C875 / 53C810A)
+I use these options at my own risks as you will do if you decide to 
+use them too.
+
+
+10.5 Serial NVRAM support boot option
+
+nvram:n     do not look for serial NVRAM
+nvram:y     test controllers for onboard serial NVRAM
+
+This option is described below (see 17. Serial NVRAM support).
+When this option is enabled, the driver tries to detect all boards using 
+a Serial NVRAM. This memory is used to hold user set up parameters.
+
+The parameters the driver is able to get from the NVRAM depend on the 
+data format used, as follow:
+
+                                 Tekram format      Symbios format
+General and host parameters
+    Boot order                         N                   Y
+    Host SCSI ID                       Y                   Y
+    SCSI parity checking               Y                   Y
+    Verbose boot messages              N                   Y
+SCSI devices parameters
+    Synchronous transfer speed         Y                   Y
+    Wide 16 / Narrow                   Y                   Y
+    Tagged Command Queuing enabled     Y                   Y
+    Disconnections enabled             Y                   Y
+    Scan at boot time                  N                   Y
+
+In order to speed up the system boot, for each device configured without 
+the "scan at boot time" option, the driver forces an error on the 
+first TEST UNIT READY command received for this device.
+
+Some SDMS BIOS revisions seem to be unable to boot cleanly with very fast 
+hard disks. In such a situation you cannot configure the NVRAM with 
+optimized parameters value.
+
+The 'nvram' boot option can be entered in hexadecimal form in order 
+to ignore some options configured in the NVRAM, as follow:
+
+mvram=<bits options>
+      0x01   look for NVRAM  (equivalent to nvram=y)
+      0x02   ignore NVRAM "Synchronous negotiation" parameters for all devices
+      0x04   ignore NVRAM "Wide negotiation"  parameter for all devices
+      0x08   ignore NVRAM "Scan at boot time" parameter for all devices
+
+My Atlas Wide only boots cleanly in 8 bits asynchronous data transfer 
+mode. However, it works flawlessly at 20 MB/second with the driver.
+Using "nvram=0x7" allows me to boot in 8 bits/async and to let the driver 
+use its setup for synchronous and wide negotiations.
+
 
 11. Some constants and flags of the ncr53c8xx.h header file
 
@@ -766,6 +849,7 @@
 SCSI_NCR_MAX_LUN	(default: 8)
 	Max number of LUNs per target.
 
+
 12. Installation
 
 12.1 Provided files
@@ -787,9 +871,9 @@
 
 You must untar the distribution with the following command:
 
-	tar zxvf ncrBsd2Linux-2.1b-src.tar.gz
+	tar zxvf ncrBsd2Linux-2.2b-src.tar.gz
 
-The sub-directory ncr53c8xx-2.1b will be created. Change to this directory.
+The sub-directory ncr53c8xx-2.2b will be created. Change to this directory.
 
 
 12.2 Installation procedure
@@ -851,12 +935,11 @@
 has one or more controllers of this family, it may happen that the order 
 the driver registers them to the kernel causes problems due to device 
 name changes.
-SDMS BIOS version 4 allows you to define the order the BIOS will scan the 
-scsi boards and stores this information for next reboots. Unfortunately, the 
-driver is not currently able to read this information and so may register 
-controllers in a different order.
+When at least one controller uses NvRAM, SDMS BIOS version 4 allows you to 
+define the order the BIOS will scan the scsi boards. The driver attaches 
+controllers according to BIOS information if NvRAM detect option is set.
 
-If you have such a problem, you can:
+If your controllers do not have NvRAM, you can:
 
 - Ask the driver to probe chip ids in reverse order from the boot command
   line: ncr53c8xx=revprob:y
@@ -1003,6 +1086,350 @@
  47      188        5.319      200        5.000
  48      192        5.208      200        5.000
  49      196        5.102      200        5.000
+
+
+17. Serial NVRAM (added by Richard Waltham: dormouse@farsrobt.demon.co.uk)
+
+17.1 Features
+
+Enabling serial NVRAM support enables detection of the serial NVRAM included
+on Symbios and some Symbios compatible host adaptors, and Tekram boards. The 
+serial NVRAM is used by Symbios and Tekram to hold set up parameters for the 
+host adaptor and it's attached drives.
+
+The Symbios NVRAM also holds data on the boot order of host adaptors in a
+system with more than one host adaptor. This enables the order of scanning
+the cards for drives to be changed from the default used during host adaptor
+detection.
+
+This can be done to a limited extent at the moment using "reverse probe" but
+this only changes the order of detection of different types of cards. The
+NVRAM boot order settings can do this as well as change the order the same
+types of cards are scanned in, something "reverse probe" cannot do.
+
+Tekram boards using Symbios chips, DC390W/F/U, which have NVRAM are detected
+and this is used to distinguish between Symbios compatible and Tekram host 
+adaptors. This is used to disable the Symbios compatible "diff" setting
+incorrectly set on Tekram boards if the CONFIG_SCSI_53C8XX_SYMBIOS_COMPAT 
+configuration parameter is set enabling both Symbios and Tekram boards to be 
+used together with the Symbios cards using all their features, including
+"diff" support. ("led pin" support for Symbios compatible cards can remain
+enabled when using Tekram cards. It does nothing useful for Tekram host
+adaptors but does not cause problems either.)
+
+
+17.2 Symbios NVRAM layout
+
+typical data at NVRAM address 0x100 (53c810a NVRAM)
+-----------------------------------------------------------
+00 00
+64 01
+8e 0b
+
+00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00 
+
+04 00 0f 00 00 10 00 50 00 00 01 00 00 62 
+04 00 03 00 00 10 00 58 00 00 01 00 00 63 
+04 00 01 00 00 10 00 48 00 00 01 00 00 61 
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 
+
+0f 00 08 08 64 00 0a 00
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+00 00 00 00 00 00 00 00 
+
+fe fe
+00 00
+00 00
+-----------------------------------------------------------
+NVRAM layout details
+
+NVRAM Address 0x000-0x0ff not used
+              0x100-0x26f initialised data
+              0x270-0x7ff not used
+
+general layout
+
+        header  -   6 bytes,
+        data    - 356 bytes (checksum is byte sum of this data)
+        trailer -   6 bytes
+                  ---
+        total     368 bytes
+
+data area layout
+
+        controller set up  -  20 bytes
+        boot configuration -  56 bytes (4x14 bytes)
+        device set up      - 128 bytes (16x8 bytes)
+        unused (spare?)    - 152 bytes (19x8 bytes)
+                             ---
+        total                356 bytes
+
+-----------------------------------------------------------
+header
+
+00 00   - ?? start marker
+64 01   - byte count (lsb/msb excludes header/trailer)
+8e 0b   - checksum (lsb/msb excludes header/trailer)
+-----------------------------------------------------------
+controller set up
+
+00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00
+                   |     |                 |
+                   |     |                  -- host ID
+                   |      --flag bits 2
+                   |        0x00000001= scan order hi->low
+                   |            (default 0x00 - scan low->hi)
+                    --flag bits 1
+                       0x00000001 scam enable
+                       0x00000010 parity enable
+                       0x00000100 verbose boot msgs
+
+remaining bytes unknown - they do not appear to change in my
+current set up for any of the controllers.
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+boot configuration
+
+boot order set by order of the devices in this table
+
+04 00 0f 00 00 10 00 50 00 00 01 00 00 62 -- 1st controller
+04 00 03 00 00 10 00 58 00 00 01 00 00 63    2nd controller
+04 00 01 00 00 10 00 48 00 00 01 00 00 61    3rd controller
+00 00 00 00 00 00 00 00 00 00 00 00 00 00    4th controller
+       |  |  |  |     |        |     |  |
+       |  |  |  |     |        |      ---- PCI io port adr
+       |  |  |  |     |         --0x01 init/scan at boot time
+       |  |  |  |      --PCI device/function number (0xdddddfff)
+       |  |   ----- ?? PCI vendor ID (lsb/msb)
+        ----PCI device ID (lsb/msb)
+
+?? use of this data is a guess but seems reasonable
+
+remaining bytes unknown - they do not appear to change in my
+current set up
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+device set up (up to 16 devices - includes controller)
+
+0f 00 08 08 64 00 0a 00 - id 0
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 
+0f 00 08 08 64 00 0a 00 - id 15
+ |     |  |  |     |  |
+ |     |  |  |      ----timeout (lsb/msb)
+ |     |  |   --synch period (0x?? 40 Mtrans/sec- fast 40) (probably 0x28)
+ |     |  |                  (0x30 20 Mtrans/sec- fast 20)
+ |     |  |                  (0x64 10 Mtrans/sec- fast )
+ |     |  |                  (0xc8  5 Mtrans/sec)
+ |     |  |                  (0x00  asynchronous)
+ |     |   -- ?? max sync offset (0x08 in NVRAM on 53c810a) 
+ |     |                         (0x10 in NVRAM on 53c875)
+ |      --device bus width (0x08 narrow)
+ |                         (0x10 16 bit wide)
+  --flag bits
+    0x00000001 - disconnect enabled
+    0x00000010 - scan at boot time
+    0x00000100 - scan luns
+    0x00001000 - queue tags enabled
+
+remaining bytes unknown - they do not appear to change in my
+current set up
+
+?? use of this data is a guess but seems reasonable 
+(but it could be max bus width)
+
+default set up for 53c810a NVRAM
+default set up for 53c875 NVRAM - bus width     - 0x10
+                                - sync offset ? - 0x10
+                                - sync period   - 0x30
+-----------------------------------------------------------
+?? spare device space (32 bit bus ??)
+
+00 00 00 00 00 00 00 00  (19x8bytes)
+.
+.
+00 00 00 00 00 00 00 00
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+trailer
+
+fe fe   - ? end marker ?
+00 00
+00 00
+
+default set up is identical for 53c810a and 53c875 NVRAM
+-----------------------------------------------------------
+
+
+
+17.3 Tekram NVRAM layout
+
+nvram 64x16 (1024 bit)
+
+Drive settings
+
+Drive ID 0-15 (addr 0x0yyyy0 = device setup, yyyy = ID)
+              (addr 0x0yyyy1 = 0x0000)
+
+    x x x x  x x x x  x x x x  x x x x
+               | | |      | |  | | | |
+               | | |      | |  | | |  ----- parity check   0 - off
+               | | |      | |  | | |                       1 - on
+               | | |      | |  | | |
+               | | |      | |  | |  ------- sync neg       0 - off
+               | | |      | |  | |                         1 - on
+               | | |      | |  | |
+               | | |      | |  |  --------- disconnect     0 - off
+               | | |      | |  |                           1 - on
+               | | |      | |  |
+               | | |      | |   ----------- start cmd      0 - off
+               | | |      | |                              1 - on
+               | | |      | |
+               | | |      |  -------------- tagged cmds    0 - off
+               | | |      |                                1 - on
+               | | |      | 
+               | | |       ---------------- wide neg       0 - off
+               | | |                                       1 - on
+               | | |
+                --------------------------- sync rate      0 - 10.0 Mtrans/sec
+                                                           1 -  8.0
+                                                           2 -  6.6
+                                                           3 -  5.7
+                                                           4 -  5.0
+                                                           5 -  4.0
+                                                           6 -  3.0
+                                                           7 -  2.0
+                                                           7 -  2.0
+                                                           8 - 20.0
+                                                           9 - 16.7
+                                                           a - 13.9
+                                                           b - 11.9
+
+Global settings
+
+Host flags 0 (addr 0x100000, 32) 
+
+    x x x x  x x x x  x x x x  x x x x
+    | | | |  | | | |           | | | |
+    | | | |  | | | |            ----------- host ID    0x00 - 0x0f
+    | | | |  | | | |
+    | | | |  | | |  ----------------------- support for    0 - off
+    | | | |  | | |                          > 2 drives     1 - on
+    | | | |  | | | 
+    | | | |  | |  ------------------------- support drives 0 - off
+    | | | |  | |                            > 1Gbytes      1 - on
+    | | | |  | |
+    | | | |  |  --------------------------- bus reset on   0 - off
+    | | | |  |                                power on     1 - on
+    | | | |  |
+    | | | |   ----------------------------- active neg     0 - off
+    | | | |                                                1 - on
+    | | | |
+    | | |  -------------------------------- imm seek       0 - off
+    | | |                                                  1 - on
+    | | |
+    | |  ---------------------------------- scan luns      0 - off
+    | |                                                    1 - on
+    | |
+     -------------------------------------- removable      0 - disable
+                                            as BIOS dev    1 - boot device
+                                                           2 - all
+
+Host flags 1 (addr 0x100001, 33)
+
+    x x x x  x x x x  x x x x  x x x x
+               | | |             | | | 
+               | | |              --------- boot delay     0 -   3 sec
+               | | |                                       1 -   5
+               | | |                                       2 -  10
+               | | |                                       3 -  20
+               | | |                                       4 -  30
+               | | |                                       5 -  60
+               | | |                                       6 - 120
+               | | |
+                --------------------------- max tag cmds   0 -  2
+                                                           1 -  4
+                                                           2 -  8
+                                                           3 - 16
+                                                           4 - 32
+
+Host flags 2 (addr 0x100010, 34)
+
+    x x x x  x x x x  x x x x  x x x x
+                                     |
+                                      ----- F2/F6 enable   0 - off ???
+                                                           1 - on  ???
+
+checksum (addr 0x111111)
+
+checksum = 0x1234 - (sum addr 0-63)
+
+----------------------------------------------------------------------------
+
+default nvram data:
+
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000
+0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 0x0037 0x0000 
+
+0x0f07 0x0400 0x0001 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
+0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0xfbbc
+
 ===============================================================================
 End of NCR53C8XX driver README file
 
diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.1.48/linux/drivers/scsi/aic7xxx.c	Mon Aug  4 16:25:38 1997
+++ linux/drivers/scsi/aic7xxx.c	Wed Aug  6 09:49:28 1997
@@ -4638,9 +4638,9 @@
     {
       old_verbose = aic7xxx_verbose;
       printk(KERN_INFO "aic7xxx: Warning - detected auto-termination.  Please "
-                       "verify driver");
+                       "verify driver\n");
       printk(KERN_INFO "         detected settings and use manual termination "
-                       "if necessary."); 
+                       "if necessary.\n"); 
 
       /* Configure auto termination. */
       outb(SEECS | SEEMS, p->base + SEECTL);
diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c
--- v2.1.48/linux/drivers/scsi/aic7xxx_proc.c	Mon Aug  4 16:25:38 1997
+++ linux/drivers/scsi/aic7xxx_proc.c	Wed Aug  6 21:51:14 1997
@@ -76,7 +76,6 @@
 {
   struct Scsi_Host *HBAptr;
   struct aic7xxx_host *p;
-  static u8 buff[512];
   int   i;
   int   found = FALSE;
   int   size = 0;
@@ -129,11 +128,6 @@
     return (aic7xxx_set_info(buffer, length, HBAptr));
   }
 
-  if (offset == 0)
-  {
-    memset(buff, 0, sizeof(buff));
-  }
-
   p = (struct aic7xxx_host *) HBAptr->hostdata;
 
   size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
@@ -142,7 +136,14 @@
 #if 0
   size += sprintf(BLS, "%s\n", rcs_version(AIC7XXX_SEQ_VER));
 #endif
+  if (size > 512)
+    printk(KERN_CRIT "aic7xxx: possible overflow at first position\n");
   len += size; pos = begin + len; size = 0;
+  if (pos < offset)
+  {
+    begin = pos;
+    len = 0;
+  }
 
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Compile Options:\n");
@@ -167,7 +168,16 @@
 #else
   size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Disabled\n");
 #endif
+  if (size > 512)
+    printk(KERN_CRIT "aic7xxx: possible overflow at second position\n");
   len += size; pos = begin + len; size = 0;
+  if (pos < offset)
+  {
+    begin = pos;
+    len = 0;
+  }
+  else if (pos >= offset + length)
+    goto stop_output;
 
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Adapter Configuration:\n");
@@ -201,7 +211,16 @@
       (p->flags & ULTRA_ENABLED) ? "En" : "Dis");
   size += sprintf(BLS, "     Target Disconnect: %sabled\n",
       p->discenable ? "En" : "Dis");
+  if (size > 512)
+    printk(KERN_CRIT "aic7xxx: possible overflow at third position\n");
   len += size; pos = begin + len; size = 0;
+  if (pos < offset)
+  {
+    begin = pos;
+    len = 0;
+  }
+  else if (pos >= offset + length)
+    goto stop_output;
 
 #ifdef AIC7XXX_PROC_STATS
   {
@@ -210,6 +229,7 @@
 
     /*
      * XXX: Need to fix this to avoid overflow...
+     * Fixed - gordo.
      */
     size += sprintf(BLS, "\n");
     size += sprintf(BLS, "Statistics:\n");
@@ -247,9 +267,18 @@
               sp->w_bins[9]);
           size += sprintf(BLS, "\n");
         }
+	if (size > 512)
+	  printk(KERN_CRIT "aic7xxx: possible overflow at loop %d:%d\n", target, lun);
+        len += size; pos = begin + len; size = 0;
+	if (pos < offset)
+	{
+	  begin = pos;
+	  len = 0;
+	}
+	else if (pos >= offset + length)
+	  goto stop_output;
       }
     }
-    len += size; pos = begin + len; size = 0;
   }
 #endif /* AIC7XXX_PROC_STATS */
 
@@ -257,7 +286,11 @@
   proc_debug("2pos: %ld offset: %ld len: %d\n", pos, offset, len);
   *start = buffer + (offset - begin);   /* Start of wanted data */
   len -= (offset - begin);      /* Start slop */
-  if (len > length)
+  if (len < 0)
+  {
+    len = 0;			/* off end of file */
+  }
+  else if (len > length)
   {
     len = length;               /* Ending slop */
   }
diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- v2.1.48/linux/drivers/scsi/ncr53c8xx.c	Tue May 13 22:41:13 1997
+++ linux/drivers/scsi/ncr53c8xx.c	Wed Aug  6 13:02:59 1997
@@ -36,11 +36,35 @@
 **  And has been ported to NetBSD by
 **          Charles M. Hannum           <mycroft@gnu.ai.mit.edu>
 **
+**-----------------------------------------------------------------------------
+**
+**                     Brief history
+**
+**  December 10 1995 by Gerard Roudier:
+**     Initial port to Linux.
+**
+**  June 23 1996 by Gerard Roudier:
+**     Support for 64 bits architectures (Alpha).
+**
+**  November 30 1996 by Gerard Roudier:
+**     Support for Fast-20 scsi.
+**     Support for large DMA fifo and 128 dwords bursting.
+**
+**  February 27 1997 by Gerard Roudier:
+**     Support for Fast-40 scsi.
+**     Support for on-Board RAM.
+**
+**  May 3 1997 by Gerard Roudier:
+**     Full support for scsi scripts instructions pre-fetching.
+**
+**  May 19 1997 by Richard Waltham <dormouse@farsrobt.demon.co.uk>:
+**     Support for NvRAM detection and reading.
+**
 *******************************************************************************
 */
 
 /*
-**	9 May 1997, version 2.1b
+**	26 July 1997, version 2.4
 **
 **	Supported SCSI-II features:
 **	    Synchronous negotiation
@@ -51,13 +75,13 @@
 **	    Etc...
 **
 **	Supported NCR chips:
-**		53C810		(NCR BIOS in flash-bios required) 
-**		53C815		(~53C810 with on board rom BIOS)
-**		53C820		(Wide, NCR BIOS in flash bios required)
-**		53C825		(Wide, ~53C820 with on board rom BIOS)
-**		53C860		(Narrow fast 20, BIOS required)
-**		53C875		(Wide fast 20 with on board rom BIOS)
-**		53C895		(Ultra2 80 MB/s with on board rom BIOS)
+**		53C810		(8 bits, Fast SCSI-2, no rom BIOS) 
+**		53C815		(8 bits, Fast SCSI-2, on board rom BIOS)
+**		53C820		(Wide,   Fast SCSI-2, no rom BIOS)
+**		53C825		(Wide,   Fast SCSI-2, on board rom BIOS)
+**		53C860		(8 bits, Fast 20,     no rom BIOS)
+**		53C875		(Wide,   Fast 20,     on board rom BIOS)
+**		53C895		(Wide,   Fast 40,     on board rom BIOS)
 **
 **	Other features:
 **		Memory mapped IO (linux-1.3.X and above only)
@@ -67,10 +91,6 @@
 
 #define SCSI_NCR_DEBUG_FLAGS	(0)		
 
-#define NCR_DATE "pl24 96/12/14"
-
-#define NCR_VERSION	(2)
-
 #define NCR_GETCC_WITHMSG
 
 /*==========================================================
@@ -186,7 +206,14 @@
 #else
 #define MAX_LUN    (1)
 #endif
+
+/*
+**    Asynchronous pre-scaler (ns). Shall be 40
+*/
  
+#ifndef SCSI_NCR_MIN_ASYNC
+#define SCSI_NCR_MIN_ASYNC (40)
+#endif
 
 /*
 **    The maximum number of jobs scheduled for starting.
@@ -279,7 +306,9 @@
 **	architecture.
 */
 
-static inline vm_offset_t remap_pci_mem(u_long base, u_long size)
+__initfunc(
+static vm_offset_t remap_pci_mem(u_long base, u_long size)
+)
 {
 	u_long page_base	= ((u_long) base) & PAGE_MASK;
 	u_long page_offs	= ((u_long) base) - page_base;
@@ -292,7 +321,9 @@
 	return (vm_offset_t) (page_remapped ? (page_remapped + page_offs) : 0UL);
 }
 
-static inline void unmap_pci_mem(vm_offset_t vaddr, u_long size)
+__initfunc(
+static void unmap_pci_mem(vm_offset_t vaddr, u_long size)
+)
 {
 	if (vaddr)
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
@@ -472,7 +503,8 @@
 	unsigned ultra_scsi	: 2;
 	unsigned force_sync_nego: 1;
 	unsigned reverse_probe: 1;
-	unsigned pci_fix_up: 2;
+	unsigned pci_fix_up: 4;
+	u_char	use_nvram;
 	u_char	verbose;
 	u_char	default_tags;
 	u_short	default_sync;
@@ -511,7 +543,149 @@
 
 static void ncr53c8xx_timeout(unsigned long np);
 
-#define bootverbose (driver_setup.verbose)
+#define initverbose (driver_setup.verbose)
+#define bootverbose (np->verbose)
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+/*
+**	Symbios NvRAM data format
+*/
+#define SYMBIOS_NVRAM_SIZE 368
+#define SYMBIOS_NVRAM_ADDRESS 0x100
+
+struct Symbios_nvram {
+/* Header 6 bytes */
+	u_short start_marker;	/* 0x0000 */
+	u_short byte_count;	/* excluding header/trailer */
+	u_short checksum;
+
+/* Controller set up 20 bytes */
+	u_short	word0;		/* 0x3000 */
+	u_short	word2;		/* 0x0000 */
+	u_short	word4;		/* 0x0000 */
+	u_short	flags;
+#define SYMBIOS_SCAM_ENABLE	(1)
+#define SYMBIOS_PARITY_ENABLE	(1<<1)
+#define SYMBIOS_VERBOSE_MSGS	(1<<2)
+	u_short	flags1;
+#define SYMBIOS_SCAN_HI_LO	(1)
+	u_short	word10;		/* 0x00 */
+	u_short	word12;		/* 0x00 */
+	u_char	host_id;
+	u_char	byte15;		/* 0x04 */
+	u_short	word16;		/* 0x0410 */
+	u_short	word18;		/* 0x0000 */
+
+/* Boot order 14 bytes * 4 */
+	struct Symbios_host{
+		u_char	word0;		/* 0x0004:ok / 0x0000:nok */
+		u_short	device_id;	/* PCI device id */
+		u_short	vendor_id;	/* PCI vendor id */
+		u_char	byte6;		/* 0x00 */
+		u_char	device_fn;	/* PCI device/function number << 3*/
+		u_short	word8;
+		u_short	flags;
+#define	SYMBIOS_INIT_SCAN_AT_BOOT	(1)
+		u_short	io_port;	/* PCI io_port address */
+	} host[4];
+
+/* Targets 8 bytes * 16 */
+	struct Symbios_target {
+		u_short	flags;
+#define SYMBIOS_DISCONNECT_ENABLE	(1)
+#define SYMBIOS_SCAN_AT_BOOT_TIME	(1<<1)
+#define SYMBIOS_SCAN_LUNS		(1<<2)
+#define SYMBIOS_QUEUE_TAGS_ENABLED	(1<<3)
+		u_char	bus_width;	/* 0x08/0x10 */
+		u_char	sync_offset;
+		u_char	sync_period;	/* 4*period factor */
+		u_char	byte6;		/* 0x00 */
+		u_short	timeout;
+	} target[16];
+	u_char	spare_devices[19*8];
+	u_char	trailer[6];		/* 0xfe 0xfe 0x00 0x00 0x00 0x00 */
+};
+typedef struct Symbios_nvram	Symbios_nvram;
+typedef struct Symbios_host	Symbios_host;
+typedef struct Symbios_target	Symbios_target;
+
+/*
+**	Tekram NvRAM data format.
+*/
+#define TEKRAM_NVRAM_SIZE 64
+#define TEKRAM_NVRAM_ADDRESS 0
+
+struct Tekram_nvram {
+	struct Tekram_target {
+		u_char	flags;
+#define	TEKRAM_PARITY_CHECK		(1)
+#define TEKRAM_SYNC_NEGO		(1<<1)
+#define TEKRAM_DISCONNECT_ENABLE	(1<<2)
+#define	TEKRAM_START_CMD		(1<<3)
+#define TEKRAM_TAGGED_COMMANDS		(1<<4)
+#define TEKRAM_WIDE_NEGO		(1<<5)
+		u_char	sync_index;
+		u_short	word2;
+	} target[16];
+	u_char	host_id;
+	u_char	flags;
+#define TEKRAM_MORE_THAN_2_DRIVES	(1)
+#define TEKRAM_DRIVES_SUP_1GB		(1<<1)
+#define	TEKRAM_RESET_ON_POWER_ON	(1<<2)
+#define TEKRAM_ACTIVE_NEGATION		(1<<3)
+#define TEKRAM_IMMEDIATE_SEEK		(1<<4)
+#define	TEKRAM_SCAN_LUNS		(1<<5)
+#define	TEKRAM_REMOVABLE_FLAGS		(3<<6)	/* 0: disable; 1: boot device; 2:all */
+	u_char	boot_delay_index;
+	u_char	max_tags_index;
+	u_short	flags1;
+#define TEKRAM_F2_F6_ENABLED		(1)
+	u_short	spare[29];
+};
+typedef struct Tekram_nvram	Tekram_nvram;
+typedef struct Tekram_target	Tekram_target;
+
+static u_char Tekram_sync[12] __initdata = {25,31,37,43,50,62,75,125,12,15,18,21};
+
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
+/*
+**	Structures used by ncr53c8xx_detect/ncr53c8xx_pci_init to 
+**	transmit device configuration to the ncr_attach() function.
+*/
+typedef struct {
+	int	bus;
+	u_char	device_fn;
+	u_int	base;
+	u_int	io_port;
+	int	irq;
+/* port and reg fields to use INB, OUTB macros */
+	u_int	port;
+	volatile struct	ncr_reg	*reg;
+} ncr_slot;
+
+typedef struct {
+	int type;
+#define	SCSI_NCR_SYMBIOS_NVRAM	(1)
+#define	SCSI_NCR_TEKRAM_NVRAM	(2)
+#ifdef	SCSI_NCR_NVRAM_SUPPORT
+	union {
+		Symbios_nvram Symbios;
+		Tekram_nvram Tekram;
+	} data;
+#endif
+} ncr_nvram;
+
+/*
+**	Structure used by ncr53c8xx_detect/ncr53c8xx_pci_init
+**	to save data on each detected board for ncr_attach().
+*/
+typedef struct {
+	ncr_slot  slot;
+	ncr_chip  chip;
+	ncr_nvram *nvram;
+	int attached;
+} ncr_device;
 
 /*==========================================================
 **
@@ -800,6 +974,7 @@
 
 #define	UF_TRACE	(0x01)
 #define	UF_NODISC	(0x02)
+#define	UF_NOSCAN	(0x04)
 
 /*---------------------------------------
 **
@@ -897,6 +1072,14 @@
 	ccb_p   hold_cp;
 
 	/*
+	**	pointer to ccb used for negotiating.
+	**	Avoid to start a nego for all queued commands 
+	**	when tagged command queuing is enabled.
+	*/
+
+	ccb_p   nego_cp;
+
+	/*
 	**	statistical data
 	*/
 
@@ -906,13 +1089,15 @@
 	/*
 	**	user settable limits for sync transfer
 	**	and tagged commands.
+	**	These limits are read from the NVRAM if present.
 	*/
 
 	u_char	usrsync;
-	u_char	usrtags;
 	u_char	usrwide;
+	u_char	usrtags;
 	u_char	usrflag;
 
+	u_char	numtags;
 	u_char	maxtags;
 	u_short	num_good;
 
@@ -1341,13 +1526,13 @@
 	int    unit;			/* Unit number                       */
 	char   chip_name[8];		/* Chip name                         */
 	char   inst_name[16];		/* Instance name                     */
-	u_int  features;		/* Chip features map		     */
 	struct timer_list timer;	/* Timer link header                 */
 	int	ncr_cache;		/* Cache test variable               */
 	Scsi_Cmnd *waiting_list;	/* Waiting list header for commands  */
 					/* that we can't put into the squeue */
 	u_long	settle_time;		/* Reset in progess		     */
 	u_char	release_stage;		/* Synchronisation stage on release  */
+	u_char	verbose;		/* Boot verbosity for this controller*/
 #ifdef	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
 	u_char	debug_error_recovery;
 	u_char	stalling;
@@ -1365,6 +1550,8 @@
 	*/
 	u_short	device_id;
 	u_char	revision_id;
+
+	u_char	sv_scntl0;
 	u_char	sv_scntl3;
 	u_char	sv_dmode;
 	u_char	sv_dcntl;
@@ -1375,6 +1562,8 @@
 	u_char	sv_stest2;
 	u_char	sv_stest4;
 
+	u_char	rv_scntl0;
+	u_char	rv_scntl3;
 	u_char	rv_dmode;
 	u_char	rv_dcntl;
 	u_char	rv_ctest3;
@@ -1382,9 +1571,7 @@
 	u_char	rv_ctest5;
 	u_char	rv_stest2;
 
-	u_char	maxburst;
 	u_char	scsi_mode;
-	u_char	multiplier;
 
 	/*-----------------------------------------------
 	**	Scripts ..
@@ -1443,21 +1630,21 @@
 	u_char	  myaddr;
 
 	/*
-	**	timing parameters
+	**	Max dwords burst supported by the adapter.
 	*/
-	u_char		ns_sync;
-	u_char		rv_scntl3;
+	u_char		maxburst;	/* log base 2 of dwords burst	*/
 
 	/*
-	**	controller chip dependent maximal offset.
+	**	timing parameters
 	*/
-	u_char		maxoffs;
+	u_char		minsync;	/* Minimum sync period factor	*/
+	u_char		maxsync;	/* Maximum sync period factor	*/
+	u_char		maxoffs;	/* Max scsi offset		*/
+	u_char		multiplier;	/* Clock multiplier (1,2,4)	*/
+	u_char		clock_divn;	/* Number of clock divisors	*/
+	u_long		clock_khz;	/* SCSI clock frequency in KHz	*/
+	u_int		features;	/* Chip features map		*/
 
-	/*
-	**	controller scsi clock frequency and available divisors
-	*/
-	u_long		clock_khz;
-	int		clock_divn;
 
 	/*-----------------------------------------------
 	**	Link to the generic SCSI driver
@@ -1690,11 +1877,11 @@
 				(ncb_p np, ncrcmd *src, ncrcmd *dst, int len);
 static  void    ncr_script_fill (struct script * scr, struct scripth * scripth);
 static	int	ncr_scatter	(ccb_p cp, Scsi_Cmnd *cmd);
-static	void	ncr_setmaxtags	(ncb_p np, tcb_p tp, u_long usrtags);
-static	int	ncr_getsync	(ncb_p np, u_char fac, u_char *fakp, u_char *scntl3p);
+static	void	ncr_setmaxtags	(ncb_p np, tcb_p tp, u_long numtags);
+static	void	ncr_getsync	(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p);
 static	void	ncr_setsync	(ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer);
 static	void	ncr_settags     (tcb_p tp, lcb_p lp);
-static	void	ncr_setwide	(ncb_p np, ccb_p cp, u_char wide);
+static	void	ncr_setwide	(ncb_p np, ccb_p cp, u_char wide, u_char ack);
 static	int	ncr_show_msg	(u_char * msg);
 static	int	ncr_snooptest	(ncb_p np);
 static	void	ncr_timeout	(ncb_p np);
@@ -1705,9 +1892,7 @@
 static	void	ncr_usercmd	(ncb_p np);
 #endif
 
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit, 
-		       ncr_chip *chip, u_int base, u_int io_port, 
-		       int irq, int bus, u_char device_fn);
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device);
 
 static void insert_into_waiting_list(ncb_p np, Scsi_Cmnd *cmd);
 static Scsi_Cmnd *retrieve_from_waiting_list(int to_remove, ncb_p np, Scsi_Cmnd *cmd);
@@ -1718,6 +1903,11 @@
 #define requeue_waiting_list(np) process_waiting_list((np), DID_OK)
 #define reset_waiting_list(np) process_waiting_list((np), DID_RESET)
 
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+static	int	ncr_get_Symbios_nvram	(ncr_slot *np, Symbios_nvram *nvram);
+static	int	ncr_get_Tekram_nvram	(ncr_slot *np, Tekram_nvram *nvram);
+#endif
+
 /*==========================================================
 **
 **
@@ -3447,7 +3637,7 @@
 			**	If PREFETCH feature not enabled, remove 
 			**	the NO FLUSH bit if present.
 			*/
-			if ((opcode & SCR_NO_FLUSH) && !(np->features & _F_PFEN)) {
+			if ((opcode & SCR_NO_FLUSH) && !(np->features & FE_PFEN)) {
 				dst[-1] = (opcode & ~SCR_NO_FLUSH);
 				++opchanged;
 			}
@@ -3570,7 +3760,7 @@
 */
 
 #define PRINT_LUN(np, target, lun) \
-printf("%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
+printf(KERN_INFO "%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun))
 
 static void PRINT_ADDR(Scsi_Cmnd *cmd)
 {
@@ -3579,6 +3769,20 @@
 	if (np) PRINT_LUN(np, cmd->target, cmd->lun);
 }
 
+/*==========================================================
+**
+**	NCR chip clock divisor table.
+**	Divisors are multiplied by 10,000,000 in order to make 
+**	calculations more simple.
+**
+**==========================================================
+*/
+
+#define _5M 5000000
+static u_long div_10M[] =
+	{2*_5M, 3*_5M, 4*_5M, 6*_5M, 8*_5M, 12*_5M, 16*_5M};
+
+
 /*===============================================================
 **
 **	Prepare io register values used by ncr_init() according 
@@ -3622,16 +3826,79 @@
 	}
 }
 
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/*
+**	Get target set-up from Symbios format NVRAM.
+*/
+
+__initfunc(
+static void
+	ncr_Symbios_setup_target(ncb_p np, int target, Symbios_nvram *nvram)
+)
+{
+	tcb_p tp = &np->target[target];
+	Symbios_target *tn = &nvram->target[target];
+
+	tp->usrsync = tn->sync_period ? (tn->sync_period + 3) / 4 : 255;
+	tp->usrwide = tn->bus_width == 0x10 ? 1 : 0;
+	tp->usrtags =
+		(tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SCSI_NCR_MAX_TAGS : 0;
+
+	if (!(tn->flags & SYMBIOS_DISCONNECT_ENABLE))
+		tp->usrflag |= UF_NODISC;
+	if (!(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME))
+		tp->usrflag |= UF_NOSCAN;
+}
+
+/*
+**	Get target set-up from Tekram format NVRAM.
+*/
+
+__initfunc(
+static void
+	ncr_Tekram_setup_target(ncb_p np, int target, Tekram_nvram *nvram)
+)
+{
+	tcb_p tp = &np->target[target];
+	struct Tekram_target *tn = &nvram->target[target];
+	int i;
+
+	if (tn->flags & TEKRAM_SYNC_NEGO) {
+		i = tn->sync_index & 0xf;
+		tp->usrsync = i < 12 ? Tekram_sync[i] : 255;
+	}
+
+	tp->usrwide = (tn->flags & TEKRAM_WIDE_NEGO) ? 1 : 0;
+
+	if (tn->flags & TEKRAM_TAGGED_COMMANDS) {
+		tp->usrtags = 2 << nvram->max_tags_index;
+		if (tp->usrtags > SCSI_NCR_MAX_TAGS)
+			tp->usrtags = SCSI_NCR_MAX_TAGS;
+	}
+
+	if (!(tn->flags & TEKRAM_DISCONNECT_ENABLE))
+		tp->usrflag = UF_NODISC;
+ 
+	/* If any device does not support parity, we will not use this option */
+	if (!(tn->flags & TEKRAM_PARITY_CHECK))
+		np->rv_scntl0  &= ~0x0a; /* SCSI parity checking disabled */
+}
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
 __initfunc(
-static int ncr_prepare_setting(ncb_p np)
+static int ncr_prepare_setting(ncb_p np, ncr_nvram *nvram)
 )
 {
 	u_char	burst_max;
+	u_long	period;
+	int i;
 
 	/*
 	**	Save assumed BIOS setting
 	*/
 
+	np->sv_scntl0	= INB(nc_scntl0) & 0x0a;
 	np->sv_scntl3	= INB(nc_scntl3) & 0x07;
 	np->sv_dmode	= INB(nc_dmode)  & 0xce;
 	np->sv_dcntl	= INB(nc_dcntl)  & 0xa8;
@@ -3643,35 +3910,73 @@
 	np->sv_stest4	= INB(nc_stest4);
 
 	/*
+	**	Wide ?
+	*/
+
+	np->maxwide	= (np->features & FE_WIDE)? 1 : 0;
+
+	/*
 	**	Get the frequency of the chip's clock.
 	**	Find the right value for scntl3.
 	*/
 
-	if	(np->features & _F_QUAD)
+	if	(np->features & FE_QUAD)
 		np->multiplier	= 4;
-	else if	(np->features & _F_DBLR)
+	else if	(np->features & FE_DBLR)
 		np->multiplier	= 2;
 	else
 		np->multiplier	= 1;
 
-	np->clock_khz	= (np->features & _F_CLK80)? 80000 : 40000;
+	np->clock_khz	= (np->features & FE_CLK80)? 80000 : 40000;
 	np->clock_khz	*= np->multiplier;
 
 	if (np->clock_khz != 40000)
 		ncr_getclock(np, np->multiplier);
 
-	if	(np->clock_khz <=  25000)	np->rv_scntl3 = 0x01;
-	else if	(np->clock_khz <=  37500)	np->rv_scntl3 = 0x02;
-	else if	(np->clock_khz <=  50000)	np->rv_scntl3 = 0x03;
-	else if	(np->clock_khz <=  75000)	np->rv_scntl3 = 0x04;
-	else if	(np->clock_khz <= 100000)	np->rv_scntl3 = 0x05;
-	else if	(np->clock_khz <= 150000)	np->rv_scntl3 = 0x06;
-	else					np->rv_scntl3 = 0x07;
+	/*
+	 * Divisor to be used for async (timer pre-scaler).
+	 */
+	i = np->clock_divn - 1;
+	while (i >= 0) {
+		--i;
+		if (10ul * SCSI_NCR_MIN_ASYNC * np->clock_khz > div_10M[i]) {
+			++i;
+			break;
+		}
+	}
+	np->rv_scntl3 = i+1;
+
+	/*
+	 * Minimum synchronous period factor supported by the chip.
+	 * Btw, 'period' is in tenths of nanoseconds.
+	 */
+
+	period = (4 * div_10M[0] + np->clock_khz - 1) / np->clock_khz;
+	if	(period <= 250)		np->minsync = 10;
+	else if	(period <= 303)		np->minsync = 11;
+	else if	(period <= 500)		np->minsync = 12;
+	else				np->minsync = (period + 40 - 1) / 40;
+
+	/*
+	 * Check against chip SCSI standard support (SCSI-2,ULTRA,ULTRA2).
+	 */
+
+	if	(np->minsync < 25 && !(np->features & (FE_ULTRA|FE_ULTRA2)))
+		np->minsync = 25;
+	else if	(np->minsync < 12 && !(np->features & FE_ULTRA2))
+		np->minsync = 12;
+
+	/*
+	 * Maximum synchronous period factor supported by the chip.
+	 */
+
+	period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz);
+	np->maxsync = period > 2540 ? 254 : period / 10;
 
 	/*
 	**	Get on-board RAM bus address when supported
 	*/
-	if (np->features & _F_RAM) {
+	if (np->features & FE_RAM) {
 		OUTONB(nc_ctest2, 0x8);
 		np->paddr2 = INL(nc_scr0);
 		OUTOFFB(nc_ctest2, 0x8);
@@ -3681,6 +3986,7 @@
 	**	Prepare initial value of other IO registers
 	*/
 #if defined SCSI_NCR_TRUST_BIOS_SETTING
+	np->rv_scntl0	= np->sv_scntl0;
 	np->rv_dmode	= np->sv_dmode;
 	np->rv_dcntl	= np->sv_dcntl;
 	np->rv_ctest3	= np->sv_ctest3;
@@ -3688,12 +3994,6 @@
 	np->rv_ctest5	= np->sv_ctest5;
 	burst_max	= burst_code(np->sv_dmode, np->sv_ctest4, np->sv_ctest5);
 #else
-	np->rv_dmode	= 0;
-	np->rv_dcntl	= 0;
-	np->rv_ctest3	= 0;
-	np->rv_ctest4	= 0;
-	np->rv_ctest5	= 0;
-	np->rv_stest2	= 0;
 
 	/*
 	**	Select burst length (dwords)
@@ -3709,19 +4009,19 @@
 	/*
 	**	Select all supported special features
 	*/
-	if (np->features & _F_ERL)
+	if (np->features & FE_ERL)
 		np->rv_dmode	|= ERL;		/* Enable Read Line */
-	if (np->features & _F_BOF)
+	if (np->features & FE_BOF)
 		np->rv_dmode	|= BOF;		/* Burst Opcode Fetch */
-	if (np->features & _F_ERMP)
+	if (np->features & FE_ERMP)
 		np->rv_dmode	|= ERMP;	/* Enable Read Multiple */
-	if (np->features & _F_PFEN)
+	if (np->features & FE_PFEN)
 		np->rv_dcntl	|= PFEN;	/* Prefetch Enable */
-	if (np->features & _F_CLSE)
+	if (np->features & FE_CLSE)
 		np->rv_dcntl	|= CLSE;	/* Cache Line Size Enable */
-	if (np->features & _F_WRIE)
+	if (np->features & FE_WRIE)
 		np->rv_ctest3	|= WRIE;	/* Write and Invalidate */
-	if (np->features & _F_DFS)
+	if (np->features & FE_DFS)
 		np->rv_ctest5	|= DFS;		/* Dma Fifo Size */
 
 	/*
@@ -3729,6 +4029,34 @@
 	*/
 	if (driver_setup.master_parity)
 		np->rv_ctest4	|= MPEE;	/* Master parity checking */
+	if (driver_setup.scsi_parity)
+		np->rv_scntl0	|= 0x0a;	/*  full arb., ena parity, par->ATN  */
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	/*
+	**	Get parity checking, host ID and verbose mode from NVRAM
+	**/
+	if (nvram) {
+		switch(nvram->type) {
+		case SCSI_NCR_TEKRAM_NVRAM:
+			np->myaddr = nvram->data.Tekram.host_id & 0x0f;
+			break;
+		case SCSI_NCR_SYMBIOS_NVRAM:
+			if (!(nvram->data.Symbios.flags & SYMBIOS_PARITY_ENABLE))
+				np->rv_scntl0  &= ~0x0a;
+			np->myaddr = nvram->data.Symbios.host_id & 0x0f;
+			if (nvram->data.Symbios.flags & SYMBIOS_VERBOSE_MSGS)
+				np->verbose += 1;
+			break;
+		}
+	}
+#endif
+	/*
+	**  Get SCSI addr of host adapter (set by bios?).
+	*/
+	if (!np->myaddr) np->myaddr = INB(nc_scid) & 0x07;
+	if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
+
 
 #endif /* SCSI_NCR_TRUST_BIOS_SETTING */
 
@@ -3738,21 +4066,31 @@
 	ncr_init_burst(np, burst_max);
 
 	/*
-	**	Set differential mode.
-	*/
-	switch(driver_setup.diff_support) {
-	case 3:
-		if (INB(nc_gpreg) & 0x08)
+	**	Set differential mode and LED support.
+	**	Ignore these features for boards known to use a 
+	**	specific GPIO wiring (Tekram only for now).
+	**	Probe initial setting of GPREG and GPCNTL for 
+	**	other ones.
+	*/
+	if (!nvram || nvram->type != SCSI_NCR_TEKRAM_NVRAM) {
+		switch(driver_setup.diff_support) {
+		case 3:
+			if (INB(nc_gpreg) & 0x08)
 			break;
-	case 2:
-		np->rv_stest2	|= 0x20;
-		break;
-	case 1:
-		np->rv_stest2	|= (np->sv_stest2 & 0x20);
-		break;
-	default:
-		break;
+		case 2:
+			np->rv_stest2	|= 0x20;
+			break;
+		case 1:
+			np->rv_stest2	|= (np->sv_stest2 & 0x20);
+			break;
+		default:
+			break;
+		}
 	}
+	if ((driver_setup.led_pin ||
+	     (nvram && nvram->type == SCSI_NCR_SYMBIOS_NVRAM)) &&
+	    !(np->sv_gpcntl & 0x01))
+		np->features |= FE_LED0;
 
 	/*
 	**	Set irq mode.
@@ -3762,36 +4100,167 @@
 		np->rv_dcntl	|= IRQM;
 		break;
 	case 1:
-		np->rv_stest2	|= (np->sv_dcntl & IRQM);
+		np->rv_dcntl	|= (np->sv_dcntl & IRQM);
 		break;
 	default:
 		break;
 	}
 
 	/*
+	**	Configure targets according to driver setup.
+	**	If NVRAM present get targets setup from NVRAM.
+	**	Allow to override sync, wide and NOSCAN from 
+	**	boot command line.
+	*/
+	for (i = 0 ; i < MAX_TARGET ; i++) {
+		tcb_p tp = &np->target[i];
+
+		tp->usrsync = 255;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+		if (nvram) {
+			switch(nvram->type) {
+			case SCSI_NCR_TEKRAM_NVRAM:
+				ncr_Tekram_setup_target(np, i, &nvram->data.Tekram);
+				break;
+			case SCSI_NCR_SYMBIOS_NVRAM:
+				ncr_Symbios_setup_target(np, i, &nvram->data.Symbios);
+				break;
+			}
+			if (driver_setup.use_nvram & 0x2)
+				tp->usrsync = driver_setup.default_sync;
+			if (driver_setup.use_nvram & 0x4)
+				tp->usrwide = driver_setup.max_wide;
+			if (driver_setup.use_nvram & 0x8)
+				tp->usrflag &= ~UF_NOSCAN;
+		}
+		else {
+#else
+		if (1) {
+#endif
+			tp->usrsync = driver_setup.default_sync;
+			tp->usrwide = driver_setup.max_wide;
+			tp->usrtags = driver_setup.default_tags;
+			if (!driver_setup.disconnection)
+				np->target[i].usrflag = UF_NODISC;
+		}
+	}
+
+	/*
 	**	Announce all that stuff to user.
 	*/
+
+	i = nvram ? nvram->type : 0;
+	printf(KERN_INFO "%s: %sID %d, Fast-%d%s%s\n", ncr_name(np),
+		i  == SCSI_NCR_SYMBIOS_NVRAM ? "Symbios format NVRAM, " :
+		(i == SCSI_NCR_TEKRAM_NVRAM  ? "Tekram format NVRAM, " : ""),
+		np->myaddr,
+		np->minsync < 12 ? 40 : (np->minsync < 25 ? 20 : 10),
+		(np->rv_scntl0 & 0xa)	? ", Parity Checking"	: ", NO Parity",
+		(np->rv_stest2 & 0x20)	? ", Differential"	: "");
+
 	if (bootverbose > 1) {
-		printf ("%s: initial value of SCNTL3 = %02x, final = %02x\n",
-			ncr_name(np), np->sv_scntl3, np->rv_scntl3);
-		printf ("%s: initial value of dmode/dcntl/ctest3/4/5 = (hex) %02x/%02x/%02x/%02x/%02x\n",
-			ncr_name(np), np->sv_dmode, np->sv_dcntl, np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
-		printf ("%s: final value of dmode/dcntl/ctest3/4/5 = (hex) %02x/%02x/%02x/%02x/%02x\n",
-			ncr_name(np), np->rv_dmode, np->rv_dcntl, np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
-		if (np->rv_stest2 & 0x20)
-			printf ("%s: DIFF mode set\n", ncr_name(np));
+		printf ("%s: initial SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+			"(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+			ncr_name(np), np->sv_scntl3, np->sv_dmode, np->sv_dcntl,
+			np->sv_ctest3, np->sv_ctest4, np->sv_ctest5);
+
+		printf ("%s: final   SCNTL3/DMODE/DCNTL/CTEST3/4/5 = "
+			"(hex) %02x/%02x/%02x/%02x/%02x/%02x\n",
+			ncr_name(np), np->rv_scntl3, np->rv_dmode, np->rv_dcntl,
+			np->rv_ctest3, np->rv_ctest4, np->rv_ctest5);
 	}
 
 	if (bootverbose && np->paddr2)
-		printf ("%s: on-board RAM at 0x%lx\n", ncr_name(np), np->paddr2);
-
-	if (bootverbose && np->ns_sync < 25)
-		printf ("%s: Ultra%s SCSI support enabled\n", ncr_name(np),
-			np->ns_sync < 12 ? "-2": "");
+		printf (KERN_INFO "%s: on-board RAM at 0x%lx\n",
+			ncr_name(np), np->paddr2);
 
 	return 0;
 }
 
+
+#ifdef SCSI_NCR_DEBUG_NVRAM
+
+__initfunc(
+void ncr_display_Symbios_nvram(ncb_p np, Symbios_nvram *nvram)
+)
+{
+	int i;
+
+	/* display Symbios nvram host data */
+	printf("%s: HOST ID=%d%s%s%s%s\n",
+		ncr_name(np), nvram->host_id & 0x0f,
+		(nvram->flags  & SYMBIOS_SCAM_ENABLE)	? " SCAM"	:"",
+		(nvram->flags  & SYMBIOS_PARITY_ENABLE)	? " PARITY"	:"",
+		(nvram->flags  & SYMBIOS_VERBOSE_MSGS)	? " VERSBOSE"	:"", 
+		(nvram->flags1 & SYMBIOS_SCAN_HI_LO)	? " HI_LO"	:"");
+
+	/* display Symbios nvram drive data */
+	for (i = 0 ; i < 15 ; i++) {
+		struct Symbios_target *tn = &nvram->target[i];
+		printf("%s-%d:%s%s%s%s WIDTH=%d SYNC=%d TMO=%d\n",
+		ncr_name(np), i,
+		(tn->flags & SYMBIOS_DISCONNECT_ENABLE)	? " DISC"	: "",
+		(tn->flags & SYMBIOS_SCAN_AT_BOOT_TIME)	? " SCAN_BOOT"	: "",
+		(tn->flags & SYMBIOS_SCAN_LUNS)		? " SCAN_LUNS"	: "",
+		(tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? " TCQ"	: "",
+		tn->bus_width,
+		tn->sync_period / 4,
+		tn->timeout);
+	}
+}
+
+static u_char Tekram_boot_delay[7] __initdata = {3, 5, 10, 20, 30, 60, 120};
+
+__initfunc(
+void ncr_display_Tekram_nvram(ncb_p np, Tekram_nvram *nvram)
+)
+{
+	int i, tags, boot_delay;
+	char *rem;
+
+	/* display Tekram nvram host data */
+	tags = 2 << nvram->max_tags_index;
+	boot_delay = 0;
+	if (nvram->boot_delay_index < 6)
+		boot_delay = Tekram_boot_delay[nvram->boot_delay_index];
+	switch((nvram->flags & TEKRAM_REMOVABLE_FLAGS) >> 6) {
+	default:
+	case 0:	rem = "";			break;
+	case 1: rem = " REMOVABLE=boot device";	break;
+	case 2: rem = " REMOVABLE=all";		break;
+	}
+
+	printf("%s: HOST ID=%d%s%s%s%s%s%s%s%s%s BOOT DELAY=%d tags=%d\n",
+		ncr_name(np), nvram->host_id & 0x0f,
+		(nvram->flags1 & SYMBIOS_SCAM_ENABLE)	? " SCAM"	:"",
+		(nvram->flags & TEKRAM_MORE_THAN_2_DRIVES) ? " >2DRIVES"	:"",
+		(nvram->flags & TEKRAM_DRIVES_SUP_1GB)	? " >1GB"	:"",
+		(nvram->flags & TEKRAM_RESET_ON_POWER_ON) ? " RESET"	:"",
+		(nvram->flags & TEKRAM_ACTIVE_NEGATION)	? " ACT_NEG"	:"",
+		(nvram->flags & TEKRAM_IMMEDIATE_SEEK)	? " IMM_SEEK"	:"",
+		(nvram->flags & TEKRAM_SCAN_LUNS)	? " SCAN_LUNS"	:"",
+		(nvram->flags1 & TEKRAM_F2_F6_ENABLED)	? " F2_F6"	:"",
+		rem, boot_delay, tags);
+
+	/* display Tekram nvram drive data */
+	for (i = 0; i <= 15; i++) {
+		int sync, j;
+		struct Tekram_target *tn = &nvram->target[i];
+		j = tn->sync_index & 0xf;
+		sync = j < 12 ? Tekram_sync[j] : 255;
+		printf("%s-%d:%s%s%s%s%s%s PERIOD=%d\n",
+		ncr_name(np), i,
+		(tn->flags & TEKRAM_PARITY_CHECK)	? " PARITY"	: "",
+		(tn->flags & TEKRAM_SYNC_NEGO)		? " SYNC"	: "",
+		(tn->flags & TEKRAM_DISCONNECT_ENABLE)	? " DISC"	: "",
+		(tn->flags & TEKRAM_START_CMD)		? " START"	: "",
+		(tn->flags & TEKRAM_TAGGED_COMMANDS)	? " TCQ"	: "",
+		(tn->flags & TEKRAM_WIDE_NEGO)		? " WIDE"	: "",
+		sync);
+	}
+}
+#endif /* SCSI_NCR_DEBUG_NVRAM */
+
 /*
 **	Host attach and initialisations.
 **
@@ -3803,18 +4272,18 @@
 */
 
 __initfunc(
-static int ncr_attach (Scsi_Host_Template *tpnt, int unit, 
-		       ncr_chip *chip, u_int base, u_int io_port, 
-		       int irq, int bus, u_char device_fn)
+static int ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device)
 )
 {
         struct host_data *host_data;
 	ncb_p np;
         struct Scsi_Host *instance = 0;
 	u_long flags = 0;
+	ncr_nvram *nvram = device->nvram;
 
-printf("ncr53c8xx: unit=%d chip=%s rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n",
-	unit, chip->name, chip->revision_id, base, io_port, irq);
+printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
+	device->chip.name, unit, device->chip.revision_id, device->slot.base,
+	device->slot.io_port, device->slot.irq);
 
 	/*
 	**	Allocate host_data structure
@@ -3825,7 +4294,6 @@
 	/*
 	**	Initialize structure.
 	*/
-	instance->irq = irq;
 	host_data = (struct host_data *) instance->hostdata;
 
 	/*
@@ -3842,18 +4310,16 @@
 	/*
 	**	Store input informations in the host data structure.
 	*/
-	strncpy(np->chip_name, chip->name, sizeof(np->chip_name) - 1);
+	strncpy(np->chip_name, device->chip.name, sizeof(np->chip_name) - 1);
 	np->unit	= unit;
+	np->verbose	= driver_setup.verbose;
 	sprintf(np->inst_name, "ncr53c%s-%d", np->chip_name, np->unit);
-	np->device_id	= chip->device_id;
-	np->revision_id	= chip->revision_id;
-	np->features	= chip->features;
-	np->maxwide	= (np->features & _F_WIDE)? 1 : 0;
-	np->ns_sync	= 25;
-	np->clock_khz	= 40000;
-	np->clock_divn	= chip->nr_divisor;
-	np->maxoffs	= chip->offset_max;
-	np->maxburst	= chip->burst_max;
+	np->device_id	= device->chip.device_id;
+	np->revision_id	= device->chip.revision_id;
+	np->features	= device->chip.features;
+	np->clock_divn	= device->chip.nr_divisor;
+	np->maxoffs	= device->chip.offset_max;
+	np->maxburst	= device->chip.burst_max;
 
 	np->script0  =
 	(struct script *) (((u_long) &host_data->script_data) & SCR_ALIGN_MASK);
@@ -3872,10 +4338,10 @@
 	**	virtual and physical memory.
 	*/
 
-	np->paddr = base;
+	np->paddr = device->slot.base;
 
 #ifndef NCR_IOMAPPED
-	np->vaddr = remap_pci_mem((u_long) base, (u_long) 128);
+	np->vaddr = remap_pci_mem((u_long) np->paddr, (u_long) 128);
 	if (!np->vaddr) {
 		printf("%s: can't map memory mapped IO region\n", ncr_name(np));
 		goto attach_error;
@@ -3898,13 +4364,35 @@
 	**	Try to map the controller chip into iospace.
 	*/
 
-	request_region(io_port, 128, "ncr53c8xx");
-	np->port = io_port;
+	request_region(device->slot.io_port, 128, "ncr53c8xx");
+	np->port = device->slot.io_port;
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	if (nvram) {
+		switch(nvram->type) {
+		case SCSI_NCR_SYMBIOS_NVRAM:
+#ifdef SCSI_NCR_DEBUG_NVRAM
+			ncr_display_Symbios_nvram(np, &nvram->data.Symbios);
+#endif
+			break;
+		case SCSI_NCR_TEKRAM_NVRAM:
+#ifdef SCSI_NCR_DEBUG_NVRAM
+			ncr_display_Tekram_nvram(np, &nvram->data.Tekram);
+#endif
+			break;
+		default:
+			nvram = 0;
+#ifdef SCSI_NCR_DEBUG_NVRAM
+			printf("%s: NVRAM: None or invalid data.\n", ncr_name(np));
+#endif
+		}
+	}
+#endif
 
 	/*
 	**	Do chip dependent initialization.
 	*/
-	(void)ncr_prepare_setting(np);
+	(void)ncr_prepare_setting(np, nvram);
 
 #ifndef NCR_IOMAPPED
 	if (np->paddr2 && sizeof(struct script) <= 4096) {
@@ -3930,7 +4418,8 @@
 #ifndef NCR_IOMAPPED
 	instance->base		= (char *) np->reg;
 #endif
-	instance->io_port	= io_port;
+	instance->irq		= device->slot.irq;
+	instance->io_port	= device->slot.io_port;
 	instance->n_io_port	= 128;
 	instance->dma_channel	= 0;
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
@@ -3956,8 +4445,7 @@
 	**    Patch the script for LED support.
 	*/
 
-	if (driver_setup.led_pin & (~np->sv_gpcntl) & 0x01) {
-		np->features |= _F_LED0;
+	if (np->features & FE_LED0) {
 		np->script0->reselect[0]  = SCR_REG_REG(gpreg, SCR_OR,  0x01);
 		np->script0->reselect1[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
 		np->script0->reselect2[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
@@ -3971,19 +4459,9 @@
 	np->jump_tcb.l_paddr	= NCB_SCRIPTH_PHYS (np, abort);
 
 	/*
-	**  Get SCSI addr of host adapter (set by bios?).
-	*/
-
-	np->myaddr = INB(nc_scid) & 0x07;
-	if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
-
-	/*
 	**	Reset chip.
 	*/
 
-	OUTW (nc_sien , 0);	/* Disable scsi interrupts */
-	OUTB (nc_dien , 0);	/* Disable dma interrupts */
-
 	OUTB (nc_istat,  SRST);
 	DELAY (1000);
 	OUTB (nc_istat,  0   );
@@ -4001,31 +4479,24 @@
 	**	Install the interrupt handler.
 	*/
 #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-#   ifdef SCSI_NCR_SHARE_IRQ
+#ifdef SCSI_NCR_SHARE_IRQ
 	if (bootverbose > 1)
 		printf("%s: requesting shared irq %d (dev_id=0x%lx)\n",
-		        ncr_name(np), irq, (u_long) np);
-	if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT|SA_SHIRQ, "53c8xx", np)) {
-#   else
-	if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx", NULL)) {
-#   endif
+		        ncr_name(np), device->slot.irq, (u_long) np);
+	if (request_irq(device->slot.irq, ncr53c8xx_intr,
+			SA_INTERRUPT|SA_SHIRQ, "ncr53c8xx", np)) {
+#else
+	if (request_irq(device->slot.irq, ncr53c8xx_intr,
+			SA_INTERRUPT, "ncr53c8xx", NULL)) {
+#endif
 #else
-	if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx")) {
+	if (request_irq(device->slot.irq, ncr53c8xx_intr,
+			SA_INTERRUPT, "ncr53c8xx")) {
 #endif
-		printf("%s: request irq %d failure\n", ncr_name(np), irq);
+		printf("%s: request irq %d failure\n", ncr_name(np), device->slot.irq);
 		goto attach_error;
 	}
-	np->irq = irq;
-
-	/*
-	**	Not allow disconnections for all targets if asked by config
-	*/
-
-	if (!driver_setup.disconnection) {
-		int i;
-		for (i = 0 ; i < MAX_TARGET ; i++)
-			np->target[i].usrflag |= UF_NODISC;
-	}
+	np->irq = device->slot.irq;
 
 	/*
 	**	After SCSI devices have been opened, we cannot
@@ -4152,6 +4623,19 @@
 		return(DID_BAD_TARGET);
         }
 
+	/*---------------------------------------------
+	**
+	**	Complete the 1st TEST UNIT READY command
+	**	with error condition if the device is 
+	**	flagged NOSCAN, in order to speed up 
+	**	the boot.
+	**
+	**---------------------------------------------
+	*/
+	if (cmd->cmnd[0] == 0 && (tp->usrflag & UF_NOSCAN)) {
+		tp->usrflag &= ~UF_NOSCAN;
+		return DID_BAD_TARGET;
+	}
 
 	if (DEBUG_FLAGS & DEBUG_TINY) {
 		PRINT_ADDR(cmd);
@@ -4177,15 +4661,13 @@
 
 	/*---------------------------------------------------
 	**
-	**	Enable tagged queue if asked by user
+	**	Enable tagged queue if asked by scsi ioctl
 	**
 	**----------------------------------------------------
 	*/
-	if (driver_setup.default_tags < SCSI_NCR_MAX_TAGS) {
-	 	if (cmd->device && cmd->device->tagged_queue &&
-		    (lp = tp->lp[cmd->lun]) && (!lp->usetags)) {
-			ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
-		}
+	if (!tp->usrtags && cmd->device && cmd->device->tagged_queue) {
+		tp->usrtags = SCSI_NCR_MAX_TAGS;
+		ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS);
 	}
 
 	/*---------------------------------------------------
@@ -4232,7 +4714,8 @@
 
 	nego = 0;
 
-	if (cmd->lun == 0 && (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
+	if (cmd->lun == 0 && !tp->nego_cp && 
+	    (tp->inqdata[2] & 0x7) >= 2 && tp->inqdata[7]) {
 		/*
 		**	negotiate wide transfers ?
 		*/
@@ -4262,6 +4745,15 @@
 				printf ("asynchronous.\n");
 			};
 		};
+
+		/*
+		**	remember nego is pending for the target.
+		**	Avoid to start a nego for all queued commands 
+		**	when tagged command queuing is enabled.
+		*/
+
+		if (nego)
+			tp->nego_cp = cp;
 	};
 
 	/*---------------------------------------------------
@@ -4973,6 +5465,15 @@
 	lp = tp->lp[cmd->lun];
 
 	/*
+	**	We donnot queue more than 1 ccb per target 
+	**	with negotiation at any time. If this ccb was 
+	**	used for negotiation, clear this info in the tcb.
+	*/
+
+	if (cp == tp->nego_cp)
+		tp->nego_cp = 0;
+
+	/*
 	**	Check for parity errors.
 	*/
 
@@ -5080,12 +5581,12 @@
 		**	If tags was reduced due to queue full,
 		**	increase tags if 100 good status received.
 		*/
-		if (tp->usrtags < tp->maxtags) {
+		if (tp->numtags < tp->maxtags) {
 			++tp->num_good;
 			if (tp->num_good >= 100) {
 				tp->num_good = 0;
-				++tp->usrtags;
-				if (tp->usrtags == 1) {
+				++tp->numtags;
+				if (tp->numtags == 1) {
 					PRINT_ADDR(cmd);
 					printf("tagged command queueing resumed\n");
 				}
@@ -5128,10 +5629,10 @@
 		**  Suspend tagged queuing and start good status counter.
 		**  Announce changes to the generic driver.
 		*/
-		if (tp->usrtags) {
+		if (tp->numtags) {
 			PRINT_ADDR(cmd);
 			printf("QUEUE FULL! suspending tagged command queueing\n");
-			tp->usrtags	= 0;
+			tp->numtags	= 0;
 			tp->num_good	= 0;
 			if (lp) {
 				ncr_settags (tp, lp);
@@ -5278,8 +5779,6 @@
 void ncr_init (ncb_p np, char * msg, u_long code)
 {
 	int	i;
-	u_long	usrsync;
-	u_char	usrwide;
 
 	/*
 	**	Reset chip.
@@ -5292,7 +5791,7 @@
 	**	Message.
 	*/
 
-	if (msg) printf ("%s: restart (%s).\n", ncr_name (np), msg);
+	if (msg) printf (KERN_INFO "%s: restart (%s).\n", ncr_name (np), msg);
 
 	/*
 	**	Clear Start Queue
@@ -5317,11 +5816,8 @@
 	*/
 
 	OUTB (nc_istat,  0x00   );	/*  Remove Reset, abort */
-	if (driver_setup.scsi_parity)
-		OUTB (nc_scntl0, 0xca);	/*  full arb., ena parity, par->ATN  */
-	else
-		OUTB (nc_scntl0, 0xc0);	/*  full arb., (no parity) */
-
+	OUTB (nc_scntl0, np->rv_scntl0 | 0xc0);
+					/*  full arb., ena parity, par->ATN  */
 	OUTB (nc_scntl1, 0x00);		/*  odd parity, and remove CRST!! */
 
 	ncr_selectclock(np, np->rv_scntl3);	/* Select SCSI clock */
@@ -5341,30 +5837,6 @@
 	OUTB (nc_stime0, 0x0d	);		/* HTH disabled  STO 0.4 sec. */
 
 	/*
-	**	Reinitialize usrsync.
-	**	Have to renegotiate synch mode.
-	*/
-
-	usrsync = driver_setup.default_sync;
-	if (usrsync != 255) {
-		if (4 * usrsync <= 11 * 50) {
-			if (usrsync < np->ns_sync) {
-				usrsync = np->ns_sync;
-			}
-		}
-		else
-			usrsync = 255;
-	};
-
-	/*
-	**	Reinitialize usrwide.
-	**	Have to renegotiate wide mode.
-	*/
-
-	usrwide = driver_setup.max_wide;
-	if (usrwide > np->maxwide) usrwide=np->maxwide;
-
-	/*
 	**	Disable disconnects.
 	*/
 
@@ -5374,7 +5846,7 @@
 	**    Enable GPIO0 pin for writing if LED support.
 	*/
 
-	if (np->features & _F_LED0) {
+	if (np->features & FE_LED0) {
 		OUTOFFB (nc_gpcntl, 0x01);
 	}
 
@@ -5397,13 +5869,15 @@
 	/*
 	**	For 895/6 enable SBMC interrupt and save current SCSI bus mode.
 	*/
-	if (np->features & _F_ULTRA2) {
+	if (np->features & FE_ULTRA2) {
 		OUTONW (nc_sien, SBMC);
 		np->scsi_mode = INB (nc_stest4) & SMODE;
 	}
 
 	/*
 	**	Fill in target structure.
+	**	Reinitialize usrsync.
+	**	Reinitialize usrwide.
 	**	Prepare sync negotiation according to actual SCSI bus mode.
 	*/
 
@@ -5413,8 +5887,18 @@
 		tp->sval    = 0;
 		tp->wval    = np->rv_scntl3;
 
-		tp->usrsync = usrsync;
-		tp->usrwide = usrwide;
+		if (tp->usrsync != 255) {
+			if (tp->usrsync <= np->maxsync) {
+				if (tp->usrsync < np->minsync) {
+					tp->usrsync = np->minsync;
+				}
+			}
+			else
+				tp->usrsync = 255;
+		};
+
+		if (tp->usrwide > np->maxwide)
+			tp->usrwide = np->maxwide;
 
 		ncr_negotiate (np, tp);
 	}
@@ -5462,14 +5946,14 @@
 	**	our limit ..
 	*/
 
-	if (minsync < np->ns_sync)
-		minsync = np->ns_sync;
+	if (minsync < np->minsync)
+		minsync = np->minsync;
 
 	/*
 	**	divider limit
 	*/
 
-	if (minsync > (11*50)/4)
+	if (minsync > np->maxsync)
 		minsync = 255;
 
 	tp->minsync = minsync;
@@ -5489,106 +5973,123 @@
 
 /*==========================================================
 **
-**	Get clock factor and sync divisor.
+**	Get clock factor and sync divisor for a given 
+**	synchronous factor period.
+**	Returns the clock factor (in sxfer) and scntl3 
+**	synchronous divisor field.
 **
 **==========================================================
 */
 
-#define SCSI_NCR_USE_ALL_DIVISORS
-
-/*
-**	NCR chip clock divisor table.
-**	Multiplied by 2x2000000 in order to avoid useless operations in 
-**	the code that gets clock factor and sync divisor from sync factor.
-*/
-#define _2M 2000000
-static u_long ncr_div2_2M[] = {2*_2M, 3*_2M, 4*_2M, 6*_2M, 8*_2M, 12*_2M, 16*_2M};
-
-/*
-**	Get clock factor and sync divisor for a given sync factor period.
-**	Returns the clock factor, scntl3 and resulting period.
-*/
-static int ncr_getsync(ncb_p np, u_char fac, u_char *fakp, u_char *scntl3p)
+static void ncr_getsync(ncb_p np, u_char sfac, u_char *fakp, u_char *scntl3p)
 {
-	u_long	clk	= np->clock_khz;	/* Clock in kHz		*/
-	int	idiv	= np->clock_divn;	/* # divisors supported	*/
-	u_long	fak, per, per_clk;
+	u_long	clk = np->clock_khz;	/* SCSI clock frequency in kHz	*/
+	int	div = np->clock_divn;	/* Number of divisors supported	*/
+	u_long	fak;			/* Sync factor in sxfer		*/
+	u_long	per;			/* Period in tenths of ns	*/
+	u_long	kpc;			/* (per * clk)			*/
 
 	/*
-	**	Compute the synchronous period in nano-seconds
+	**	Compute the synchronous period in tenths of nano-seconds
 	*/
-	if	(fac <= 10)	per = 25;
-	else if	(fac == 11)	per = 30;
-	else if	(fac == 12)	per = 50;
-	else			per = 4 * fac;
+	if	(sfac <= 10)	per = 250;
+	else if	(sfac == 11)	per = 303;
+	else if	(sfac == 12)	per = 500;
+	else			per = 40 * sfac;
 
 	/*
-	**	Find the greatest divisor that allows an input speed 
-	**	faster than the period.
+	**	Look for the greatest clock divisor that allows an 
+	**	input speed faster than the period.
 	*/
-	per_clk = per * clk;
-	while (--idiv >= 0) {
-#ifndef SCSI_NCR_USE_ALL_DIVISORS
-		if (idiv & 1) continue;
-#endif
-		if (ncr_div2_2M[idiv] <= per_clk) break;
-	}
-	if (idiv < 0) idiv = 0;		/* Should never happen */
+	kpc = per * clk;
+	while (--div >= 0)
+		if (kpc >= (div_10M[div] << 2)) break;
 
 	/*
 	**	Calculate the lowest clock factor that allows an output 
 	**	speed not faster than the period.
 	*/
-	fak = (4 * per_clk - 1) / ncr_div2_2M[idiv] + 1;
-	per = (fak * ncr_div2_2M[idiv]) / (4 * clk);
+	fak = (kpc - 1) / div_10M[div] + 1;
+
+#if 0	/* This optimization does not seem very usefull */
+
+	per = (fak * div_10M[div]) / clk;
 
-#ifdef SCSI_NCR_USE_ALL_DIVISORS
 	/*
-	**	Try the next divisor and choose the one that give 
-	**	the fastest output speed.
+	**	Why not to try the immediate lower divisor and to choose 
+	**	the one that allows the fastest output speed ?
+	**	We dont want input speed too much greater than output speed.
 	*/
-	if (idiv >= 1 && fak < 8) {
+	if (div >= 1 && fak < 8) {
 		u_long fak2, per2;
-		fak2 = (4 * per_clk - 1) / ncr_div2_2M[idiv-1] + 1;
-		per2 = (fak2 * ncr_div2_2M[idiv-1]) / (4 * clk);
+		fak2 = (kpc - 1) / div_10M[div-1] + 1;
+		per2 = (fak2 * div_10M[div-1]) / clk;
 		if (per2 < per && fak2 <= 8) {
 			fak = fak2;
 			per = per2;
-			--idiv;
+			--div;
 		}
 	}
 #endif
-	if (fak < 4) fak = 4;		/* Should never happen */
+
+	if (fak < 4) fak = 4;	/* Should never happen, too bad ... */
 
 	/*
 	**	Compute and return sync parameters for the ncr
 	*/
 	*fakp		= fak - 4;
-	*scntl3p	= ((idiv+1) << 4) + (fac < 25 ? ULTRA : 0);
-
-#ifdef DEBUG_NCR53C8XX
-printf("fac=%d idiv=%d per=%d fak=%x ", fac, idiv, per, *fakp);
-#endif
-
-	return per;
+	*scntl3p	= ((div+1) << 4) + (sfac < 25 ? 0x80 : 0);
 }
 
+
 /*==========================================================
 **
-**	Switch sync mode for current job and it's target
+**	Set actual values, sync status and patch all ccbs of 
+**	a target according to new sync/wide agreement.
 **
 **==========================================================
 */
 
-static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer)
+static void ncr_set_sync_wide_status (ncb_p np, u_char target)
 {
-	Scsi_Cmnd *cmd;
-	tcb_p tp;
-	u_char target = INB (nc_ctest0) & 0x0f;
-	u_char idiv;
-
-	assert (cp);
-	if (!cp) return;
+	ccb_p cp;
+	tcb_p tp = &np->target[target];
+
+	/*
+	**	set actual value and sync_status
+	*/
+	OUTB (nc_sxfer, tp->sval);
+	np->sync_st = tp->sval;
+	OUTB (nc_scntl3, tp->wval);
+	np->wide_st = tp->wval;
+
+	/*
+	**	patch ALL ccbs of this target.
+	*/
+	for (cp = np->ccb; cp; cp = cp->link_ccb) {
+		if (!cp->cmd) continue;
+		if (cp->cmd->target != target) continue;
+		cp->sync_status = tp->sval;
+		cp->wide_status = tp->wval;
+	};
+}
+
+/*==========================================================
+**
+**	Switch sync mode for current job and it's target
+**
+**==========================================================
+*/
+
+static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer)
+{
+	Scsi_Cmnd *cmd;
+	tcb_p tp;
+	u_char target = INB (nc_ctest0) & 0x0f;
+	u_char idiv;
+
+	assert (cp);
+	if (!cp) return;
 
 	cmd = cp->cmd;
 	assert (cmd);
@@ -5603,10 +6104,12 @@
 
 	/*
 	**	Deduce the value of controller sync period from scntl3.
+	**	period is in tenths of nano-seconds.
 	*/
+
 	idiv = ((scntl3 >> 4) & 0x7);
 	if ((sxfer & 0x1f) && idiv)
-		tp->period = (((sxfer>>5)+4)*ncr_div2_2M[idiv-1])/(4*np->clock_khz);
+		tp->period = (((sxfer>>5)+4)*div_10M[idiv-1])/np->clock_khz;
 	else
 		tp->period = 0xffff;
 
@@ -5622,66 +6125,53 @@
 	*/
 	PRINT_ADDR(cmd);
 	if (sxfer & 0x01f) {
-		unsigned f10 = 10000 << (tp->widedone ? tp->widedone -1 : 0);
+		unsigned f10 = 100000 << (tp->widedone ? tp->widedone -1 : 0);
 		unsigned mb10 = (f10 + tp->period/2) / tp->period;
-		char *msg;
+		char *scsi;
 
 		/*
 		**  Disable extended Sreq/Sack filtering
 		*/
-		if (tp->period <= 200) OUTOFFB (nc_stest2, EXT);
+		if (tp->period <= 2000) OUTOFFB (nc_stest2, EXT);
 
 		/*
 		**	Bells and whistles   ;-)
 		*/
-		msg = "";
-		if (tp->widedone > 1) {
-			if	(tp->period < 50)  msg = "ULTRA-2 WIDE SCSI ";
-			else if	(tp->period < 100) msg = "ULTRA WIDE SCSI ";
-			else if	(tp->period < 200) msg = "FAST WIDE SCSI-2 ";
-		}
-		else {
-			if	(tp->period < 50)  msg = "ULTRA-2 SCSI ";
-			else if	(tp->period < 100) msg = "ULTRA SCSI ";
-			else if	(tp->period < 200) msg = "FAST SCSI-2 ";
-		}
-
-		printf ("%s%d.%d MB/s (%d ns, offset %d)\n", msg,
-			mb10 / 10, mb10 % 10, tp->period, sxfer & 0x1f);
-	} else  printf ("asynchronous.\n");
+		if	(tp->period < 500)	scsi = "FAST-40";
+		else if	(tp->period < 1000)	scsi = "FAST-20";
+		else if	(tp->period < 2000)	scsi = "FAST-10";
+		else				scsi = "SLOW";
+
+		printf ("%s %sSCSI %d.%d MB/s (%d ns, offset %d)\n", scsi,
+			tp->widedone > 1 ? "WIDE " : "",
+			mb10 / 10, mb10 % 10, tp->period / 10, sxfer & 0x1f);
+	} else
+		printf ("%sasynchronous.\n", tp->widedone > 1 ? "wide " : "");
 
 	/*
 	**	set actual value and sync_status
-	*/
-	OUTB (nc_sxfer, sxfer);
-	np->sync_st = sxfer;
-	OUTB (nc_scntl3, scntl3);
-	np->wide_st = scntl3;
-
-	/*
 	**	patch ALL ccbs of this target.
 	*/
-	for (cp = np->ccb; cp; cp = cp->link_ccb) {
-		if (!cp->cmd) continue;
-		if (cp->cmd->target != target) continue;
-		cp->sync_status = sxfer;
-		cp->wide_status = scntl3;
-	};
+	ncr_set_sync_wide_status(np, target);
 }
 
 /*==========================================================
 **
 **	Switch wide mode for current job and it's target
+**	SCSI specs say: a SCSI device that accepts a WDTR 
+**	message shall reset the synchronous agreement to 
+**	asynchronous mode.
 **
 **==========================================================
 */
 
-static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide)
+static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack)
 {
 	Scsi_Cmnd *cmd;
 	u_short target = INB (nc_ctest0) & 0x0f;
 	tcb_p tp;
 	u_char	scntl3;
+	u_char	sxfer;
 
 	assert (cp);
 	if (!cp) return;
@@ -5694,7 +6184,14 @@
 	tp = &np->target[target];
 	tp->widedone  =  wide+1;
 	scntl3 = (tp->wval & (~EWS)) | (wide ? EWS : 0);
-	if (tp->wval == scntl3) return;
+
+	sxfer = ack ? 0 : tp->sval;
+
+	/*
+	**	 Stop there if sync/wide parameters are unchanged
+	*/
+	if (tp->sval == sxfer && tp->wval == scntl3) return;
+	tp->sval = sxfer;
 	tp->wval = scntl3;
 
 	/*
@@ -5710,18 +6207,9 @@
 
 	/*
 	**	set actual value and sync_status
-	*/
-	OUTB (nc_scntl3, scntl3);
-	np->wide_st = scntl3;
-
-	/*
 	**	patch ALL ccbs of this target.
 	*/
-	for (cp = np->ccb; cp; cp = cp->link_ccb) {
-		if (!cp->cmd) continue;
-		if (cp->cmd->target != target) continue;
-		cp->wide_status = scntl3;
-	};
+	ncr_set_sync_wide_status(np, target);
 }
 
 /*==========================================================
@@ -5731,11 +6219,13 @@
 **==========================================================
 */
 
-static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long usrtags)
+static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long numtags)
 {
 	int l;
-	tp->usrtags = usrtags;
-	tp->maxtags = usrtags;
+	if (numtags > tp->usrtags)
+		numtags = tp->usrtags;
+	tp->numtags = numtags;
+	tp->maxtags = numtags;
 
 	for (l=0; l<MAX_LUN; l++) {
 		lcb_p lp;
@@ -5748,11 +6238,11 @@
 		wastags = lp->usetags;
 		ncr_settags (tp, lp);
 
-		if (usrtags > 1 && lp->reqccbs > 1) {
+		if (numtags > 1 && lp->reqccbs > 1) {
 			PRINT_LUN(np, tp - np->target, l);
-			printf("using tagged command queueing, up to %ld cmds/lun\n", usrtags);
+			printf("using tagged command queueing, up to %ld cmds/lun\n", numtags);
 		}
-		else if (usrtags <= 1 && wastags) {
+		else if (numtags <= 1 && wastags) {
 			PRINT_LUN(np, tp - np->target, l);
 			printf("disabling tagged command queueing\n");
 		}
@@ -5773,8 +6263,8 @@
 	*/
 	if ((  tp->inqdata[2] & 0x7) >= 2 &&
 	    (  tp->inqdata[7] & INQ7_QUEUE) && ((tp->inqdata[0] & 0x1f)==0x00)
-		&& tp->usrtags > 1) {
-		reqtags = tp->usrtags;
+		&& tp->numtags > 1) {
+		reqtags = tp->numtags;
 		if (lp->actlink <= 1)
 			lp->usetags=reqtags;
 	} else {
@@ -5830,6 +6320,7 @@
 			np->user.data = SCSI_NCR_MAX_TAGS;
 		for (t=0; t<MAX_TARGET; t++) {
 			if (!((np->user.target>>t)&1)) continue;
+			np->target[t].usrtags = np->user.data;
 			ncr_setmaxtags (np, &np->target[t], np->user.data);
 		};
 		break;
@@ -6467,11 +6958,13 @@
 **
 **==========================================================
 **
-**	I'm not quite sure of what is to be done in such a
-**	situation.
-**	For now,
-**	Reset the bus if some devices use too fast sync transfers.
-**	Otherwise, just try to renegotiate sync with targets. 
+**	spi2-r12 11.2.3 says a transceiver mode change must 
+**	generate a reset event and a device that detects a reset 
+**	event shall initiate a hard reset. It says also that a
+**	device that detects a mode change shall set data transfer 
+**	mode to eight bit asynchronous, etc...
+**	So, just resetting should be enough.
+**	 
 **
 **----------------------------------------------------------
 */
@@ -6479,32 +6972,14 @@
 static int ncr_int_sbmc (ncb_p np)
 {
 	u_char scsi_mode = INB (nc_stest4) & SMODE;
-	int i;
-	int oversync;
-
-	printf("%s: SCSI bus mode change from %x to %x\n", ncr_name(np),
-		np->scsi_mode, scsi_mode);
 
-	if (scsi_mode == np->scsi_mode)
-		return 0;
+	printf("%s: SCSI bus mode change from %x to %x, resetting ...\n",
+		ncr_name(np), np->scsi_mode, scsi_mode);
 
 	np->scsi_mode = scsi_mode;
-	oversync = 0;
-	for (i = 0; i < MAX_TARGET; i++) {
-		tcb_p tp = &np->target[i];
-
-		if (np->ns_sync < 12 && tp->maxoffs && tp->usrsync < 12) {
-			if (scsi_mode != SMODE_SE)
-				ncr_negotiate(np, tp);
-			else
-				++oversync;
-		}
-	}
-
-	if (oversync)
-		ncr_start_reset(np, 2);
+	ncr_start_reset(np, 2);
 
-	return oversync;
+	return 1;
 }
 
 /*==========================================================
@@ -6562,10 +7037,10 @@
 
 	/*
 	**	Take into account dma fifo and various buffers and latches,
-	**	only if the interrupted phase was DATA OUT.
+	**	only if the interrupted phase is an OUTPUT phase.
 	*/
 
-	if ((cmd & 7) == 0) {
+	if ((cmd & 1) == 0) {
 		u_char	ctest5, ss0, ss2;
 		u_short	delta;
 
@@ -7011,7 +7486,7 @@
 			break;
 
 		case NS_WIDE:
-			ncr_setwide (np, cp, 0);
+			ncr_setwide (np, cp, 0, 0);
 			break;
 
 		};
@@ -7054,8 +7529,8 @@
 		**	check values against driver limits.
 		*/
 
-		if (per < np->ns_sync)
-			{chg = 1; per = np->ns_sync;}
+		if (per < np->minsync)
+			{chg = 1; per = np->minsync;}
 		if (per < tp->minsync)
 			{chg = 1; per = tp->minsync;}
 		if (ofs > tp->maxoffs)
@@ -7067,7 +7542,7 @@
 		fak	= 7;
 		scntl3	= 0;
 		if (ofs != 0) {
-			(void) ncr_getsync(np, per, &fak, &scntl3);
+			ncr_getsync(np, per, &fak, &scntl3);
 			if (fak > 7) {
 				chg = 1;
 				ofs = 0;
@@ -7110,7 +7585,7 @@
 				return;
 
 			case NS_WIDE:
-				ncr_setwide (np, cp, 0);
+				ncr_setwide (np, cp, 0, 0);
 				break;
 			};
 		};
@@ -7209,13 +7684,13 @@
 					/*
 					**	Answer wasn't acceptable.
 					*/
-					ncr_setwide (np, cp, 0);
+					ncr_setwide (np, cp, 0, 1);
 					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad));
 				} else {
 					/*
 					**	Answer is ok.
 					*/
-					ncr_setwide (np, cp, wide);
+					ncr_setwide (np, cp, wide, 1);
 					OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack));
 				};
 				return;
@@ -7231,7 +7706,7 @@
 		**      prepare an answer message
 		*/
 
-		ncr_setwide (np, cp, wide);
+		ncr_setwide (np, cp, wide, 1);
 
 		np->msgout[0] = M_EXTENDED;
 		np->msgout[1] = 2;
@@ -7543,10 +8018,12 @@
 		tp->jump_tcb.l_cmd   = (SCR_JUMP^IFFALSE (DATA (0x80 + target)));
 		tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr;
 
-		tp->getscr[0] = (np->features & _F_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
+		tp->getscr[0] =
+			(np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
 		tp->getscr[1] = vtophys (&tp->sval);
 		tp->getscr[2] = np->paddr + offsetof (struct ncr_reg, nc_sxfer);
-		tp->getscr[3] = (np->features & _F_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
+		tp->getscr[3] =
+			(np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
 		tp->getscr[4] = vtophys (&tp->wval);
 		tp->getscr[5] = np->paddr + offsetof (struct ncr_reg, nc_scntl3);
 
@@ -8206,15 +8683,6 @@
 	*/
 	f1		*= np->multiplier;
 	np->clock_khz	= f1;
-	np->ns_sync	= 25;
-
-	if	(f1 >= 160000) {
-		if	(np->features & _F_ULTRA2)	np->ns_sync = 10;
-		else if	(np->features & _F_ULTRA)	np->ns_sync = 12;
-	}
-	else if	(f1 >= 80000) {
-		if (np->features & _F_ULTRA)		np->ns_sync = 12;
-	}
 }
 
 /*===================== LINUX ENTRY POINTS SECTION ==========================*/
@@ -8244,13 +8712,14 @@
 {
 #ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
 	char *cur = str;
-	char *pv;
+	char *pc, *pv;
 	int val;
 	int base;
 	int c;
 
-	while (cur != NULL && (pv = strchr(cur, ':')) != NULL) {
+	while (cur != NULL && (pc = strchr(cur, ':')) != NULL) {
 		val = 0;
+		pv = pc;
 		c = *++pv;
 		if	(c == 'n')
 			val = 0;
@@ -8314,9 +8783,15 @@
 			driver_setup.irqm	= val;
 		else if	(!strncmp(cur, "pcifix:", 7))
 			driver_setup.pci_fix_up	= val;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+		else if	(!strncmp(cur, "nvram:", 6))
+			driver_setup.use_nvram	= val;
+#endif
 
 		else if	(!strncmp(cur, "safe:", 5) && val)
 			memcpy(&driver_setup, &driver_safe_setup, sizeof(driver_setup));
+		else
+			printf("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", pc-cur+1, cur);
 
 		if ((cur = strchr(cur, ',')) != NULL)
 			++cur;
@@ -8324,8 +8799,8 @@
 #endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
 }
 
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit,
-	     uchar bus, uchar device_fn);
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+	     uchar bus, uchar device_fn, ncr_device *device);
 
 /*
 **   Linux entry point for NCR53C8XX devices detection routine.
@@ -8336,6 +8811,9 @@
 **   Read the PCI configuration and try to attach each
 **   detected NCR board.
 **
+**   If NVRAM is present, try to attach boards according to 
+**   the used defined boot order.
+**
 **   Returns the number of boards successfully attached.
 */
 
@@ -8372,6 +8850,91 @@
 static ncr_chip	ncr_chip_table[] __initdata	= SCSI_NCR_CHIP_TABLE;
 static ushort	ncr_chip_ids[]   __initdata	= SCSI_NCR_CHIP_IDS;
 
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+__initfunc(
+static int
+ncr_attach_using_nvram(Scsi_Host_Template *tpnt, int nvram_index, int count, ncr_device device[])
+)
+{
+	int i, j;
+	int attach_count = 0;
+	ncr_nvram  *nvram;
+	ncr_device *devp;
+
+	if (!nvram_index)
+		return 0;
+
+	/* find first Symbios NVRAM if there is one as we need to check it for host boot order */
+	for (i = 0, nvram_index = -1; i < count; i++) {
+		devp  = &device[i];
+		nvram = devp->nvram;
+		if (!nvram)
+			continue;
+		if (nvram->type == SCSI_NCR_SYMBIOS_NVRAM) {
+			if (nvram_index == -1)
+				nvram_index = i;
+#ifdef SCSI_NCR_DEBUG_NVRAM
+			printf("ncr53c8xx: NVRAM: Symbios format Boot Block, 53c%s, PCI bus %d, device %d, function %d\n",
+				devp->chip.name, devp->slot.bus, 
+				(int) (devp->slot.device_fn & 0xf8) >> 3, 
+				(int) devp->slot.device_fn & 7);
+			for (j = 0 ; j < 4 ; j++) {
+				Symbios_host *h = &nvram->data.Symbios.host[j];
+			printf("ncr53c8xx: BOOT[%d] device_id=%04x vendor_id=%04x device_fn=%02x io_port=%04x %s\n",
+				j,		h->device_id,	h->vendor_id,
+				h->device_fn,	h->io_port,
+				(h->flags & SYMBIOS_INIT_SCAN_AT_BOOT) ? "SCAN AT BOOT" : "");
+			}
+		}
+		else if (nvram->type == SCSI_NCR_TEKRAM_NVRAM) {
+			/* display Tekram nvram data */
+			printf("ncr53c8xx: NVRAM: Tekram format data, 53c%s, PCI bus %d, device %d, function %d\n",
+				devp->chip.name, devp->slot.bus, 
+				(int) (devp->slot.device_fn & 0xf8) >> 3, 
+				(int) devp->slot.device_fn & 7);
+#endif
+		}
+	}
+
+	if (nvram_index >= 0 && nvram_index < count)
+		nvram = device[nvram_index].nvram;
+	else
+		nvram = 0;
+
+	if (!nvram)
+		goto out;
+
+	/* 
+	** check devices in the boot record against devices detected. 
+	** attach devices if we find a match. boot table records that 
+	** do not match any detected devices will be ignored. 
+	** devices that do not match any boot table will not be attached
+	** here but will attempt to be attached during the device table 
+	** rescan.
+	*/
+     	for (i = 0; i < 4; i++) {
+		Symbios_host *h = &nvram->data.Symbios.host[i];
+		for (j = 0 ; j < count ; j++) {
+			devp = &device[j];
+			if (h->device_fn == devp->slot.device_fn &&
+#if 0	/* bus number location in nvram ? */
+			    h->bus	 == devp->slot.bus	 &&
+#endif
+			    h->device_id == devp->chip.device_id)
+				break;
+		}
+		if (j < count && !devp->attached &&
+		    !ncr_attach (tpnt, attach_count, devp)) {
+			attach_count++;
+			devp->attached = 1;
+		}
+	}
+
+out:
+	return attach_count;
+}
+#endif /* SCSI_NCR_NVRAM_SUPPORT */
+
 __initfunc(
 int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
 )
@@ -8381,8 +8944,18 @@
 	int count = 0;
 	uchar bus, device_fn;
 	short index;
+	int attach_count = 0;
+	ncr_device device[8];
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	ncr_nvram  nvram[4];
+	int k, nvrams;
+#endif
+	int hosts;
 
-	if (bootverbose >= 2)
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	int nvram_index = 0;
+#endif
+	if (initverbose >= 2)
 		ncr_print_driver_setup();
 
 #ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
@@ -8396,32 +8969,99 @@
 # endif
 #endif
 
+	/* 
+	** Detect all 53c8xx hosts and then attach them.
+	**
+	** If we are using NVRAM, once all hosts are detected, we need to check
+	** any NVRAM for boot order in case detect and boot order differ and
+	** attach them using the order in the NVRAM.
+	**
+	** If no NVRAM is found or data appears invalid attach boards in the 
+	** the order they are detected.
+	*/
+
 	if (!pcibios_present())
 		return 0;
 
-	chips = sizeof(ncr_chip_ids) / sizeof(ncr_chip_ids[0]);
+	chips	= sizeof(ncr_chip_ids)	/ sizeof(ncr_chip_ids[0]);
+	hosts	= sizeof(device)	/ sizeof(device[0]);
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	k = 0;
+	if (driver_setup.use_nvram & 0x1)
+		nvrams	= sizeof(nvram)	/ sizeof(nvram[0]);
+	else
+		nvrams	= 0;
+#endif
+
 	for (j = 0; j < chips ; ++j) {
 		i = driver_setup.reverse_probe ? chips-1 - j : j;
 		for (index = 0; ; index++) {
-			if (pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
-						index, &bus, &device_fn))
+			char *msg = "";
+			if ((pcibios_find_device(PCI_VENDOR_ID_NCR, ncr_chip_ids[i],
+						index, &bus, &device_fn)) ||
+			    (count == hosts))
 				break;
-			if (!ncr53c8xx_pci_init(tpnt, count, bus, device_fn))
-				++count;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+			device[count].nvram = k < nvrams ? &nvram[k] : 0;
+#else
+			device[count].nvram = 0;
+#endif
+			if (ncr53c8xx_pci_init(tpnt, bus, device_fn, &device[count])) {
+				device[count].nvram = 0;
+				continue;
+			}
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+			if (device[count].nvram) {
+				++k;
+				nvram_index |= device[count].nvram->type;
+				switch (device[count].nvram->type) {
+				case SCSI_NCR_TEKRAM_NVRAM:
+					msg = "with Tekram NVRAM";
+					break;
+				case SCSI_NCR_SYMBIOS_NVRAM:
+					msg = "with Symbios NVRAM";
+					break;
+				default:
+					msg = "";
+					device[count].nvram = 0;
+					--k;
+				}
+			}
+#endif
+			printf(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
+				device[count].chip.name, msg);
+
+			device[count].attached = 0;
+			++count;
+		}
+	}
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	attach_count = ncr_attach_using_nvram(tpnt, nvram_index, count, device);
+#endif
+	/* 
+	** rescan device list to make sure all boards attached.
+	** devices without boot records will not be attached yet
+	** so try to attach them here.
+	*/
+	for (i= 0; i < count; i++) {
+		if ((!device[i].attached) && (!ncr_attach (tpnt, attach_count, &device[i]))) {
+ 			attach_count++;
+			device[i].attached = 1;
 		}
 	}
-	return count;
-}
 
+	return attach_count;
+}
 
 /*
-**   Read the PCI configuration of a found NCR board and
-**   try yo attach it.
+**   Read and check the PCI configuration for any detected NCR 
+**   boards and save data for attaching after all boards have 
+**   been detected.
 */
 
 __initfunc(
-static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit,
-			      uchar bus, uchar device_fn)
+static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt,
+			      uchar bus, uchar device_fn, ncr_device *device)
 )
 {
 	ushort vendor_id, device_id, command;
@@ -8433,23 +9073,27 @@
 	ulong base, io_port; 
 #endif
 	int i, error;
-	ncr_chip ncrchip, *chip;
 
-	printk("ncr53c8xx: at PCI bus %d, device %d, function %d\n",
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	ncr_nvram *nvram = device->nvram;
+#endif
+	ncr_chip *chip;
+
+	printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
 		bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
 	/*
 	 * Read info from the PCI config space
 	 */
 	if (
-(error=pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id))	||
-(error=pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id))	||
-(error=pcibios_read_config_word( bus, device_fn, PCI_COMMAND, &command))		||
-(error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0,&io_port))	|| 
-(error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, &base))	||
-(error=pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION,&revision))	||
-(error=pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq))	||
-(error=pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, &cache_line_size)) ||
-(error=pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER, &latency_timer))
+		(error=pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id))	||
+		(error=pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id))	||
+		(error=pcibios_read_config_word( bus, device_fn, PCI_COMMAND, &command))	||
+		(error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0,&io_port))	|| 
+		(error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, &base))	||
+		(error=pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION,&revision))	||
+		(error=pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq))	||
+		(error=pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, &cache_line_size)) ||
+		(error=pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER, &latency_timer))
 	)
 		goto err_pcibios;
 
@@ -8462,7 +9106,7 @@
 			continue;
 		if (revision > ncr_chip_table[i].revision_id)
 			continue;
-		chip = &ncrchip;
+		chip = &device->chip;
 		memcpy(chip, &ncr_chip_table[i], sizeof(*chip));
 		chip->revision_id = revision;
 		break;
@@ -8516,23 +9160,28 @@
 	}
 
 	/*
-	 * Remove not wished features.
+	 * Fix some features according to driver setup.
 	 */
 	if (!driver_setup.special_features)
-		chip->features &= ~_F_SPECIAL_SET;
-	if (driver_setup.ultra_scsi < 2 && chip->features & _F_ULTRA2)
-		chip->features &= ~(_F_ULTRA2 | _F_ULTRA);
+		chip->features &= ~FE_SPECIAL_SET;
+	if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
+		chip->features |=  FE_ULTRA;
+		chip->features &= ~FE_ULTRA2;
+	}
 	if (driver_setup.ultra_scsi < 1)
-		chip->features &= ~_F_ULTRA;
+		chip->features &= ~FE_ULTRA;
 	if (!driver_setup.max_wide)
-		chip->features &= ~_F_WIDE;
+		chip->features &= ~FE_WIDE;
+
+
+#ifdef	SCSI_NCR_PCI_FIX_UP_SUPPORT
 
 	/*
 	 * Try to fix up PCI config according to wished features.
 	 */
-#ifdef __i386
+#if defined(__i386) && !defined(MODULE)
 	if ((driver_setup.pci_fix_up & 1) &&
-	    (chip->features & _F_CLSE) && cache_line_size == 0) {
+	    (chip->features & FE_CLSE) && cache_line_size == 0) {
 		extern char x86;
 		switch(x86) {
 		case 4:	cache_line_size = 4; break;
@@ -8542,38 +9191,137 @@
 			error = pcibios_write_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, cache_line_size);
 		if (error)
 			goto err_pcibios;
-		if (bootverbose)
+		if (initverbose)
 			printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size);
 	}
 
 	if ((driver_setup.pci_fix_up & 2) && cache_line_size &&
-	    (chip->features & _F_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+	    (chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
 		command |= PCI_COMMAND_INVALIDATE;
 		error=pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
 		if (error)
 			goto err_pcibios;
-		if (bootverbose)
+		if (initverbose)
 			printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n");
 	}
 #endif
 	/*
-	 * Remove features that will not work
+	 * Fix up for old chips that support READ LINE but not CACHE LINE SIZE.
+	 * - If CACHE LINE SIZE is unknown, set burst max to 32 bytes = 8 dwords
+	 *   and donnot enable READ LINE.
+	 * - Otherwise set it to the CACHE LINE SIZE (power of 2 assumed). 
 	 */
-	if ((chip->features & _F_CLSE) && cache_line_size == 0) {
-		chip->features &= ~_F_CACHE_SET;
+
+	if (!(chip->features & FE_CLSE)) {
+		int burst_max = chip->burst_max;
+		if (cache_line_size == 0) {
+			chip->features	&= ~FE_ERL;
+			if (burst_max > 3)
+				burst_max = 3;
+		}
+		else {
+			while (cache_line_size < (1 << burst_max))
+				--burst_max;
+		}
+		chip->burst_max = burst_max;
+	}
+
+	/*
+	 * Tune PCI LATENCY TIMER according to burst max length transfer.
+	 * (latency timer >= burst length + 6, we add 10 to be quite sure)
+	 * If current value is zero, the device has probably been configured 
+	 * for no bursting due to some broken hardware.
+	 */
+
+	if (latency_timer == 0 && chip->burst_max)
+		printk("ncr53c8xx: PCI_LATENCY_TIMER=0, bursting should'nt be allowed.\n");
+
+	if ((driver_setup.pci_fix_up & 4) && chip->burst_max) {
+		uchar lt = (1 << chip->burst_max) + 6 + 10;
+		if (latency_timer < lt) {
+			latency_timer = lt;
+			if (initverbose)
+				printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fix-up).\n", latency_timer);
+			error = pcibios_write_config_byte(bus, device_fn,
+					PCI_LATENCY_TIMER, latency_timer);
+			if (error)
+				goto err_pcibios;
+		}
+	}
+
+	/*
+	 * Fix up for recent chips that support CACHE LINE SIZE.
+	 * If PCI config space is not OK, remove features that shall not be 
+	 * used by the chip. No need to trigger possible chip bugs.
+	 */
+
+	if ((chip->features & FE_CLSE) && cache_line_size == 0) {
+		chip->features &= ~FE_CACHE_SET;
 		printk("ncr53c8xx: PCI_CACHE_LINE_SIZE not set, features based on CACHE LINE SIZE not used.\n");
 	}
 
-	if ((chip->features & _F_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
-		chip->features &= ~_F_WRIE;
+	if ((chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
+		chip->features &= ~FE_WRIE;
 		printk("ncr53c8xx: PCI_COMMAND_INVALIDATE not set, WRITE AND INVALIDATE not used\n");
 	}
 
+#endif	/* SCSI_NCR_PCI_FIX_UP_SUPPORT */
+
+ 	/* initialise ncr_device structure with items required by ncr_attach */
+	device->slot.bus	= bus;
+	device->slot.device_fn	= device_fn;
+	device->slot.base	= base;
+	device->slot.io_port	= io_port;
+	device->slot.irq	= irq;
+	device->attached	= 0;
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+	if (!nvram)
+		goto out;
+
 	/*
-	 * Try to attach the controller
-	 */
-	return ncr_attach (tpnt, unit, chip, base, io_port, (int) irq, 
-			bus, (uchar) device_fn);
+	** Get access to chip IO registers
+	*/
+#ifdef NCR_IOMAPPED
+	request_region(io_port, 128, "ncr53c8xx");
+	device->slot.port = ioport;
+#else
+	device->slot.reg = (struct ncr_reg *) remap_pci_mem((ulong) base, 128);
+	if (!device->slot.reg)
+		goto out;
+#endif
+
+	/*
+	** Try to read SYMBIOS nvram.
+	** Data can be used to order booting of boards.
+	**
+	** Data is saved in ncr_device structure if NVRAM found. This
+	** is then used to find drive boot order for ncr_attach().
+	**
+	** NVRAM data is passed to Scsi_Host_Template later during ncr_attach()
+	** for any device set up.
+	**
+	** Try to read TEKRAM nvram if Symbios nvram not found.
+	*/
+
+	if	(!ncr_get_Symbios_nvram(&device->slot, &nvram->data.Symbios))
+		nvram->type = SCSI_NCR_SYMBIOS_NVRAM;
+	else if	(!ncr_get_Tekram_nvram(&device->slot, &nvram->data.Tekram))
+		nvram->type = SCSI_NCR_TEKRAM_NVRAM;
+	else
+		nvram->type = 0;
+out:
+	/*
+	** Release access to chip IO registers
+	*/
+#ifdef NCR_IOMAPPED
+	release_region(device->slot.port, 128);
+#else
+	unmap_pci_mem((vm_offset_t) device->slot.reg, (u_long) 128);
+#endif
+
+#endif	/* SCSI_NCR_NVRAM_SUPPORT */
+	return 0;     
+
 err_pcibios:
 	printk("ncr53c8xx: error %s reading configuration space\n",
 		pcibios_strerror(error));
@@ -9229,7 +9977,7 @@
 		copy_info(&info, "  Using memory mapped IO at virtual address 0x%lx\n",
 		                  (u_long) np->reg);
 #endif
-	copy_info(&info, "  Synchronous period factor %d, ", (int) np->ns_sync);
+	copy_info(&info, "  Synchronous period factor %d, ", (int) np->minsync);
 	copy_info(&info, "max commands per lun %d\n", SCSI_NCR_MAX_TAGS);
 
 	if (driver_setup.debug || driver_setup.verbose > 1) {
@@ -9311,6 +10059,460 @@
 **=========================================================================
 */
 #endif
+
+
+#ifdef SCSI_NCR_NVRAM_SUPPORT
+
+/* ---------------------------------------------------------------------
+**
+**	Try reading Symbios format nvram
+**
+** ---------------------------------------------------------------------
+**
+** GPOI0 - data in/data out
+** GPIO1 - clock
+**
+**	return 0 if NVRAM data OK, 1 if NVRAM data not OK
+** ---------------------------------------------------------------------
+*/
+
+#define SET_BIT 0
+#define CLR_BIT 1
+#define SET_CLK 2
+#define CLR_CLK 3
+
+static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl);
+static void nvram_start(ncr_slot *np, u_char *gpreg);
+static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl);
+static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl);
+static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl);
+static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl);
+static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg);
+static void nvram_stop(ncr_slot *np, u_char *gpreg);
+static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode);
+
+__initfunc(
+static int ncr_get_Symbios_nvram (ncr_slot *np, Symbios_nvram *nvram)
+)
+{
+	static u_char Symbios_trailer[6] = {0xfe, 0xfe, 0, 0, 0, 0};
+	u_char	gpcntl, gpreg;
+	u_char	old_gpcntl, old_gpreg;
+	u_short	csum;
+	u_char	ack_data;
+	int	retv = 1;
+
+	/* save current state of GPCNTL and GPREG */
+	old_gpreg	= INB (nc_gpreg);
+	old_gpcntl	= INB (nc_gpcntl);
+	gpcntl		= old_gpcntl & 0xfc;
+
+	/* set up GPREG & GPCNTL to set GPIO0 and GPIO1 in to known state */
+	OUTB (nc_gpreg,  old_gpreg);
+	OUTB (nc_gpcntl, gpcntl);
+
+	/* this is to set NVRAM into a known state with GPIO0/1 both low */
+	gpreg = old_gpreg;
+	nvram_setBit(np, 0, &gpreg, CLR_CLK);
+	nvram_setBit(np, 0, &gpreg, CLR_BIT);
+		
+	/* now set NVRAM inactive with GPIO0/1 both high */
+	nvram_stop(np, &gpreg);
+	
+	/* activate NVRAM */
+	nvram_start(np, &gpreg);
+
+	/* write device code and random address MSB */
+	nvram_write_byte(np, &ack_data,
+		0xa0 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+	if (ack_data & 0x01)
+		goto out;
+
+	/* write random address LSB */
+	nvram_write_byte(np, &ack_data,
+		(SYMBIOS_NVRAM_ADDRESS & 0x7f) << 1, &gpreg, &gpcntl);
+	if (ack_data & 0x01)
+		goto out;
+
+	/* regenerate START state to set up for reading */
+	nvram_start(np, &gpreg);
+	
+	/* rewrite device code and address MSB with read bit set (lsb = 0x01) */
+	nvram_write_byte(np, &ack_data,
+		0xa1 | ((SYMBIOS_NVRAM_ADDRESS >> 7) & 0x0e), &gpreg, &gpcntl);
+	if (ack_data & 0x01)
+		goto out;
+
+	/* now set up GPIO0 for inputting data */
+	gpcntl |= 0x01;
+	OUTB (nc_gpcntl, gpcntl);
+		
+	/* input all active data - only part of total NVRAM */
+	csum = nvram_read_data(np,
+			(u_char *) nvram, sizeof(*nvram), &gpreg, &gpcntl);
+
+	/* finally put NVRAM back in inactive mode */
+	gpcntl &= 0xfe;
+	OUTB (nc_gpcntl, gpcntl);
+	nvram_stop(np, &gpreg);
+	
+#ifdef SCSI_NCR_DEBUG_NVRAM
+printf("ncr53c8xx: NvRAM marker=%x trailer=%x %x %x %x %x %x byte_count=%d/%d checksum=%x/%x\n",
+	nvram->start_marker,
+	nvram->trailer[0], nvram->trailer[1], nvram->trailer[2],
+	nvram->trailer[3], nvram->trailer[4], nvram->trailer[5],
+	nvram->byte_count, sizeof(*nvram) - 12,
+	nvram->checksum, csum);
+#endif
+
+	/* check valid NVRAM signature, verify byte count and checksum */
+	if (nvram->start_marker == 0 &&
+	    !memcmp(nvram->trailer, Symbios_trailer, 6) &&
+	    nvram->byte_count == sizeof(*nvram) - 12 &&
+	    csum == nvram->checksum)
+		retv = 0;
+out:
+	/* return GPIO0/1 to original states after having accessed NVRAM */
+	OUTB (nc_gpcntl, old_gpcntl);
+	OUTB (nc_gpreg,  old_gpreg);
+
+	return retv;
+}
+
+/*
+ * Read Symbios NvRAM data and compute checksum.
+ */
+__initfunc(
+static u_short nvram_read_data(ncr_slot *np, u_char *data, int len, u_char *gpreg, u_char *gpcntl)
+)
+{
+	int	x;
+	u_short	csum;
+
+	for (x = 0; x < len; x++) 
+		nvram_read_byte(np, &data[x], (x == (len - 1)), gpreg, gpcntl);
+
+	for (x = 6, csum = 0; x < len - 6; x++)
+		csum += data[x];
+
+	return csum;
+}
+
+/*
+ * Send START condition to NVRAM to wake it up.
+ */
+__initfunc(
+static void nvram_start(ncr_slot *np, u_char *gpreg)
+)
+{
+	nvram_setBit(np, 1, gpreg, SET_BIT);
+	nvram_setBit(np, 0, gpreg, SET_CLK);
+	nvram_setBit(np, 0, gpreg, CLR_BIT);
+	nvram_setBit(np, 0, gpreg, CLR_CLK);
+}
+
+/*
+ * WRITE a byte to the NVRAM and then get an ACK to see it was accepted OK,
+ * GPIO0 must already be set as an output
+ */
+__initfunc(
+static void nvram_write_byte(ncr_slot *np, u_char *ack_data, u_char write_data, u_char *gpreg, u_char *gpcntl)
+)
+{
+	int x;
+	
+	for (x = 0; x < 8; x++)
+		nvram_doBit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
+		
+	nvram_readAck(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * READ a byte from the NVRAM and then send an ACK to say we have got it,
+ * GPIO0 must already be set as an input
+ */
+__initfunc(
+static void nvram_read_byte(ncr_slot *np, u_char *read_data, u_char ack_data, u_char *gpreg, u_char *gpcntl)
+)
+{
+	int x;
+	u_char read_bit;
+
+	*read_data = 0;
+	for (x = 0; x < 8; x++) {
+		nvram_doBit(np, &read_bit, 1, gpreg);
+		*read_data |= ((read_bit & 0x01) << (7 - x));
+	}
+
+	nvram_writeAck(np, ack_data, gpreg, gpcntl);
+}
+
+/*
+ * Output an ACK to the NVRAM after reading,
+ * change GPIO0 to output and when done back to an input
+ */
+__initfunc(
+static void nvram_writeAck(ncr_slot *np, u_char write_bit, u_char *gpreg, u_char *gpcntl)
+)
+{
+	OUTB (nc_gpcntl, *gpcntl & 0xfe);
+	nvram_doBit(np, 0, write_bit, gpreg);
+	OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Input an ACK from NVRAM after writing,
+ * change GPIO0 to input and when done back to an output
+ */
+__initfunc(
+static void nvram_readAck(ncr_slot *np, u_char *read_bit, u_char *gpreg, u_char *gpcntl)
+)
+{
+	OUTB (nc_gpcntl, *gpcntl | 0x01);
+	nvram_doBit(np, read_bit, 1, gpreg);
+	OUTB (nc_gpcntl, *gpcntl);
+}
+
+/*
+ * Read or write a bit to the NVRAM,
+ * read if GPIO0 input else write if GPIO0 output
+ */
+__initfunc(
+static void nvram_doBit(ncr_slot *np, u_char *read_bit, u_char write_bit, u_char *gpreg)
+)
+{
+	nvram_setBit(np, write_bit, gpreg, SET_BIT);
+	nvram_setBit(np, 0, gpreg, SET_CLK);
+	if (read_bit)
+		*read_bit = INB (nc_gpreg);
+	nvram_setBit(np, 0, gpreg, CLR_CLK);
+	nvram_setBit(np, 0, gpreg, CLR_BIT);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZzzzz!!
+ */
+__initfunc(
+static void nvram_stop(ncr_slot *np, u_char *gpreg)
+)
+{
+	nvram_setBit(np, 0, gpreg, SET_CLK);
+	nvram_setBit(np, 1, gpreg, SET_BIT);
+}
+
+/*
+ * Set/clear data/clock bit in GPIO0
+ */
+__initfunc(
+static void nvram_setBit(ncr_slot *np, u_char write_bit, u_char *gpreg, int bit_mode)
+)
+{
+	DELAY(5);
+	switch (bit_mode){
+	case SET_BIT:
+		*gpreg |= write_bit;
+		break;
+	case CLR_BIT:
+		*gpreg &= 0xfe;
+		break;
+	case SET_CLK:
+		*gpreg |= 0x02;
+		break;
+	case CLR_CLK:
+		*gpreg &= 0xfd;
+		break;
+
+	}
+	OUTB (nc_gpreg, *gpreg);
+	DELAY(5);
+}
+
+#undef SET_BIT 0
+#undef CLR_BIT 1
+#undef SET_CLK 2
+#undef CLR_CLK 3
+
+
+/* ---------------------------------------------------------------------
+**
+**	Try reading Tekram format nvram
+**
+** ---------------------------------------------------------------------
+**
+** GPOI0 - data in
+** GPIO1 - data out
+** GPIO2 - clock
+** GPIO4 - chip select
+**
+**	return 0 if NVRAM data OK, 1 if NVRAM data not OK
+** ---------------------------------------------------------------------
+*/
+
+static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg);
+static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg);
+static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg);
+static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg);
+static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg);
+static void Tnvram_Stop(ncr_slot *np, u_char *gpreg);
+static void Tnvram_Clk(ncr_slot *np, u_char *gpreg);
+
+__initfunc(
+static int ncr_get_Tekram_nvram (ncr_slot *np, Tekram_nvram *nvram)
+)
+{
+	u_char gpcntl, gpreg;
+	u_char old_gpcntl, old_gpreg;
+	u_short csum;
+
+	/* save current state of GPCNTL and GPREG */
+	old_gpreg	= INB (nc_gpreg);
+	old_gpcntl	= INB (nc_gpcntl);
+
+	/* set up GPREG & GPCNTL to set GPIO0/1/2/4 in to known state, 0 in,
+	   1/2/4 out */
+	gpreg = old_gpreg & 0xe9;
+	OUTB (nc_gpreg, gpreg);
+	gpcntl = (old_gpcntl & 0xe9) | 0x09;
+	OUTB (nc_gpcntl, gpcntl);
+
+	/* input all of NVRAM, 64 words */
+	csum = Tnvram_read_data(np, (u_short *) nvram,
+			sizeof(*nvram) / sizeof(short), &gpreg);
+	
+	/* return GPIO0/1/2/4 to original states after having accessed NVRAM */
+	OUTB (nc_gpcntl, old_gpcntl);
+	OUTB (nc_gpreg,  old_gpreg);
+
+	/* check data valid */
+	if (csum != 0x1234)
+		return 1;
+
+	return 0;
+}
+
+/*
+ * Read Tekram NvRAM data and compute checksum.
+ */
+__initfunc(
+static u_short Tnvram_read_data(ncr_slot *np, u_short *data, int len, u_char *gpreg)
+)
+{
+	u_char	read_bit;
+	u_short	csum;
+	int	x;
+
+	for (x = 0, csum = 0; x < len; x++)  {
+
+		/* output read command and address */
+		Tnvram_Send_Command(np, 0x180 | x, &read_bit, gpreg);
+		if (read_bit & 0x01)
+			return 0; /* Force bad checksum */
+
+		Tnvram_Read_Word(np, &data[x], gpreg);
+		csum += data[x];
+
+		Tnvram_Stop(np, gpreg);
+	}
+
+	return csum;
+}
+
+/*
+ * Send read command and address to NVRAM
+ */
+__initfunc(
+static void Tnvram_Send_Command(ncr_slot *np, u_short write_data, u_char *read_bit, u_char *gpreg)
+)
+{
+	int x;
+
+	/* send 9 bits, start bit (1), command (2), address (6)  */
+	for (x = 0; x < 9; x++)
+		Tnvram_Write_Bit(np, (u_char) (write_data >> (8 - x)), gpreg);
+
+	*read_bit = INB (nc_gpreg);
+}
+
+/*
+ * READ a byte from the NVRAM
+ */
+__initfunc(
+static void Tnvram_Read_Word(ncr_slot *np, u_short *nvram_data, u_char *gpreg)
+)
+{
+	int x;
+	u_char read_bit;
+
+	*nvram_data = 0;
+	for (x = 0; x < 16; x++) {
+		Tnvram_Read_Bit(np, &read_bit, gpreg);
+
+		if (read_bit & 0x01)
+			*nvram_data |=  (0x01 << (15 - x));
+		else
+			*nvram_data &= ~(0x01 << (15 - x));
+	}
+}
+
+/* 
+ * Read bit from NVRAM
+ */
+__initfunc(
+static void Tnvram_Read_Bit(ncr_slot *np, u_char *read_bit, u_char *gpreg)
+)
+{
+	DELAY(2);
+	Tnvram_Clk(np, gpreg);
+	*read_bit = INB (nc_gpreg);
+}
+
+/*
+ * Write bit to GPIO0
+ */
+__initfunc(
+static void Tnvram_Write_Bit(ncr_slot *np, u_char write_bit, u_char *gpreg)
+)
+{
+	if (write_bit & 0x01)
+		*gpreg |= 0x02;
+	else
+		*gpreg &= 0xfd;
+		
+	*gpreg |= 0x10;
+		
+	OUTB (nc_gpreg, *gpreg);
+	DELAY(2);
+
+	Tnvram_Clk(np, gpreg);
+}
+
+/*
+ * Send STOP condition to NVRAM - puts NVRAM to sleep... ZZZzzz!!
+ */
+__initfunc(
+static void Tnvram_Stop(ncr_slot *np, u_char *gpreg)
+)
+{
+	*gpreg &= 0xef;
+	OUTB (nc_gpreg, *gpreg);
+	DELAY(2);
+
+	Tnvram_Clk(np, gpreg);
+}
+
+/*
+ * Pulse clock bit in GPIO0
+ */
+__initfunc(
+static void Tnvram_Clk(ncr_slot *np, u_char *gpreg)
+)
+{
+	OUTB (nc_gpreg, *gpreg | 0x04);
+	DELAY(2);
+	OUTB (nc_gpreg, *gpreg);
+}
+
+#endif	/* SCSI_NCR_NVRAM_SUPPORT */
 
 /*
 **	Module stuff
diff -u --recursive --new-file v2.1.48/linux/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h
--- v2.1.48/linux/drivers/scsi/ncr53c8xx.h	Mon Jul  7 16:02:51 1997
+++ linux/drivers/scsi/ncr53c8xx.h	Mon Aug 11 14:18:49 1997
@@ -45,7 +45,7 @@
 /*
 **	Name and revision of the driver
 */
-#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 2.1b"
+#define SCSI_NCR_DRIVER_NAME		"ncr53c8xx - revision 2.4"
 
 /*
 **	Check supported Linux versions
@@ -95,6 +95,7 @@
 
 #define SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
 #define SCSI_NCR_DEBUG_INFO_SUPPORT
+#define SCSI_NCR_PCI_FIX_UP_SUPPORT
 #ifdef	SCSI_NCR_PROC_INFO_SUPPORT
 #	define	SCSI_NCR_PROFILE_SUPPORT
 #	define	SCSI_NCR_USER_COMMAND_SUPPORT
@@ -102,6 +103,18 @@
 /* #	define	SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT */
 #endif
 
+/*==========================================================
+**
+** nvram settings - #define SCSI_NCR_NVRAM_SUPPORT to enable
+**
+**==========================================================
+*/
+
+#ifdef CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT
+#define SCSI_NCR_NVRAM_SUPPORT
+/* #define SCSI_NCR_DEBUG_NVRAM */
+#endif
+
 /* ---------------------------------------------------------------------
 ** Take into account kernel configured parameters.
 ** Most of these options can be overridden at startup by a command line.
@@ -361,67 +374,67 @@
 	unsigned char	offset_max;
 	unsigned char	nr_divisor;
 	unsigned int	features;
-#define _F_LED0		(1<<0)
-#define _F_WIDE		(1<<1)
-#define _F_ULTRA	(1<<2)
-#define _F_ULTRA2	(1<<3)
-#define _F_DBLR		(1<<4)
-#define _F_QUAD		(1<<5)
-#define _F_ERL		(1<<6)
-#define _F_CLSE		(1<<7)
-#define _F_WRIE		(1<<8)
-#define _F_ERMP		(1<<9)
-#define _F_BOF		(1<<10)
-#define _F_DFS		(1<<11)
-#define _F_PFEN		(1<<12)
-#define _F_LDSTR	(1<<13)
-#define _F_RAM		(1<<14)
-#define _F_CLK80	(1<<15)
-#define _F_CACHE_SET	(_F_ERL|_F_CLSE|_F_WRIE|_F_ERMP)
-#define _F_SCSI_SET	(_F_WIDE|_F_ULTRA|_F_ULTRA2|_F_DBLR|_F_QUAD|F_CLK80)
-#define _F_SPECIAL_SET	(_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM)
+#define FE_LED0		(1<<0)
+#define FE_WIDE		(1<<1)
+#define FE_ULTRA	(1<<2)
+#define FE_ULTRA2	(1<<3)
+#define FE_DBLR		(1<<4)
+#define FE_QUAD		(1<<5)
+#define FE_ERL		(1<<6)
+#define FE_CLSE		(1<<7)
+#define FE_WRIE		(1<<8)
+#define FE_ERMP		(1<<9)
+#define FE_BOF		(1<<10)
+#define FE_DFS		(1<<11)
+#define FE_PFEN		(1<<12)
+#define FE_LDSTR	(1<<13)
+#define FE_RAM		(1<<14)
+#define FE_CLK80	(1<<15)
+#define FE_CACHE_SET	(FE_ERL|FE_CLSE|FE_WRIE|FE_ERMP)
+#define FE_SCSI_SET	(FE_WIDE|FE_ULTRA|FE_ULTRA2|FE_DBLR|FE_QUAD|F_CLK80)
+#define FE_SPECIAL_SET	(FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM)
 } ncr_chip;
 
 #define SCSI_NCR_CHIP_TABLE						\
 {									\
  {PCI_DEVICE_ID_NCR_53C810, 0x0f, "810",  4,  8, 4,			\
- _F_ERL}								\
+ FE_ERL}								\
  ,									\
  {PCI_DEVICE_ID_NCR_53C810, 0xff, "810a", 4,  8, 4,			\
- _F_CACHE_SET|_F_LDSTR|_F_PFEN|_F_BOF}					\
+ FE_CACHE_SET|FE_LDSTR|FE_PFEN|FE_BOF}					\
  ,									\
  {PCI_DEVICE_ID_NCR_53C815, 0xff, "815",  4,  8, 4,			\
- _F_ERL|_F_BOF}								\
+ FE_ERL|FE_BOF}								\
  ,									\
  {PCI_DEVICE_ID_NCR_53C820, 0xff, "820",  4,  8, 4,			\
- _F_WIDE|_F_ERL}							\
+ FE_WIDE|FE_ERL}							\
  ,									\
  {PCI_DEVICE_ID_NCR_53C825, 0x0f, "825",  4,  8, 4,			\
- _F_WIDE|_F_ERL|_F_BOF}							\
+ FE_WIDE|FE_ERL|FE_BOF}							\
  ,									\
  {PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 7,  8, 4,			\
- _F_WIDE|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM}		\
+ FE_WIDE|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}		\
  ,									\
  {PCI_DEVICE_ID_NCR_53C860, 0xff, "860",  4,  8, 5,			\
- _F_WIDE|_F_ULTRA|_F_CLK80|_F_CACHE_SET|_F_BOF|_F_LDSTR|_F_PFEN|_F_RAM}	\
+ FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN|FE_RAM}	\
  ,									\
  {PCI_DEVICE_ID_NCR_53C875, 0x01, "875",  7, 16, 5,			\
- _F_WIDE|_F_ULTRA|_F_CLK80|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM}	\
+ FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,									\
  {PCI_DEVICE_ID_NCR_53C875, 0xff, "875",  7, 16, 5,			\
- _F_WIDE|_F_ULTRA|_F_DBLR|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM}	\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,									\
  {PCI_DEVICE_ID_NCR_53C875J, 0xff, "875J",  7, 16, 5,			\
- _F_WIDE|_F_ULTRA|_F_DBLR|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM}	\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,									\
  {PCI_DEVICE_ID_NCR_53C885, 0xff, "885",  7, 16, 5,			\
- _F_WIDE|_F_ULTRA|_F_DBLR|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM}	\
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,									\
  {PCI_DEVICE_ID_NCR_53C895, 0xff, "895",  7, 31, 7,			\
- _F_WIDE|_F_ULTRA|_F_ULTRA2|_F_QUAD|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM}	\
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
  ,									\
  {PCI_DEVICE_ID_NCR_53C896, 0xff, "896",  7, 31, 7,			\
- _F_WIDE|_F_ULTRA|_F_ULTRA2|_F_QUAD|_F_CACHE_SET|_F_BOF|_F_DFS|_F_LDSTR|_F_PFEN|_F_RAM}	\
+ FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
 }
 
 /*
@@ -456,6 +469,7 @@
 	0,					\
 	0,					\
 	1,					\
+	1,					\
 	SCSI_NCR_SETUP_DEFAULT_TAGS,		\
 	SCSI_NCR_SETUP_DEFAULT_SYNC,		\
 	0x00,					\
@@ -482,6 +496,7 @@
 	0,					\
 	0,					\
 	0,					\
+	1,					\
 	2,					\
 	0,					\
 	255,					\
diff -u --recursive --new-file v2.1.48/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.48/linux/fs/buffer.c	Mon Aug  4 16:25:38 1997
+++ linux/fs/buffer.c	Wed Aug  6 16:14:07 1997
@@ -113,7 +113,7 @@
 int bdflush_min[N_PARAM] = {  0,  10,    5,   25,  0,   100,   100, 1, 1};
 int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 60000, 60000, 2047, 5};
 
-void wakeup_bdflush(int);
+static void wakeup_bdflush(int);
 
 /*
  * Rewrote the wait-routines to use the "new" wait-queue functionality,
@@ -540,9 +540,10 @@
 		next = tmp->b_next;
 		if (tmp->b_blocknr != block || tmp->b_size != size || tmp->b_dev != dev)
 			continue;
-		return tmp;
+		next = tmp;
+		break;
 	}
-	return NULL;
+	return next;
 }
 
 /*
@@ -559,7 +560,7 @@
 
 		bh=find_buffer(dev,block,size);
 		if (!bh)
-			return NULL;
+			return bh;
 		bh->b_count++;
 		bh->b_lru_time = jiffies;
 		wait_on_buffer(bh);
@@ -1658,11 +1659,11 @@
  * response to dirty buffers.  Once this process is activated, we write back
  * a limited number of buffers to the disks and then go back to sleep again.
  */
-struct wait_queue * bdflush_wait = NULL;
-struct wait_queue * bdflush_done = NULL;
+static struct wait_queue * bdflush_wait = NULL;
+static struct wait_queue * bdflush_done = NULL;
 struct task_struct *bdflush_tsk = 0;
 
-void wakeup_bdflush(int wait)
+static void wakeup_bdflush(int wait)
 {
 	if (current == bdflush_tsk)
 		return;
diff -u --recursive --new-file v2.1.48/linux/fs/dcache.c linux/fs/dcache.c
--- v2.1.48/linux/fs/dcache.c	Mon Aug  4 16:25:38 1997
+++ linux/fs/dcache.c	Mon Aug 11 14:11:44 1997
@@ -92,6 +92,25 @@
 }
 
 /*
+ * Try to invalidate the dentry if it turns out to be
+ * possible. If there are other users of the dentry we
+ * can't invalidate it.
+ *
+ * This is currently incorrect. We should try to see if
+ * we can invalidate any unused children - right now we
+ * refuse to invalidate way too much.
+ */
+int d_invalidate(struct dentry * dentry)
+{
+	/* We should do a partial shrink_dcache here */
+	if (dentry->d_count != 1)
+		return -EBUSY;
+
+	d_drop(dentry);
+	return 0;
+}
+
+/*
  * Shrink the dcache. This is done when we need
  * more memory, or simply when we need to unmount
  * something (at which point we need to unuse
@@ -270,18 +289,6 @@
 		(dentry->d_name.len == len);
 }
 
-static inline void d_insert_to_parent(struct dentry * entry, struct dentry * parent)
-{
-	list_add(&entry->d_hash, d_hash(dget(parent), entry->d_name.hash));
-}
-
-static inline void d_remove_from_parent(struct dentry * dentry, struct dentry * parent)
-{
-	list_del(&dentry->d_hash);
-	dput(parent);
-}
-
-
 /*
  * When a file is deleted, we have two options:
  * - turn this dentry into a negative dentry
@@ -317,42 +324,55 @@
 
 void d_add(struct dentry * entry, struct inode * inode)
 {
-	d_insert_to_parent(entry, entry->d_parent);
+	struct dentry * parent = dget(entry->d_parent);
+
+	list_add(&entry->d_hash, d_hash(parent, entry->d_name.hash));
 	d_instantiate(entry, inode);
 }
 
-static inline void alloc_new_name(struct dentry * entry, struct qstr *newname)
-{
-	int len = newname->len;
-	int hash = newname->hash;
-	char *name = (char *) entry->d_name.name;
-
-	if (NAME_ALLOC_LEN(len) != NAME_ALLOC_LEN(entry->d_name.len)) {
-		name = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL);
-		if (!name)
-			printk("out of memory for dcache\n");
-		kfree(entry->d_name.name);
-		entry->d_name.name = name;
-	}
-	memcpy(name, newname->name, len);
-	name[len] = 0;
-	entry->d_name.len = len;
-	entry->d_name.hash = hash;
-}
+#define switch(x,y) do { \
+	__typeof__ (x) __tmp = x; \
+	x = y; y = __tmp; } while (0)
 
-void d_move(struct dentry * dentry, struct dentry * newdir, struct qstr * newname)
+/*
+ * We cannibalize "newdentry" when moving dentry on top of it,
+ * because it's going to be thrown away anyway. We could be more
+ * polite about it, though.
+ *
+ * This forceful removal will result in ugly /proc output if
+ * somebody holds a file open that got deleted due to a rename.
+ * We could be nicer about the deleted file, and let it show
+ * up under the name it got deleted rather than the name that
+ * deleted it.
+ *
+ * Careful with the hash switch. The hash switch depends on
+ * the fact that any list-entry can be a head of the list.
+ * Think about it.
+ */
+void d_move(struct dentry * dentry, struct dentry * target)
 {
-	if (!dentry)
-		return;
+	struct list_head * oldhead;
 
 	if (!dentry->d_inode)
 		printk("VFS: moving negative dcache entry\n");
 
-	d_remove_from_parent(dentry, dentry->d_parent);
-	alloc_new_name(dentry, newname);
-	dentry->d_parent = newdir;
-	d_insert_to_parent(dentry, newdir);
+	/* Switch the hashes.. */
+	oldhead = dentry->d_hash.prev;
+	list_del(&dentry->d_hash);
+	list_add(&dentry->d_hash, &target->d_hash);
+	list_del(&target->d_hash);
+	list_add(&target->d_hash, oldhead);
+
+	/* Switch the parents and the names.. */
+	switch(dentry->d_parent, target->d_parent);
+	switch(dentry->d_name.name, target->d_name.name);
+	switch(dentry->d_name.len, target->d_name.len);
+	switch(dentry->d_name.hash, target->d_name.hash);
+
+	/* Mark the (now overwritten) target deleted. */
+	d_delete(target);
 }
+
 /*
  * "buflen" should be PAGE_SIZE or more.
  */
@@ -364,6 +384,11 @@
 
 	*--end = '\0';
 	buflen--;
+	if (dentry->d_parent != dentry && list_empty(&dentry->d_hash)) {
+		buflen -= 10;
+		end -= 10;
+		memcpy(end, " (deleted)", 10);
+	}
 
 	/* Get '/' right */
 	retval = end-1;
diff -u --recursive --new-file v2.1.48/linux/fs/ext2/namei.c linux/fs/ext2/namei.c
--- v2.1.48/linux/fs/ext2/namei.c	Mon Aug  4 16:25:39 1997
+++ linux/fs/ext2/namei.c	Sat Aug  9 21:49:35 1997
@@ -201,7 +201,7 @@
 
 	*err = -EINVAL;
 	*res_dir = NULL;
-	if (!dir)
+	if (!dir || !dir->i_nlink)
 		return NULL;
 	sb = dir->i_sb;
 
@@ -351,10 +351,13 @@
 	struct inode * inode;
 	struct buffer_head * bh;
 	struct ext2_dir_entry * de;
-	int err;
+	int err = -EIO;
 
 	if (!dir)
 		return -ENOENT;
+	/*
+	 * N.B. Several error exits in ext2_new_inode don't set err.
+	 */
 	inode = ext2_new_inode (dir, mode, &err);
 	if (!inode)
 		return err;
@@ -386,7 +389,7 @@
 	struct inode * inode;
 	struct buffer_head * bh;
 	struct ext2_dir_entry * de;
-	int err;
+	int err = -EIO;
 
 	if (!dir)
 		return -ENOENT;
@@ -443,7 +446,7 @@
 	struct inode * inode;
 	struct buffer_head * bh, * dir_block;
 	struct ext2_dir_entry * de;
-	int err;
+	int err = -EIO;
 
 	if (dentry->d_name.len > EXT2_NAME_LEN)
 		return -ENAMETOOLONG;
@@ -714,8 +717,7 @@
 	struct inode * inode = NULL;
 	struct buffer_head * bh = NULL, * name_block = NULL;
 	char * link;
-	int i, err;
-	int l;
+	int i, l, err = -EIO;
 	char c;
 
 	if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) {
@@ -975,8 +977,7 @@
 	}
 
 	/* Update the dcache */
-	d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
-	d_delete(new_dentry);
+	d_move(old_dentry, new_dentry);
 	retval = 0;
 end_rename:
 	brelse (dir_bh);
diff -u --recursive --new-file v2.1.48/linux/fs/inode.c linux/fs/inode.c
--- v2.1.48/linux/fs/inode.c	Mon Aug  4 16:25:39 1997
+++ linux/fs/inode.c	Fri Aug  8 19:07:13 1997
@@ -30,16 +30,15 @@
  * Each inode can be on two separate lists. One is
  * the hash list of the inode, used for lookups. The
  * other linked list is the "type" list:
- *  "in_use" - valid inode, hashed
- *  "dirty" - valid inode, hashed, dirty.
+ *  "in_use" - valid inode, hashed if i_nlink > 0
+ *  "dirty"  - valid inode, hashed if i_nlink > 0, dirty.
  *  "unused" - ready to be re-used. Not hashed.
  *
- * The two first versions also have a dirty list, allowing
- * for low-overhead inode sync() operations.
+ * A "dirty" list is maintained for each super block,
+ * allowing for low-overhead inode sync() operations.
  */
 
 static LIST_HEAD(inode_in_use);
-static LIST_HEAD(inode_dirty);
 static LIST_HEAD(inode_unused);
 static struct list_head inode_hashtable[HASH_SIZE];
 
@@ -59,12 +58,19 @@
 
 int max_inodes = NR_INODE;
 
+/*
+ * Put the inode on the super block's dirty list
+ */
 void __mark_inode_dirty(struct inode *inode)
 {
-	spin_lock(&inode_lock);
-	list_del(&inode->i_list);
-	list_add(&inode->i_list, &inode_dirty);
-	spin_unlock(&inode_lock);
+	struct super_block * sb = inode->i_sb;
+
+	if (sb) {
+		spin_lock(&inode_lock);
+		list_del(&inode->i_list);
+		list_add(&inode->i_list, &sb->s_dirty);
+		spin_unlock(&inode_lock);
+	}
 }
 
 static inline void unlock_inode(struct inode *inode)
@@ -169,14 +175,28 @@
 }
 
 /*
- * "sync_inodes()" goes through the dirty list 
- * and writes them out and puts them back on
- * the normal list.
+ * "sync_inodes()" goes through the super block's dirty list, 
+ * writes them out, and puts them back on the normal list.
  */
 void sync_inodes(kdev_t dev)
 {
+	struct super_block * sb = super_blocks + 0;
+	int i;
+
+	/*
+	 * Search the super_blocks array for the device(s) to sync.
+	 */
 	spin_lock(&inode_lock);
-	sync_list(&inode_dirty, &inode_in_use);
+	for (i = NR_SUPER ; i-- ; sb++) {
+		if (!sb->s_dev)
+			continue;
+		if (dev && sb->s_dev != dev)
+			continue;
+
+		sync_list(&sb->s_dirty, &inode_in_use);
+		if (dev)
+			break;
+	}
 	spin_unlock(&inode_lock);
 }
 
@@ -185,10 +205,17 @@
  */
 void write_inode_now(struct inode *inode)
 {
-	spin_lock(&inode_lock);
-	if (test_bit(I_DIRTY, &inode->i_state))
-		sync_one(&inode_dirty, &inode_in_use, &inode->i_list, inode);
-	spin_unlock(&inode_lock);
+	struct super_block * sb = inode->i_sb;
+
+	if (sb) {
+		spin_lock(&inode_lock);
+		if (test_bit(I_DIRTY, &inode->i_state))
+			sync_one(&sb->s_dirty, &inode_in_use, &inode->i_list,
+				 inode);
+		spin_unlock(&inode_lock);
+	}
+	else
+		printk("write_inode_now: no super block\n");
 }
 
 /*
@@ -232,7 +259,11 @@
 	spin_unlock(&inode_lock);
 }
 
-static int invalidate_list(struct list_head *head, kdev_t dev, struct list_head * dispose)
+/*
+ * Invalidate all inodes for a device, except for the root inode. 
+ */
+static int invalidate_list(struct list_head *head, kdev_t dev, 
+			struct inode * root, struct list_head * dispose)
 {
 	struct list_head *next;
 	int busy = 0;
@@ -248,6 +279,8 @@
 		inode = list_entry(tmp, struct inode, i_list);
 		if (inode->i_dev != dev)
 			continue;
+		if (inode == root)
+			continue;
 		if (!inode->i_count && !inode->i_state) {
 			list_del(&inode->i_hash);
 			INIT_LIST_HEAD(&inode->i_hash);
@@ -267,14 +300,16 @@
  * is because we don't want to sleep while messing
  * with the global lists..
  */
-int invalidate_inodes(kdev_t dev)
+static int invalidate_inodes_except(kdev_t dev, struct inode * root)
 {
+	struct super_block * sb = get_super(dev);
 	int busy;
 	LIST_HEAD(throw_away);
 
 	spin_lock(&inode_lock);
-	busy = invalidate_list(&inode_in_use, dev, &throw_away);
-	busy |= invalidate_list(&inode_dirty, dev, &throw_away);
+	busy = invalidate_list(&inode_in_use, dev, root, &throw_away);
+	if (sb)
+		busy |= invalidate_list(&sb->s_dirty, dev, root, &throw_away);
 	spin_unlock(&inode_lock);
 
 	dispose_list(&throw_away);
@@ -282,6 +317,11 @@
 	return busy;
 }
 
+int invalidate_inodes(kdev_t dev)
+{
+	return invalidate_inodes_except(dev, NULL);
+}
+
 /*
  * This is called with the inode lock held. It just looks at the last
  * inode on the in-use list, and if the inode is trivially freeable
@@ -534,18 +574,34 @@
 }
 
 /*
- * FIXME! These need to go through the in-use inodes to
- * check whether we can mount/umount/remount.
+ * Check whether we can mount.
  */
 int fs_may_mount(kdev_t dev)
 {
-	return 1;
+	return !invalidate_inodes(dev);
 }
 
+/*
+ * Check whether we can unmount.
+ */
 int fs_may_umount(struct super_block *sb, struct dentry * root)
 {
+	int busy;
+
 	shrink_dcache();
-	return root->d_count == 1;
+
+	if (!root->d_inode || root->d_inode->i_dev != sb->s_dev) {
+		printk("fs_may_umount: root inode not on device??\n");
+		return 0;
+	}
+
+	/*
+	 * Invalidate the inodes for this device. Device has been synced
+	 * prior to call, so there should be no dirty inodes.
+	 */
+	busy = invalidate_inodes_except(sb->s_dev, root->d_inode);
+
+	return (root->d_count == 1) && !busy;
 }
 
 /* This belongs in file_table.c, not here... */
diff -u --recursive --new-file v2.1.48/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.1.48/linux/fs/isofs/inode.c	Thu Jul 17 10:06:07 1997
+++ linux/fs/isofs/inode.c	Sat Aug  9 13:16:10 1997
@@ -613,6 +613,7 @@
 	inode->i_uid = inode->i_sb->u.isofs_sb.s_uid;
 	inode->i_gid = inode->i_sb->u.isofs_sb.s_gid;
 	inode->i_size = isonum_733 (raw_inode->size);
+	inode->i_blocks = inode->i_blksize = 0;
 
 	/* There are defective discs out there - we do this to protect
 	   ourselves.  A cdrom will never contain more than 800Mb */
@@ -736,6 +737,7 @@
 	inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
 	inode->u.isofs_i.i_first_extent = 0;
 	inode->i_size = 0;
+	inode->i_blocks = inode->i_blksize = 0;
 	inode->i_nlink = 1;
 	inode->i_uid = inode->i_gid = 0;
 	inode->i_mode = S_IFREG;  /*Regular file, no one gets to read*/
diff -u --recursive --new-file v2.1.48/linux/fs/minix/namei.c linux/fs/minix/namei.c
--- v2.1.48/linux/fs/minix/namei.c	Sun Jul 27 12:11:01 1997
+++ linux/fs/minix/namei.c	Sat Aug  9 21:49:47 1997
@@ -754,8 +754,7 @@
 		}
 	}
 	/* Update the dcache */
-	d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
-	d_delete(new_dentry);
+	d_move(old_dentry, new_dentry);
 	retval = 0;
 end_rename:
 	brelse(dir_bh);
diff -u --recursive --new-file v2.1.48/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
--- v2.1.48/linux/fs/msdos/namei.c	Sun Jul 27 12:11:01 1997
+++ linux/fs/msdos/namei.c	Sat Aug  9 21:50:28 1997
@@ -555,8 +555,7 @@
 	}
 	memcpy(old_de->name,new_name,MSDOS_NAME);
 	/* Update the dcache */
-	d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
-	d_delete(new_dentry);
+	d_move(old_dentry, new_dentry);
 set_hid:
 	old_de->attr = is_hid
 		? (old_de->attr | ATTR_HIDDEN)
@@ -583,7 +582,7 @@
 	struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode;
 	struct dentry *walk;
 	int new_ino,free_ino,dotdot_ino;
-	int error,exists,ino;
+	int error,exists;
 
 	if (old_dir->i_dev != new_dir->i_dev) return -EINVAL;
 	if (old_ino == new_dir->i_ino) return -EINVAL;
@@ -684,8 +683,7 @@
 		fat_brelse(sb, dotdot_bh);
 	}
 	/* Update the dcache */
-	d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
-	d_delete(new_dentry);
+	d_move(old_dentry, new_dentry);
 	error = 0;
 rename_done:
 	fat_brelse(sb, free_bh);
diff -u --recursive --new-file v2.1.48/linux/fs/namei.c linux/fs/namei.c
--- v2.1.48/linux/fs/namei.c	Mon Aug  4 16:25:39 1997
+++ linux/fs/namei.c	Mon Aug 11 11:41:23 1997
@@ -245,7 +245,9 @@
 	if (!result) {
 		int error;
 		result = d_alloc(parent, name);
+		result->d_count++;
 		error = dir->i_op->lookup(dir, result);
+		result->d_count--;
 		if (error) {
 			d_free(result);
 			result = ERR_PTR(error);
@@ -268,16 +270,12 @@
 
 	if (dentry && dentry->d_revalidate) {
 		int validated, (*revalidate)(struct dentry *) = dentry->d_revalidate;
-		struct dentry * save;
 
 		dentry->d_count++;
-		validated = revalidate(dentry);
-		save = dentry;
-		if (!validated) {
-			d_drop(dentry);
+		validated = revalidate(dentry) || d_invalidate(dentry);
+		dput(dentry);
+		if (!validated)
 			dentry = NULL;
-		}
-		dput(save);
 	}
 	return dentry;
 }
@@ -1057,21 +1055,6 @@
 		up(s2);
 }
 
-static inline int is_reserved(struct dentry *dentry)
-{
-	if (dentry->d_name.name[0] == '.') {
-		switch (dentry->d_name.len) {
-		case 2:
-			if (dentry->d_name.name[1] != '.')
-				break;
-			/* fallthrough */
-		case 1:
-			return 1;
-		}
-	}
-	return 0;
-}
-
 static inline int do_rename(const char * oldname, const char * newname)
 {
 	int error;
@@ -1106,11 +1089,10 @@
 	if (error)
 		goto exit_lock;
 
-	error = -EPERM;
-	if (is_reserved(new_dentry) || is_reserved(old_dentry))
-		goto exit_lock;
-
-	/* Disallow moves of mountpoints. */
+	/*
+	 * Disallow moves of mountpoints.  There is no technical 
+	 * reason for this, but user level stuff gets too confused.
+	 */
 	error = -EBUSY;
 	if (old_dentry->d_covers != old_dentry)
 		goto exit_lock;
diff -u --recursive --new-file v2.1.48/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.1.48/linux/fs/nfs/dir.c	Mon Aug  4 16:25:39 1997
+++ linux/fs/nfs/dir.c	Sat Aug  9 21:50:42 1997
@@ -663,8 +663,7 @@
 	nfs_invalidate_dircache(new_dir);
 
 	/* Update the dcache */
-	d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
-	d_delete(new_dentry);
+	d_move(old_dentry, new_dentry);
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.48/linux/fs/nfsd/export.c linux/fs/nfsd/export.c
--- v2.1.48/linux/fs/nfsd/export.c	Sun Jul 27 12:11:01 1997
+++ linux/fs/nfsd/export.c	Wed Aug  6 13:03:00 1997
@@ -145,7 +145,7 @@
 	    !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
 		return -EINVAL;
 
-	dprintk("exp_export called for %s:%s (%x/%d fl %x).\n",
+	dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n",
 			nxp->ex_client, nxp->ex_path,
 			nxp->ex_dev, nxp->ex_ino, nxp->ex_flags);
 	dev = nxp->ex_dev;
@@ -360,7 +360,7 @@
 	struct dentry		*dentry;
 	struct inode		*inode;
 
-	dprintk("nfsd: exp_rootfh(%s:%x/%d)\n", clp->cl_ident, dev, ino);
+	dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino);
 
 	if (!(exp = exp_get(clp, dev, ino)))
 		return -EPERM;
@@ -373,7 +373,7 @@
 	}
 	if(inode->i_dev != dev || inode->i_ino != ino) {
 		printk("exp_rootfh: Aieee, ino/dev mismatch\n");
-		printk("exp_rootfh: arg[dev(%x):ino(%d)] inode[dev(%x):ino(%ld)]\n",
+		printk("exp_rootfh: arg[dev(%x):ino(%ld)] inode[dev(%x):ino(%ld)]\n",
 		       dev, ino, inode->i_dev, inode->i_ino);
 	}
 
diff -u --recursive --new-file v2.1.48/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- v2.1.48/linux/fs/nfsd/nfsfh.c	Sun Jul 27 12:11:01 1997
+++ linux/fs/nfsd/nfsfh.c	Wed Aug  6 13:03:00 1997
@@ -31,7 +31,7 @@
 	if(fhp->fh_dverified)
 		return 0;
 
-	dprintk("nfsd: fh_lookup(exp %x/%d fh %p)\n",
+	dprintk("nfsd: fh_lookup(exp %x/%ld fh %p)\n",
 			fh->fh_xdev, fh->fh_xino, fh->fh_dentry);
 
 	/* Look up the export entry. */
@@ -93,7 +93,7 @@
 void
 fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry)
 {
-	dprintk("nfsd: fh_compose(exp %x/%d dentry %p)\n",
+	dprintk("nfsd: fh_compose(exp %x/%ld dentry %p)\n",
 			exp->ex_dev, exp->ex_ino, dentry);
 
 	fh_init(fhp);			/* initialize empty fh */
diff -u --recursive --new-file v2.1.48/linux/fs/super.c linux/fs/super.c
--- v2.1.48/linux/fs/super.c	Mon Aug  4 16:25:39 1997
+++ linux/fs/super.c	Mon Aug 11 10:02:56 1997
@@ -1067,7 +1067,16 @@
 
 __initfunc(void mount_root(void))
 {
+	struct super_block * sb = super_blocks;
+	int i;
+
 	memset(super_blocks, 0, sizeof(super_blocks));
+	/*
+	 * Initialize the dirty inode list headers for the super blocks
+	 */
+	for (i = NR_SUPER ; i-- ; sb++)
+		INIT_LIST_HEAD(&sb->s_dirty);
+
 	do_mount_root();
 }
 
@@ -1109,7 +1118,6 @@
 		dput(dir_d);
 		error = -ENOTDIR;
 	}
-	dput(old_root);
 	dput(old_pwd);
 	if (error) {
 		int umount_error;
diff -u --recursive --new-file v2.1.48/linux/include/linux/dcache.h linux/include/linux/dcache.h
--- v2.1.48/linux/include/linux/dcache.h	Mon Aug  4 16:25:40 1997
+++ linux/include/linux/dcache.h	Mon Aug 11 14:12:24 1997
@@ -81,11 +81,11 @@
 extern void d_instantiate(struct dentry *, struct inode *);
 extern void d_delete(struct dentry *);
 
-
 /* allocate/de-allocate */
 extern void d_free(struct dentry *);
 extern struct dentry * d_alloc(struct dentry * parent, const struct qstr *name);
 extern void shrink_dcache(void);
+extern int d_invalidate(struct dentry *);
 
 /* only used at mount-time */
 extern struct dentry * d_alloc_root(struct inode * root_inode, struct dentry * old_root);
@@ -97,7 +97,7 @@
 extern void d_add(struct dentry * entry, struct inode * inode);
 
 /* used for rename() and baskets */
-extern void d_move(struct dentry * entry, struct dentry * newparent, struct qstr * newname);
+extern void d_move(struct dentry * entry, struct dentry * newdentry);
 
 /* appendix may either be NULL or be used for transname suffixes */
 extern struct dentry * d_lookup(struct dentry * dir, struct qstr * name);
diff -u --recursive --new-file v2.1.48/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.1.48/linux/include/linux/fs.h	Mon Aug  4 16:25:40 1997
+++ linux/include/linux/fs.h	Mon Aug 11 14:17:20 1997
@@ -501,6 +501,7 @@
 	struct inode		*s_ibasket;
 	short int		s_ibasket_count;
 	short int		s_ibasket_max;
+	struct list_head	s_dirty;	/* dirty inodes */
 
 	union {
 		struct minix_sb_info	minix_sb;
diff -u --recursive --new-file v2.1.48/linux/include/linux/hdlcdrv.h linux/include/linux/hdlcdrv.h
--- v2.1.48/linux/include/linux/hdlcdrv.h	Thu Mar 27 14:40:10 1997
+++ linux/include/linux/hdlcdrv.h	Tue Aug  5 09:49:51 1997
@@ -117,7 +117,7 @@
 
 #define HDLCDRV_MAGIC      0x5ac6e778
 #define HDLCDRV_IFNAMELEN    6
-#define HDLCDRV_HDLCBUFFER  16 /* should be a power of 2 for speed reasons */
+#define HDLCDRV_HDLCBUFFER  32 /* should be a power of 2 for speed reasons */
 #define HDLCDRV_BITBUFFER  256 /* should be a power of 2 for speed reasons */
 #undef HDLCDRV_LOOPBACK  /* define for HDLC debugging purposes */
 #define HDLCDRV_DEBUG
diff -u --recursive --new-file v2.1.48/linux/include/linux/hfmodem.h linux/include/linux/hfmodem.h
--- v2.1.48/linux/include/linux/hfmodem.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/hfmodem.h	Tue Aug  5 09:48:55 1997
@@ -0,0 +1,254 @@
+/*****************************************************************************/
+
+/*
+ *      hfmodem.h  --  Linux soundcard HF FSK driver.
+ *
+ *      Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *        Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *  This is the Linux realtime sound output driver
+ */
+
+/*****************************************************************************/
+
+#ifndef _HFMODEM_H
+#define _HFMODEM_H
+/* --------------------------------------------------------------------- */
+
+#include <linux/version.h>
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#if LINUX_VERSION_CODE >= 0x20100
+#include <linux/poll.h>
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#define HFMODEM_MINOR         145
+
+#define HFMODEM_SRATE        8000
+#define HFMODEM_MAXBITS      4800   /* required for GTOR 300 baud mode */
+#define HFMODEM_MINBAUD        40
+#define HFMODEM_MAXBAUD       400
+#define HFMODEM_MAXCORRLEN   ((HFMODEM_SRATE+HFMODEM_MINBAUD-1)/HFMODEM_MINBAUD)
+
+/* --------------------------------------------------------------------- */
+
+typedef unsigned long hfmodem_time_t;
+typedef int hfmodem_soft_t;
+typedef unsigned long hfmodem_id_t;
+
+/* --------------------------------------------------------------------- */
+
+struct hfmodem_ioctl_fsk_tx_request {
+	hfmodem_time_t tstart;
+	hfmodem_time_t tinc;
+	int inv;
+	hfmodem_id_t id;
+	unsigned int nbits;
+	unsigned char *data;
+	unsigned int freq[2];
+};
+
+struct hfmodem_ioctl_fsk_rx_request {
+	hfmodem_time_t tstart;
+	hfmodem_time_t tinc;
+	unsigned int baud;
+	hfmodem_id_t id;
+	unsigned int nbits;
+	hfmodem_soft_t *data;
+	unsigned int freq[2];
+};
+
+struct hfmodem_ioctl_mixer_params {
+	int src;
+	int igain;
+	int ogain;
+};
+
+struct hfmodem_ioctl_sample_params {
+	__s16 *data;
+	int len;
+};
+
+#define HFMODEM_IOCTL_FSKTXREQUEST    _IOW('H', 0, struct hfmodem_ioctl_fsk_tx_request)
+#define HFMODEM_IOCTL_FSKRXREQUEST    _IOW('H', 1, struct hfmodem_ioctl_fsk_rx_request)
+#define HFMODEM_IOCTL_CLEARRQ         _IO('H',  3)
+#define HFMODEM_IOCTL_GETCURTIME      _IOR('H', 4, hfmodem_time_t)
+#define HFMODEM_IOCTL_WAITRQ          _IOR('H', 5, hfmodem_id_t)
+#define HFMODEM_IOCTL_MIXERPARAMS     _IOW('H', 6, struct hfmodem_ioctl_mixer_params)
+#define HFMODEM_IOCTL_SAMPLESTART     _IOW('H', 7, struct hfmodem_ioctl_sample_params)
+#define HFMODEM_IOCTL_SAMPLEFINISHED  _IO('H',  8)
+
+/* --------------------------------------------------------------------- */
+#ifdef __KERNEL__
+
+
+#define DMA_MODE_AUTOINIT      0x10
+
+#define NR_DEVICE 1
+
+#define HFMODEM_FRAGSAMPLES (HFMODEM_SRATE/100)
+#define HFMODEM_FRAGSIZE    (HFMODEM_FRAGSAMPLES*2)
+#define HFMODEM_NUMFRAGS    8
+#define HFMODEM_EXCESSFRAGS 3
+
+#define HFMODEM_NUMRXSLOTS 20
+#define HFMODEM_NUMTXSLOTS 4
+
+#define HFMODEM_CORRELATOR_CACHE 8
+
+enum slot_st { ss_unused = 0, ss_ready, ss_oper, ss_retired };
+typedef int hfmodem_conv_t;
+
+struct hfmodem_state {
+	const struct hfmodem_scops *scops;
+
+	/* io params */
+	struct {
+		unsigned int base_addr;
+		unsigned int dma;
+		unsigned int irq;
+	} io;
+
+	struct {
+		unsigned int seriobase;
+		unsigned int pariobase;
+		unsigned int midiiobase;
+		unsigned int flags;
+	} ptt_out;
+
+	struct {
+		__s16 *buf;
+		unsigned int lastfrag;
+		unsigned int fragptr;
+		unsigned int last_dmaptr;
+		int ptt_frames;
+	} dma;
+
+	struct {
+		unsigned int last_tvusec;
+		unsigned long long time_cnt;
+		hfmodem_time_t lasttime;
+#ifdef __i386__
+		unsigned int starttime_lo, starttime_hi;
+#endif /* __i386__ */
+	} clk;
+
+	int active;
+	struct wait_queue *wait;
+
+	struct {
+		__s16 *kbuf;
+		__s16 *ubuf;
+		__s16 *kptr;
+		unsigned int size;
+		int rem;
+	} sbuf;
+
+	struct {
+		hfmodem_time_t last_time; 
+		unsigned int tx_phase;
+		
+		struct hfmodem_l1_rxslot {
+			enum slot_st state;
+			hfmodem_time_t tstart, tinc;
+			hfmodem_soft_t *data;
+			hfmodem_soft_t *userdata;
+			unsigned int nbits;
+			unsigned int cntbits;
+			hfmodem_id_t id;
+			unsigned int corrlen;
+			hfmodem_conv_t scale;
+			unsigned int corr_cache;
+		} rxslots[HFMODEM_NUMRXSLOTS];
+		
+		struct hfmodem_l1_txslot {
+			enum slot_st state;
+			hfmodem_time_t tstart, tinc;
+			unsigned char *data;
+			unsigned int nbits;
+			unsigned int cntbits;
+			hfmodem_id_t id;
+			unsigned char inv;
+			unsigned int phinc;
+			unsigned int phase_incs[2];
+		} txslots[HFMODEM_NUMTXSLOTS];
+	} l1;
+};
+
+struct hfmodem_correlator_cache {
+	int refcnt;
+	int lru;
+	unsigned short phase_incs[2];
+	hfmodem_conv_t correlator[2][2][HFMODEM_MAXCORRLEN];
+};
+
+struct hfmodem_scops {
+	unsigned int extent;
+
+	void (*init)(struct hfmodem_state *dev);
+	void (*prepare_input)(struct hfmodem_state *dev);
+	void (*trigger_input)(struct hfmodem_state *dev);
+	void (*prepare_output)(struct hfmodem_state *dev);
+	void (*trigger_output)(struct hfmodem_state *dev);
+	void (*stop)(struct hfmodem_state *dev);
+	unsigned int (*intack)(struct hfmodem_state *dev);
+	void (*mixer)(struct hfmodem_state *dev, int src, int igain, int ogain);
+};
+
+/* --------------------------------------------------------------------- */
+
+extern int hfmodem_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+#if LINUX_VERSION_CODE >= 0x20100
+extern unsigned int hfmodem_poll(struct file *file, poll_table *wait);
+#else
+extern int hfmodem_select(struct inode *inode, struct file *file, int sel_type, select_table *wait);
+#endif
+
+extern void hfmodem_clear_rq(struct hfmodem_state *dev);
+extern void hfmodem_input_samples(struct hfmodem_state *dev, hfmodem_time_t tstart, 
+				hfmodem_time_t tinc, __s16 *samples);
+extern int hfmodem_output_samples(struct hfmodem_state *dev, hfmodem_time_t tstart, 
+				hfmodem_time_t tinc, __s16 *samples);
+extern long hfmodem_next_tx_event(struct hfmodem_state *dev, hfmodem_time_t curr);
+extern void hfmodem_finish_pending_rx_requests(struct hfmodem_state *dev);
+extern void hfmodem_wakeup(struct hfmodem_state *dev);
+
+
+extern int hfmodem_sbcprobe(struct hfmodem_state *dev);
+extern int hfmodem_wssprobe(struct hfmodem_state *dev);
+
+extern void hfmodem_refclock_probe(void);
+extern void hfmodem_refclock_init(struct hfmodem_state *dev);
+extern hfmodem_time_t hfmodem_refclock_current(struct hfmodem_state *dev, hfmodem_time_t expected, int exp_valid);
+
+/* --------------------------------------------------------------------- */
+
+extern const char hfmodem_drvname[];
+extern const char hfmodem_drvinfo[];
+
+extern struct hfmodem_state hfmodem_state[NR_DEVICE];
+extern struct hfmodem_correlator_cache hfmodem_correlator_cache[HFMODEM_CORRELATOR_CACHE];
+
+/* --------------------------------------------------------------------- */
+#endif /* __KERNEL__ */
+/* --------------------------------------------------------------------- */
+#endif /* _HFMODEM_H */
diff -u --recursive --new-file v2.1.48/linux/include/linux/nfsd/syscall.h linux/include/linux/nfsd/syscall.h
--- v2.1.48/linux/include/linux/nfsd/syscall.h	Sun Jul 27 12:11:01 1997
+++ linux/include/linux/nfsd/syscall.h	Wed Aug  6 13:03:00 1997
@@ -112,8 +112,7 @@
  * Kernel syscall implementation.
  */
 #if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
-extern asmlinkage int	sys_nfsservctl(int, struct nfsctl_arg *,
-					    union nfsctl_res *);
+extern asmlinkage int	sys_nfsservctl(int, void *, void *);
 #else
 #define sys_nfsservctl		sys_ni_syscall
 #endif
diff -u --recursive --new-file v2.1.48/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.1.48/linux/include/net/tcp.h	Mon Jul  7 16:04:03 1997
+++ linux/include/net/tcp.h	Mon Aug 11 14:20:57 1997
@@ -566,7 +566,7 @@
 	default:
 		if (oldstate==TCP_ESTABLISHED)
 			tcp_statistics.TcpCurrEstab--;
-		if (state == TCP_TIME_WAIT)
+		if (state == TCP_TIME_WAIT || state == TCP_CLOSE)
 			sk->prot->rehash(sk);
 	}
 }
